2. Eukleideen algoritmi

2. Eukleideen algoritmi
■ 2.1 Suurimman yhteisen tekijän tehokas laskutapa
Tässä luvussa tarkastellaan annettujen lukujen suurimman yhteisen tekijän etsimistä tehokkaalla tavalla. Erinomaisen käyttökelpoinen menetelmä on Eukleideen algoritmi, joka on ikivanha, mutta ei vähääkään vanhanaikainen
laskutapa. Nykyisen tietotekniikan aikakautena sen merkitys sovelluksissa on vain entisestään kasvanut.
Olkoot a ja b positiivisia kokonaislukuja ja a ≥ b. Kirjoitetaan luku a jakoalgoritmin mukaiseen muotoon
a = q b + r, 0 ≤ r < b, (ks. Lause 1.1). Jokainen lukujen a ja b yhteinen tekijä, sanokaamme t, on luvun b ja
jakojäännöksen r = a - q b yhteinen tekijä – ja myös päinvastoin. Todellakin, jos a = t a ' ja b = t b ', missä
t, a ', b ' ∈ ℤ+ , niin
(2.1)
r = a - q b = (t a ') – q (t b ') = t (a' – q b '),
ts. t on luvun r tekijä – ja oletuksen mukaan myös luvun b tekijä.
Toisaalta, jos b = t b ' ja r = t r', missä t, b ', r' ∈ ℤ+ , niin
(2.2)
a = q b + r = q (t b ') + (t r') = t (q b ' + r'),
ts. t on luvun a tekijä – ja oletuksen mukaan myös luvun b tekijä.
Merkitään d = syt(a, b). Edellä nähdyn nojalla d | b ja d | r, joten d | syt(b, r). Vastaavasti, kuten kohdassa
(2.2) nähtiin, syt(b, r) | syt(a, b), joten
d = syt(a, b) = syt(b, r), kun a = q b + r, 0 ≤ r < b.
Jos r = 0 (ja siis a = q b), saadaan tulos syt(a, b) = b. Olkoon r ≠ 0. Jatketaan nyt samalla tavalla lukujen b ja
r kanssa. Kirjoitetaan siis a = q1 b + r1 , b = q2 r1 + r2 , missä 0 ≤ r2 < r1 . Vastaavasti kuten edellä nähdään, että
syt(a, b) = syt(b, r1 ) = syt(r1 , r2 ). Jatketaan menettelyä kunnes toinen argumentti rn-1 on ensimmäisen argumentin
rn-2 tekijä (näin käy lopulta, koska r1 > r2 > r3 > ⋯ > rn-2 > rn-1 > rn = 0). Luku rn-1 on täsmälleen etsitty
lukujen a ja b suurin yhteinen tekijä, ts. rn-1 = syt(a, b). Tämä algoritmi on nimeltään Eukleideen algoritmi
(Euclid's Algorithm) ja se on erittäin nopea tapa laskea syt(a, b). Huomattavaa on, että syt(a, b) löytyy ilman
lukujen a ja b tekijöihinjakoa (jota suurten lukujen kyseessä ollen voi käytännössä olla mahdotonta löytää, ks.
RSA-haasteluvut vuoteen 2007 saakka – käytä netissä hakusanaa: The RSA Challenge Numbers).
Salakirjoitus
2
Algoritmi 2.1
syöte
Yksinkertainen Eukleideen algoritmi
a, b positiivisia kokonaislukuja, a ≥ b
while b > 0 do
begin
aseta r = jakojäännös, kun a on jaettu luvulla b
(siis r saadaan jakoyhtälöstä a = q b + r, 0 ≤ r < b)
aseta a = b
aseta b = r
end
tulosta a
Käyttämällä Mathematica-funktioita While, Floor ja Print, yllä esitetty algoritmi voidaan ohjelmoida seuraavasti. Viimeinen nollasta eroava jakojäännös on sama kuin Algoritmin 2.1 lopussa tulostama luku a, joka
puolestaan on syötteenä annettujen lukujen suurin yhteinen tekijä:
a = 2345; b = 987;
While[b ≠ 0, r = a - Floor[a / b] * b;
{a, b} = {b, r};
Print[{a, b}]]
{987, 371}
{371, 245}
{245, 126}
{126, 119}
{119, 7}
{7, 0}
Tämä algoritmi voitaisiin implementoida myös näin:
Clear[f];
f[{a_, b_}] := {b, r = a - Floor[a / b] * b};
NestWhileList[f, {2345, 987}, Last[#] ≠ 0 &]
{{2345, 987}, {987, 371}, {371, 245},
{245, 126}, {126, 119}, {119, 7}, {7, 0}}
Tarkastellaan vielä kahta yksityiskohtaista esimerkkiä.
Esimerkki 2.1
Laske lukujen 129 ja 15 suurin yhteinen tekijä.
Salakirjoitus
3
Ratkaisu.
Käytetään jakoyhtälöä a = q b + r, 0 ≤ r < b, toistuvasti. Lisäksi syt(a, b) = syt(b, r) = syt(r, r') = ... .
Siispä Eukleideen algoritmin toiminta näyttää seuraavalta:
129
15
9
6
=
=
=
=
8
1
1
2
15
9
6
3
*
*
*
*
9
6
3
0
+
+
+
+
syt (129, 15) = syt (15, 9)
syt (15, 9) = syt (9, 6)
syt (9, 6) = syt (6, 3)
syt (6, 3) = syt (3, 0) = 3
Viimeinen nollasta eroava jakojäännös = 3 = syt(129, 15).
Esimerkki 2.2
Laske lukujen 2345 ja 123 suurin yhteinen tekijä.
Ratkaisu.
Tässä saadaan tulokseksi syt = 1. Tämä tulos löydetään Eukleideen algoritmilla seuraavasti:
2345
123
8
3
2
=
=
=
=
=
19
15
2
1
2
*
*
*
*
*
123
8
3
2
1
+
+
+
+
+
8
3
2
1
0
Viimeinen nollasta eroava jakojäännös = 1 = syt(2345, 123).
Harjoituksia
11 (a) Määritä d = syt(1233, 63). Välivaiheet tulee merkitä selkeästi näkyviin
Tätä harjoitusta jatketaan vielä seuraavassa kohdassa 2.2 (esitä d muodossa d = s 1233 + t 63, missä s, t ∈ ℤ).
12 (a) Määritä syt(2333, 187).
Tätä harjoitusta jatketaan vielä seuraavassa kohdassa 2.2 (esitä d muodossa d = s 2333 + t 187,
missä s, t ∈ ℤ).
■ 2.2 Lineaarikombinaatio syt(a, b) = u a + v b
Salakirjoituksissa lasketaan usein hyvin suurten lukujen, sanokaamme a, jakojäännöksillä - modulo jokin luku b.
Tällaisten suurten lukujen a käänteisluvuilla (modulo b) on ensiarvoisen keskeinen merkitys, kuten tullaan
näkemään. Lauseen 1.4 mukaan on olemassa kertoimet u ja v ∈ ℤ joiden avulla syt(a, b) voidaan esittää muodossa syt(a, b) = u a + v b. Siinä tapauksessa, että syt(a, b) = 1, kuten oli edellä Esimerkissä 2.2, tälle lineaarikom
binaatiolle pätee u a + v b = 1. Tällöin saadaan u a = (–v) b + 1, ts. u a ≡ 1 (modulo b) ja siis u = a -1 (modulo
b). Näin ollen lineaarikombinaatiota 1 = syt(a, b) = u a + v b voidaan suoraan käyttää hyväksi käänteisalkioiden
Salakirjoitus
4
a-1 (modulo b) laskemisessa. Edellä esiintynyt merkintä x ≡ y (mod m) tarkoittaa, että m jakaa kokonaislukujen
x ja y erotuksen x - y, ts. x - y = q m, eräälle q ∈ ℤ. Tällöin sanotaan, että x ja y ovat keskenään kongruentteja modulo m. Kongruensseja käsitellään laajemmin Kappaleessa 3.
Kun Lauseen 1.4 mukaiset kertoimet u ja v halutaan löytää, ts. esittää syt(a, b) muodossa
syt(a, b) = u a + v b,
voidaan Algoritmia 2.1 muuntaa alla esitetyllä tavalla. Huomaa, että jättämällä pois symboleja u i ja vi sisältävät
rivit, tämä (laajennettu) algoritmi palautuu yksinkertaiseen Eukleideen algoritmiin, jossa
syt(a, b) = syt(r-1 , r0 ) = syt(r0 , r1 ) = syt(r1 , r2 ) = … = syt(rn-1 , rn ) = syt(rn-1 , 0) = rn-1 .
Algoritmin 2.2 oikeellisuuden todistus löytyy Liitteestä 1, jossa esitetään yksityiskohtaiseisesti tähän suoraviivaiseen tapaan johtavat tarkastelut. Liitteessä 1 nähdään lisäksi, että jakoalgoritmin tuottama jakojäännös r i
voidaan esittää jokaisella askeleella muodossa ui a + vi b. Viimeisen jakojäännöksen rn ollessa nolla, on
viimeinen nollasta eroava jakojäännös rn-1 juuri etsitty lineaarikombinaatio d = syt(a, b) = rn-1 = u a + v b.
Algoritmi todistetaan matemaattisella induktiolla ja siitä esitetään käsinlaskuesimerkkejä. Liitteestä 1 löytyvät myös
perustelut matriisien kertolaskun käytölle lineaarikombinaation laskemisessa. Lisäksi liitteestä 1 löytyy kaikki
välivaiheet esittävä Mathematica-ohjelma, jota Mathematica-ympäristön käyttäjät voivat kokeilla. Tuon ohjelman
voi myös muuttaa jossakin toisessa ohjelmointiympäristössä toimivaksi koodiksi.
Algoritmi 2.2
Laajennettu Eukleideen algoritmi
a≥b>0
r-1 = a; r0 = b;
u-1 = 1; u0 = 0; v-1 = 0; v0 = 1; n = 0;
while rn > 0 do begin
aseta
n = n + 1;
tulosta
rn-2 = qn rn-1 + rn , rn-1 > rn ≥ 0;
syöte
alkuarvot
aseta
un = un-2 - qn un-1 ;
aseta
vn = vn-2 - qn vn-1 ;
end
aseta u = un-1 ; v = vn-1 ;
(2.2)
syt(a, b) = rn-1 = u a + v b
Todistus:
Katso Liite 1 (Laajennettu Eukleideen algoritmi – suoraviivainen tapa).

Vaikka lukijaa kannustetaankin perehtymään Liitteeseen 1, emme vaadi tällä salakirjoitusten peruskurssilla siinä
esitettyjä perusteluja hallittavaksi loppukokeessa. Esitämme nyt kuitenkin yhden esimerkin tuossa liitteessä kirjoitetun laajennettuEukleides-ohjelman toiminnasta (vrt. Esimerkki 2.1 yllä):
Salakirjoitus
5
laajennettuEukleides[129, 15]
Laajennettu Eukleideen algoritmi toimii seuraavasti:
a = 129, b = 15
r-1 =129=q1 *r0 +r1 =8*15+9; r1 =9=u1 a +v1 b = (1)*129 + (-8)*15
r0 =15=q2 *r1 +r2 =1*9+6; r2 =6=u2 a +v2 b = (-1)*129 + (9)*15
r1 =9=q3 *r2 +r3 =1*6+3; r3 =3=u3 a +v3 b = (2)*129 + (-17)*15
r2 =6=q4 *r3 +r4 =2*3+0; r4 =0=u4 a +v4 b = (-5)*129 + (43)*15
Viimeinen nollasta eroava jakojäännös = 3 = syt(129, 15).
Lineaarikombinaatio: 3 = u a + v b = (2)*129 + (-17)*15.
Lasketaan tämä lineaarikombinaatio nyt käsin toisella, aluksi ehkä helpommalla, tavalla. Ainakin tämä alhaalta
ylöspäin etenevä laskutapa tulee hallita. Siis jompikumpi tapa kuuluu kurssin keskeisiin vaatimuksiin. Tavan suoraviivainen ylhäältä alas (yllä), tai alhaalta ylös (alla) - voi valita itse sen mukaan kumpi tuntuu luontevalta.
Esimerkki 2.3
Laske lukujen 129 ja 15 suurin yhteinen tekijä ja esitä se muodossa u*129 + v*15.
Esimerkissä 2.1 laskimme jo, että viimeinen nollasta eroava jakojäännös = 3 = syt(129, 15). Toistamme tässä vielä
nämä laskut:
(rivi 1)
(rivi 2)
(rivi 3)
(rivi 4)
129
15
9
6
=
=
=
=
8
1
1
2
*
*
*
*
15
9
6
3
+
+
+
+
9
6
3
0
Lähdemme nyt liikkeelle riviltä 3, jossa on viimeinen nollasta eroava jakojäännös (syt = 3). Saadaan:
3 = 9 - 1*6
(rivi 3)
Nyt riviltä 2 saadaan ratkaistuksi edeltävä jakojäännös 6 = 15 - 1*9. Sijoittamalla tämä laskematon lauseke (15
- 1*9) luvun 6 paikalle yllä, saadaan:
3
= 9 - 1*6
= 9 - 1*(15 - 1*9)
= -1*15 + 2*9
Riviltä 1 saadaan jakojäännös
(rivi 3)
(rivi 2)
(sievennys)
9 = 129 - 8*15. Sijoittamalla lauseke (129 - 8*15) luvun 9 paikalle edellä
Salakirjoitus
6
saatuun viimeiseen muotoon, saadaan:
3
=
=
=
=
=
9 - 1*6
9 - 1*(15 - 1*9)
-1*15 + 2*9
-1*15 + 2*(129 - 8*15)
2*129 + (-17)*15
(rivi 3)
(rivi 2)
(sievennys)
(rivi 1)
(sievennys)
Siis kysytty lineaarikombinaatio on 3 = syt(129, 15) = u a + v b = 2*129 + (-17)*15.
Huomattakoon vielä, että yllä sievennys tarkoitti sitä, että kootaan yhteen (so. esitetään yhden kertoimen avulla)
jatkossa ylemmiltä riveiltä löytyvät jakojäännökset (3, 6, 9) tai luvut b ja a (15, 129). Esitettyjä kertolaskuja ei
tule laskea loppuun, koska silloin menetettäisiin lausekkeiden rakenne. Laskemisen kannalta näitä lukuja käsitellään
siis symboleina tai merkkijonoina. Samaa ajattelutapaa kannattaa käytää myös, jos kirjoittaa tämän algoritmin
ohjelman muotoon symbolisen laskennan ohjelmointiympäristössä.
Esimerkki 2.4
Laske lukujen 2345 ja 123 suurin yhteinen tekijä ja esitä se muodossa u*2345 + v*123.
Esimerkissä 2.2 laskimme jo, että viimeinen nollasta eroava jakojäännös = 1 = syt(2345, 123). Toistamme vielä
nämä laskut:
(rivi 1)
(rivi 2)
(rivi 3)
(rivi 4)
(rivi 5)
2345
123
8
3
2
=
=
=
=
=
19
15
2
1
2
*
*
*
*
*
123
8
3
2
1
+
+
+
+
+
8
3
2
1
0
Laskemme nyt kysytyn lineaarikombinaation seuraavasti:
1
=
=
=
=
=
=
=
=
3 - 1*2
(rivi 4)
3 - 1*(8 - 2*3)
(rivi 3)
-1*8 + 3*3
(sievennys)
-1*8 + 3*(123 - 15*8)
(rivi 2)
3*123 - 46*8
(sievennys)
3*123 - 46*(2345 - 19*123)
(rivi 1)
-46*2345 + (3 + (-46)(-19))*123
(sievennys)
-46*2345 + 877*123
(sievennys)
Siis kysytty lineaarikombinaatio on 1 = syt(2345, 123) = u a + v b = (-46)*2345 + 877*123.
Mathematicassa tämä laajennettu Eukleideen algoritmi on standardi funktio ExtendedGCD. Lukujen suuruusjärjestyksestä käyttäjän ei tarvitse huolehtia:
a = 963; b = 4320; ExtendedGCD[a, b]
{9, {-157, 35}}
Salakirjoitus
7
Todellakin 9 = syt(963, 4320) = –157*963 + 35*4320.
Harjoituksia
11
Määritä syt(1233, 63) (ks. T11(a)) ja esitä se muodossa s 1233 + t 63, missä s, t ∈ ℤ.
Välivaiheet tulee merkitä selkeästi näkyviin. Huom. syt(1233, 63) laskettiin aiemmin tehtävässä 11(a).
12
Määritä syt(2333, 187) ja esitä se muodossa s 2333 + t 187, missä s, t ∈ ℤ.
Huom. syt(2333, 187) laskettiin aiemmin tehtävässä 12(a).
Lisäharjoitus. Valitse itse luvut a ja b. Laske valitsemillasi luvuilla d = syt(a, b)
ja esitä d muodossa d = s a + t b, missä s, t ∈ ℤ.
12'
■ 2.3 Eukleideen algoritmin kompleksisuus
Päätetään tämä kappale Eukleideen algoritmin kompleksisuuden tarkasteluun. Olkoon a > b ≥ 1. Verrataan
algoritmin toiminnan nopeutta luvun b arvoon. Algoritmin toiminta on hitaimmillaan silloin, kun jakojäännös rk
pienenee kussakin askeleessa rk-2 = qk rk-1 + rk mahdollisimman vähän. Tällaiseen tilanteeseen joudutaan, jos
rk-2 = rk-1 + rk aina kun 2 ≤ k ≤ n - 1, ts. jos jokaisella askeleella (ensimmäistä askelta a = q 1 b + r1 lukuunottamatta) osamäärä qk saa arvon 1 – edellyttäen, että se on ylipäätään mahdollista. Tällöin on myös voimassa
rn-2 = 2 rn-1 , kun rn = 0. Toisin sanoen, sellainen luvun a pienin arvo (a > b ≥ 1), jolle syt(a, b):n laskeminen
vie n - 2 askelta (so. riviä) saadaan, kun a = Fn ja b = Fn-1 , missä jono (Fi )i≥0 on kuuluisa Fibonacci-lukujono, joka määritellään asettamalla F 0 = 0, F1 = 1 ja Fi+2 = Fi+1 + Fi aina kun i ≥ 0. Jonon alku on siis: 0, 1,
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... .
Alla Mathematican toiminta kohdistuu toistuvasti listoihin, jotka koostuvat kahdesta peräkkäisestä Fibonacci-luvusta (tässä käytetään Nest-funktiota). Näin saadaan menetelmä Fibonacci-lukujen laskemiseksi. Tässä
esimerkissä lasketaan F99 ja F100 , jotka ovat jo hyvin suuria lukuja:
f[{u_, v_}] := {v, u + v}
n = 99; Nest[f, {0, 1}, n]
{218 922 995 834 555 169 026, 354 224 848 179 261 915 075}
Voidaan käyttää myös Mathematican sisäänrakennettua funktiota: Fibonacci.
Fibonacci[100]
354 224 848 179 261 915 075
Tarkastellaan vielä yllä esitettyä analyysiä kirjoittamalla Mathematicalla funktio sytIteroinnit.
käytetään jakojäännöksen laskemiseen valmista funktiota Mod:
Siinä
Salakirjoitus
8
sytIteroinnit[n_Integer?Positive,
m_Integer?Positive] := Module[
{ a = n, b = m, r, t = 0},
While[ b > 0, r = Mod[a, b];
{a, b, t} = {b, r, t + 1}]; t]
n = 100;
sytIteroinnit[Fibonacci[n], Fibonacci[n - 1]]
98
Siis tosiaankin syt(a, b):n laskeminen vie tapauksessa n = 100 juuri nuo aiemmin mainitut n - 2 askelta, kun
a = Fn ja b = Fn-1 .
Kokeillaan vielä tapausta n = 10. Tässäkin tapauksessa Eukleideen algoritmin toiminta päättyy 8 askeleessa, ts.
tarvitaan 8 riviä. Tässä Fibonacci-luvut ovat seuraavat:
{Fibonacci[10], Fibonacci[9]}
{55, 34}
Eukleideen algoritmi päättyy rivillä numero 8:
(rivi 1)
(rivi 2)
(rivi 3)
(rivi 4)
(rivi 5)
(rivi 6)
(rivi 7)
(rivi 8)
55
34
21
13
8
5
3
2
=
=
=
=
=
=
=
=
1
1
1
1
1
1
1
2
*
*
*
*
*
*
*
*
34
21
13
8
5
3
2
1
+
+
+
+
+
+
+
+
21
13
8
5
3
2
1
0
Asettamalla Fn = c f n Fibonacci-lukujen määrittelyrelaatiossa Fi+2 = Fi+1 + Fi , saadaaan f 2 = f + 1, jonka nollakohdat ovat:
1± 5
2
. Esitetään ilman todistusta seuraava yläraja Eukleideen algoritmin kompleksisuudelle. Se voidaan
todistaa induktiolla luvun a suhteen (erottelemalla tapaukset b ≤
a
f
ja
a
f
< b < a).
Lause 2.3 Eukleideen algoritmin kompleksisuus
1+
5
Olkoot a ja b positiivisia kokonaislukuja, a > b ≥ 1 ja olkoon f = 2 . Tällöin
Eukleideen algoritmin tarvitsema iteraatioiden lukumäärä syt(a, b):n laskemiseksi on korkeintaan
1 + log f b.
Testataan tätä vielä yhdellä suhteellisen suurella luvun b arvolla:
Salakirjoitus
9
a = Fibonacci[1000]; b = Fibonacci[999];
sytIteroinnit[a, b]
Ceiling[Log[(1 + Sqrt[5]) / 2 , b]]
998
998
Ja vielä yhdellä suhteellisen pienellä luvun b arvolla:
a = Fibonacci[1000]; b = 100;
sytIteroinnit[a, b]
Ceiling[Log[(1 + Sqrt[5]) / 2 , b]]
3
10
Alkuperäisessä Mathematica Notebookissa on mukana automaattisesti alustettavia laskentakoodeja, joista alla on
esimerkkinä funktio Eukleides. Tässä etsitään Fibonacci-lukujen suurinta yhteistä tekijää:
Salakirjoitus
10
Eukleides[ a = Fibonacci[30], b = Fibonacci[29] ]
Tässä syt = 1.
Tulos nähdään Eukleideen algoritmilla seuraavasti:
832 040
514 229
317 811
196 418
121 393
75 025
46 368
28 657
17 711
10 946
6765
4181
2584
1597
987
610
377
233
144
89
55
34
21
13
8
5
3
2
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
1
2
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
514 229
317 811
196 418
121 393
75 025
46 368
28 657
17 711
10 946
6765
4181
2584
1597
987
610
377
233
144
89
55
34
21
13
8
5
3
2
1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
317 811
196 418
121 393
75 025
46 368
28 657
17 711
10 946
6765
4181
2584
1597
987
610
377
233
144
89
55
34
21
13
8
5
3
2
1
0
Viimeinen nollasta eroava jakojäännös =
1 = syt(832 040, 514 229).
Käytännössä Eukleideen algoritmi toimii yleensä paljon nopeammin, kuin mitä Lauseen 2.3 yläraja antaisi aiheen
odottaa.