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.
© Copyright 2024