26
Algebarski algoritmi Uvek kada izvršavamo neku algebarsku operaciju, kao što je recimo množenje dva broja ili njihovo stepenovanje, mi u stvari izvršavamo neki algoritam. Mi te operacije koristimo kao gradivne elemente u razvijanju složenijih algoritama i često ne zalazimo dublje u analizu njihove složenosti. Međutim, i sami al- goritmi sabiranja, oduzimanja, množenja i deljenja brojeva (posebno ako su brojevi dati nizovima svojih cifara) predstavljaju važne algebarske algoritme. U algebarske algoritme spadaju i mnogi algoritmi sa kojima smo se već susreli kao što su izračunavanje vrednosti broja na osnovu datih cifara ili, nasuprot tome, određivanje cifara broja na osnovu njegove vrednosti, zatim razni algoritmi nad polinomima kao što su izračunavanje vrednosti polinoma i množenje polinoma. U ovom poglavlju bavićemo se algebarskim algoritmima sa kojima se do sada nismo susreli. Mnogi od njih igraju važnu ulogu u oblasti kriptografije, ali i u drugim oblastima. Modularna aritmetika Kažemo da je broj r ostatak pri deljenju broja x brojem y i pišemo x mod y = r ako i samo ako postoji broj q takav da je x = q · y + r i0 r<y. Pored toga što sa mod označavamo binarnu operaciju, mod se može koristiti i kao oznaka binarne relacije u skupu celih brojeva. Naime, pisaćemo a b (mod m) ako m|(a - b). Ovo je ekvivalentno tome da a i b daju isti ostatak pri deljenju sa m. Na primer, pišemo 12 2 (mod 5) jer 5|(12 - 2). Relaciju mod koristimo u nekim svakodnevnim situacijama, a da toga često nismo ni svesni. Jedan od takvih primera je rad sa vremenom. Naime, za 15 sata nakon 11 časova reći ćemo da je 2 sata (što odgovara tome da je 11 + 15 2(mod 24)). Slično važi i za dane u nedelji, koje računamo po modulu 7: danas je recimo četvrtak i interesuje nas koji će dan biti za 100 dana. Analogno se računa i mesec ili redni broj nedelje u godini. Slično je i prilikom rada sa uglovima. Modularna aritmetika ima puno praktičnih primena: koristi se za izračunavanje kontrolnih suma za međunarodne standardne identifikatore knjiga (ISBN brojeve) i identifikatore banki (IBAN). Modularna aritmetika je i u osnovi savremenih kriptografskih sistema. Brojevi se u računarima predstavljaju po modulu 2 k . Na primer, u jeziku C++ brojevi tipa unsigned int predstavljaju se po modulu 2 32 . Na primer, u narednom kodu rezultat kvadriranja broja 123456789 biće vrednost 123456789 2 mod 32 = 2537071545. 1

Algebarski algoritmi - University of Belgradepoincare.matf.bg.ac.rs/~vesnap/kaa/12_kaa-algebarski... · 2019. 6. 5. · Algebarski algoritmi Uvekkadaizvršavamonekualgebarskuoperaciju,kaoštojerecimomnoženje

  • Upload
    others

  • View
    26

  • Download
    0

Embed Size (px)

Citation preview

  • Algebarski algoritmi

    Uvek kada izvršavamo neku algebarsku operaciju, kao što je recimo množenjedva broja ili njihovo stepenovanje, mi u stvari izvršavamo neki algoritam. Mite operacije koristimo kao gradivne elemente u razvijanju složenijih algoritamai često ne zalazimo dublje u analizu njihove složenosti. Međutim, i sami al-goritmi sabiranja, oduzimanja, množenja i deljenja brojeva (posebno ako subrojevi dati nizovima svojih cifara) predstavljaju važne algebarske algoritme. Ualgebarske algoritme spadaju i mnogi algoritmi sa kojima smo se već susreli kaošto su izračunavanje vrednosti broja na osnovu datih cifara ili, nasuprot tome,određivanje cifara broja na osnovu njegove vrednosti, zatim razni algoritmi nadpolinomima kao što su izračunavanje vrednosti polinoma i množenje polinoma.U ovom poglavlju bavićemo se algebarskim algoritmima sa kojima se do sadanismo susreli. Mnogi od njih igraju važnu ulogu u oblasti kriptografije, ali i udrugim oblastima.

    Modularna aritmetika

    Kažemo da je broj r ostatak pri deljenju broja x brojem y i pišemo x mod y = rako i samo ako postoji broj q takav da je x = q ·y+ r i 0 ≤ r < y. Pored toga štosa mod označavamo binarnu operaciju, mod se može koristiti i kao oznakabinarne relacije u skupu celih brojeva. Naime, pisaćemo a ≡ b (mod m) akom|(a− b). Ovo je ekvivalentno tome da a i b daju isti ostatak pri deljenju sa m.Na primer, pišemo 12 ≡ 2 (mod 5) jer 5|(12− 2).

    Relaciju mod koristimo u nekim svakodnevnim situacijama, a da toga čestonismo ni svesni. Jedan od takvih primera je rad sa vremenom. Naime, za15 sata nakon 11 časova reći ćemo da je 2 sata (što odgovara tome da je11 + 15 ≡ 2 (mod 24)). Slično važi i za dane u nedelji, koje računamo pomodulu 7: danas je recimo četvrtak i interesuje nas koji će dan biti za 100 dana.Analogno se računa i mesec ili redni broj nedelje u godini. Slično je i prilikomrada sa uglovima.

    Modularna aritmetika ima puno praktičnih primena: koristi se za izračunavanjekontrolnih suma za međunarodne standardne identifikatore knjiga (ISBN brojeve)i identifikatore banki (IBAN). Modularna aritmetika je i u osnovi savremenihkriptografskih sistema.

    Brojevi se u računarima predstavljaju po modulu 2k. Na primer, u jezikuC++ brojevi tipa unsigned int predstavljaju se po modulu 232. Na primer, unarednom kodu rezultat kvadriranja broja 123456789 biće vrednost 1234567892mod 32 = 2537071545.

    1

  • unsigned int x = 123456789;cout

  • Važi i naredno tvrđenje:

    an mod m = (a mod m)n mod m

    Problem: Napiši program koji određuje poslednje tri cifre zbira i poslednje tricifre proizvoda četiri uneta cela broja manja od 999.

    Ideja koja prirodno prva padne na pamet je da se izračunaju zbir i proizvodunetih brojeva, a da se onda poslednje tri cifre odrede izračunavanjem ostatkapri celobrojnom deljenju sa 1000. Kada se u obzir uzme raspon brojeva kojise unose, takvo rešenje bi davalo korektne rezultate u slučaju zbira, međutim,proizvod brojeva može biti mnogo veći od samih brojeva i zato je za izračunavanjeposlednje tri cifre proizvoda potrebno primeniti malo napredniji algoritam kojikoristi činjenicu da su za poslednje tri cifre proizvoda relevantne samo poslednjetri cifre svakog od činilaca. Zato je pre množenja moguće odrediti ostatak prideljenju sa 1000 svakog od činilaca, izračunati proizvod, a onda odrediti njegovetri poslednje cifre izračunavanjem njegovog ostatka pri deljenju sa 1000.

    Funkcije za množenje i sabiranje po modulu zasnovaćemo direktno na prikazanimrelacijama, a onda ćemo ih primenjivati tako što ćemo proizvod (tj. zbir) pomodulu m svih prethodnih brojeva primenom funkcija kombinovati sa novimbrojem. Dakle, ako funkciju za sabiranje po modulu m označimo sa +m, amnoženje po modulu m sa ·m, izračunavaćemo ((a+m b) +m c) +m d tj. ((a ·mb) ·m c) ·m d.

    Ipak, naglasimo da je izračunavanje celobrojnog količnika i ostatka vremenskizahtevna operacija (najčešće se izvršava dosta sporije nego osnovne aritmetičkeoperacije), tako da je u praksi poželjno izbeći ih kada je to moguće. Na primer,ako će zbir a + b biti moguće predstaviti odabranim tipom podataka, tada jeumesto (a mod m+ b mod m) mod m ipak bolje koristiti (a+ b) mod m.

    int plus_mod(int a, int b, int m){return ((a % m) + (b % m)) % m;

    }

    int puta_mod(int a, int b, int m){return ((a % m) * (b % m)) % m;

    }

    int main(){

    int a, b, c, d;cout a >> b >> c >> d;cout

  • }

    // n je neparno// prolazimo kroz neparne brojevefor (int i = 3; i

  • prostog broja označavamo da su ti brojevi složeni, čuvaćemo vrednost najmanjegprostog činioca tog broja. Nakon toga izračunaćemo činioce broja n uzastopnimdeljenjem broja n njegovim najmanjim prostim činiocem.

    const int MAX = 1000;

    // niz u kome cemo cuvati najmanji cinilac za svaki brojvector najmanjiCinilac(MAX);

    void eratosten(){

    // postavljamo da je najmanji cinilac svakog broja sam taj brojfor (int i = 1; i < MAX; i++)

    najmanjiCinilac[i] = i;

    for (int d = 2; d*d

  • return 0;}

    Složenost prvog koraka algoritma – određivanja najmanjeg prostog činioca svihbrojeva do n jednaka je složenosti Eratostenovog sita O(n log logn), a ispisivanjačinioca datog broja je O(logn) jer je maksimalni broj činilaca broja n jednaklog2 n. Dakle, ukoliko je potrebno izvršiti faktorizaciju na proste činioce O(n)puta onda će složenost ovog algoritma biti O(n log logn)+O(n logn) = O(n logn)što je efikasnije od prethodnog pristupa.

    Za veliko n prikazani algoritam nije dovoljno efikasan. Važi i sledeće: nijejednako teško faktorisati sve brojeve jedne iste dužine. Najteže instance ovihproblema čine brojevi koji su proizvodi dva prosta broja. Kada su oba ovaprosta činioca velika i slične veličine, ovaj problem postaje jako teško rešiti. 2009.godine okončao se dvogodišnji napor grupe istraživača da faktoriše broj od 232cifre korišćenjem više stotina računa. Međutim, do danas nije ni dokazano da nepostoji neki efikasan algoritam koji bi rešavao ovaj problem. Pretpostavljenatežina ovog problema čini srž velikog broja kriptografskih algoritama, kao što jeRSA.

    Ojlerova funkcija

    Ojlerova funkcija φ broja n označava broj prirodnih brojeva manjih ili jednakihn koji su uzajamno prosti sa n. Na primer, φ(9) = 6 jer postoji šest brojeva1, 2, 4, 5, 7, 8 manjih od 9 koji su uzajamno prosti sa 9.

    Problem: Za dati prirodan broj n izračunati vrednost Ojlerove funkcije φ(n).

    Direktan način da se reši problem bio bi da se redom prođe kroz sve brojeve od1 do n− 1 i da se izbroji koliko je brojeva od njih uzajamno prosto sa n.

    // funkcija koja racuna najveci zajednicki delilac dva brojaint nzd(int a, int b){

    if (a == 0)return b;

    return nzd(b % a, a);}

    // funkcija koja racuna vrednost Ojlerove funkcijeint ojlerovaFunkcija(int n){

    // 1 je uvek uzajamno prosto sa nint br = 1;for (int i = 2; i < n; i++)

    if (nzd(i, n) == 1)br++;

    return br;}

    7

  • int main(){

    int n;cout n;cout

  • // rezultat inicijalizujemo na nint br = n;

    // za svaki prost cinilac p broja n// rezultat mnozimo sa 1-1/p = (p-1)/p// usput azuriramo vrednost broja nfor (int p = 2; p*p 1)

    br = br*(n-1)/n;

    return br;}

    Složenost najgoreg slučaja (kada je n = p2, za neko prosto p ili ako je sam brojn prost) je O(

    √n).

    S obzirom na to da važi br · pp−1 = br · (1−1p ) = br−

    brp , možemo izbeći množenje

    u prethodnoj implementaciji tako što ćemo od n oduzeti broj umnožaka svakogprostog činioca.

    int ojlerovaFunkcija(int n){

    // rezultat inicijalizujemo na nint br = n;

    // za svaki prost cinilac p oduzimamo od br broj njegovih umnozakafor (int p = 2; p*p

  • if (n > 1)br -= br / n;

    return br;}

    Ukoliko bi zadatak bio da se izračuna vrednost Ojlerove funkcije za sve vrednostimanje ili jednake n, ova implementacija bi bila složenosti O(n

    √n). Kao i ranije,

    razmotrimo varijantu algoritma zasnovanu na Eratostenovom situ. Inicijalizo-vaćemo sve vrednosti φ(i), 1 ≤ i ≤ n na i. Razmatraćemo redom vrednosti zap počev od 2 do n i ukoliko je φ(p) = p, to znači da je broj p prost i vrednostiOjlerove funkcije svih umnožaka broja p pomnožićemo sa 1− 1p .

    void ispisiVrednostiOjleroveFunkcijeDoN(int n){

    vector ojlerovaFunkcija(n+1);

    // inicijalizujemo sve vrednostifor (int i = 1; i

  • cin >> n;ispisiVrednostiOjleroveFunkcijeDoN(n);return 0;

    }

    Složenost ovog algoritma odgovara složenosti Eratostenovog sita, odnosno jednakaje O(n log logn).

    Jedno važno svojstvo Ojlerove funkcije jeste Ojlerova teorema.

    Ojlerova teorema: Ako je n ≥ 1 i a je ceo broj uzajamno prost sa n, onda važi

    aφ(n) ≡ 1 (mod n)

    Ova teorema predstavlja uopštenje Male Fermaove teoreme.

    Mala Fermaova teorema: Ako je p prost broj, onda za svaki ceo broj a važi

    ap ≡ a (mod p)

    Pretpostavimo da je n jednako proizvodu dva različita prosta broja p i q. Problemfaktorizacije broja n je težak, međutim ako je pored n poznata i vrednost φ(n),onda se p i q mogu jednostavno odrediti. Naime važi φ(n) = (p − 1) · (q − 1).Odavde dobijamo da je n− φ(n) + 1 = p+ q. Na osnovu vrednosti p+ q i p · qlako se mogu odrediti brojevi p i q. Ovo je važna stvar u razmatranju sigurnostiRSA enkripcije. Naime, jačina metoda enkripcije se zasniva na tajnosti vrednostiφ(n), a kao što smo prethodno videli računanje vrednosti Ojlerove funkcije jeekvivalentno faktorizaciji broja, što se smatra teškim problemom.

    Broj i zbir delilaca

    Problem: Za dati broj n izračunati ukupan broj njegovih delilaca.

    Na primer, ako je n = 24, njegovi delioci su 1, 2, 3, 4, 6, 8, 12, 24 te je traženavrednost 8.

    Naivni pristup bi prolazio skupom svih brojeva od 1 do n i za svaku vrednostkoja deli n inkrementirao bi ukupni broj delilaca.

    // funkcija koja racuna broj delilaca broja nint brojDelilaca(int n){

    // broj delilaca je bar 1 -- sam taj brojint br = 1;for (int i = 1; i < n; i++)

    if (n % i == 0)br++;

    return br;}

    11

  • Primetimo da se delioci uglavnom javljaju u paru: u prethodnom primeru imamoda je 24 = 1 ·24 = 2 ·12 = 3 ·8 = 4 ·6. Ovo ne važi samo u slučaju kvadrata nekogbroja, npr. delioci broja 16 su 1, 2, 4, 8, 16 gde srednji delilac nema svog para.Nešto efikasniji algoritam bi prolazio skupom svih brojeva manjih ili jednakih√n i za svaki broj d koji deli broj n inkrementirao bi ukupan broj delilaca za 2,

    ako je d 6= nd , a inače za 1.

    // funkcija koja racuna broj delilaca broja nint brojDelilaca(int n){

    int br = 0;for (int i = 1; i

  • // za sve vrednosti od 2 do nfor (int p = 2; p

  • Malo efikasniji pristup bi delioce otkrivao u paru, tako što bi prolazio skupomsvih brojeva manjih ili jednakih

    √n i za svaki broj i koji deli broj n vrednost i i

    vrednost njemu odgovarajućeg činioca n/i bi dodavao na ukupnu sumu, osimkada je i = n/i, odnosno kada je i =

    √n.

    // funkcija koja racuna sumu delilaca broja nint sumaDelilaca(int n){

    int suma = 0;for (int i = 1; i

  • }sumaDelilaca = sumaDelilaca * (pow(p,br+1) - 1)/(p-1);

    }}

    }return sumaDelilaca;

    }

    Iza ovih efikasnijih rešenja krije se to da su ove dve funkcije: broj delilaca i sumadelilaca multiplikativne, odnosno važi da je f(a · b) = f(a) · f(b) za uzajamnoproste brojeve a i b. Preciznije, σk(n) je funkcija delilaca i označava sumuk-tih stepena svih pozitivnih delilaca broja n i za nju važi da je multiplikativna.Specijalno, σ0(n) označava broj delilaca, a σ1(n) sumu delilaca broja n. Dakle,ako saB(n) označimo broj delilaca broja n, a sa S(n) sumu njegovih delilaca, ondaza uzajamno proste brojeve x i y važi B(x ·y) = B(x) ·B(y) i S(x ·y) = S(x) ·S(y)(svaki činilac broja x može se iskombinovati sa svakim činiocem broja y i na tajnačin dobijamo sve moguće činioce broja xy).

    Prošireni Euklidov algoritam

    Podsetimo se ideje osnovnog Euklidovog algoritma za određivanje najvećegzajedničkog delioca brojeva a i b: polazeći od brojeva r0 = a i r1 = b, izračunavase ostatak r2 = r0 mod r1, zatim ostatak r3 = r1 mod r2, itd. Tako se dobijaopadajući niz ostataka

    ri−1 = qiri + ri+1, 0 ≤ ri+1 < ri, za i = 1, 2, . . . , k (1)

    (pri deljenju ri−1 sa ri količnik je qi, a ostatak ri+1). Dobijeni niz je konačan jerje opadajući i sastoji se od prirodnih brojeva. Neka je npr. rk+1 = 0 i neka jerk 6= 0 poslednji član ovog niza različit od nule. Kako je

    nzd(r0, r1) = nzd(r1, r2) = . . . = nzd(rk−1, rk) = nzd(rk, 0) = rk,

    vidimo da je d = nzd(a, b) upravo jednako rk, poslednjem ostatku različitom odnule.

    Uz malu dopunu, Euklidov algoritam se može iskoristiti i za rešavanje sledećegproblema.

    Problem: Najveći zajednički delilac d dva prirodna broja a i b izraziti kaonjihovu celobrojnu linearnu kombinaciju. Drugim rečima, odrediti cele brojeve xi y, tako da važi d = nzd(a, b) = x · a+ y · b.

    Cilj je d izraziti u obliku celobrojne linearne kombinacije d = x · r0 + y · r1ostataka r0 = a i r1 = b. Problem se može rešiti indukcijom. Polazi se od baze,izraza za d u obliku linearne kombinacije ostataka rk−1 i rk−2:

    d = rk = rk−2 − qk−1rk−1

    15

  • Ovaj izraz je ekvivalentan pretposlednjem deljenju u Euklidovom algoritmu(izraz (1) za i = k − 1).

    Korak indukcije bio bi da se polazeći od izraza d = x′ri + y′ri+1 kao celobrojnelinearne kombinacije ostataka ri i ri+1, broj d izrazi u obliku celobrojne linearnekombinacije ostataka ri−1 i ri, odnosno kao d = x′′ri−1 + y′′ri. Zaista, zamenomri+1 u prethodnom izrazu sa ri−1 − qiri, dobija se

    d = x′ri + y′ri+1 = x′ri + y′(ri−1 − qiri) = y′ri−1 + (x′ − qiy′)ri, (2)

    odnosno:

    x′′ = y′

    y′′ = x′ − qiy′ (3)

    tj. dobija se izraz za d u obliku celobrojne linearne kombinacije ostataka ri−1i ri. Dakle, indukcijom po i, i = k − 2, k − 1, . . . , 1 dokazano je da se d možeizraziti kao celobrojna linearna kombinacija bilo koja dva uzastopna člana ri,ri+1 niza ostataka. Specijalno, za i = 0, dobija se traženi izraz. Ova verzijaEuklidovog algoritma naziva se prošireni Euklidov algoritam.

    Primer: Odrediti cele brojeve x i y tako da važi 3 = 33x+ 24y. S obzirom nato da je nzd(33, 24) = 3 možemo iskoristiti prethodni postupak za određivanjebrojeva x i y. Prilikom izračunavanja najvećeg zajedničkog delioca imamo naredniniz jednakosti: nzd(33, 24) = nzd(24, 9) = nzd(9, 6) = nzd(6, 3) = nzd(3, 0).Prateći ovaj niz jednakosti dobijamo: d = 3 = 9−6 = 9−(24−2 ·9) = 3 ·9−24 =3 · (33− 24)− 24 = 3 · 33− 4 · 24, odnosno x = 3, y = −4.

    Rekurzivna verzija ovog algoritma bila bi sledeća:

    int nzdProsireni(int a, int b, int &x, int &y){

    // ako je b jednako 0, onda je nzd(a,b) = aif (b == 0){

    x = 1;y = 0;return a;

    }

    int x1, y1;// rekurzivno resavamo problem za brojeve b i a%bint nzd = nzdProsireni(b, a%b, x1, y1);// na osnovu (3) vazi naredna vezax = y1;y = x1 - (a/b) * y1;

    16

  • return nzd;}

    int main(){

    int a, b;int x, y;cout a >> b;int d = nzdProsireni(a, b, x, y);cout

  • x_tek = xpom;

    int ypom = y_preth - q*y_tek;y_preth = y_tek;y_tek = ypom;

    }

    // postavljamo konacne vrednosti za x i y// kao vrednosti x i y za r_kx = x_preth;y = y_preth;

    // vracamo a kao nzd brojevareturn a;

    }

    int main(){

    int a, b;int x, y;cout a >> b;int d = nzdProsireni(a, b, x, y);cout

  • 1 (mod 11). Multiplikativni inverz po modulu m mora biti iz skupa vrednosti{0, 1, 2 . . . ,m− 1}.

    Postavlja se pitanje da li modularni inverz uvek postoji. Pokazuje se da onpostoji ako i samo ako su brojevi a i m uzajamno prosti i tada je jedinstven.

    Problem: Za data dva cela broja a i m odrediti multiplikativni inverz broja apo modulu m.

    Naivni pristup rešavanju ovog problema bi za x razmatrao redom brojeve od 1do m i proveravao da li je ostatak pri deljenju broja a · x sa m jednak 1.

    // multiplikativni inverz broja a po modulu mint modInverz(int a, int m){

    a = a % m;// proveravamo redom kandidate od 1 do m-1for (int x = 1; x < m; x++)

    if ((a*x) % m == 1)return x;

    }

    int main(){

    int a, m;cout a >> m;cout

  • a · x+m · y ≡ 1 (mod m)

    Drugi sabirak na levoj strani jednačine je deljiv sa m pa ga možemo ukloniti, tedobijamo:

    a · x ≡ 1 (mod m)

    odakle sledi da je multiplikativni inverz broja a po modulum jednak (x mod m+m) mod m.

    Dakle, određivanje modularnog multiplikativnog inverza možemo svesti naprošireni Euklidov algoritam.

    // multiplikativni inverz broja a po modulu mint modInverz(int a, int m){

    int x, y;// odredjujemo x i y tako da vazi a*x+m*y=dint d = nzdProsireni(a,m,x,y);

    // ako a i m nisu uzajamno prosti, onda ne postoji inverzif (d != 1)

    cout

  • int q = r / r1;int tmp;

    tmp = r;r = r1;r1 = tmp - q*r1;

    tmp = x;x = x1;x1 = tmp - q*x1;

    }

    // x postavljamo na pozitivnu vrednostif (x < 0)

    x += m;

    // vracamo true ako je nzd(a,m)=1, inace falsereturn r == 1;

    }

    Modularni multiplikativni inverz ima primenu u oblasti kriptografije, npr. uRSA algoritmu.

    RSA algoritam je jedan od prvih sistema šifrovanja koji se zasniva na javnomključu. U ovakvim sistemima ključ koji se koristi za kodiranje je javni i on serazlikuje od ključa koji se koristi za dekodiranje koji je tajni. Razmotrimo kakoizgleda kodiranje i dekodiranje u RSA algoritmu.

    1. Izaberemo dva različita prosta broja, p i q.2. Izračunamo n kao n = p · q3. Izračunamo k = φ(n) = (p− 1) · (q − 1)4. Biramo vrednost e tako da je uzajamno prosta sa k5. Odredimo d kao multiplikativni inverz broja e po modulu k, odnosno tako

    da važi e · d ≡ 1 (mod k)6. Brojevi n i e su javni, a broj d je privatni7. Poruka m koja se predstavlja celim brojem manjim od n se kodira raču-

    nanjem S = me (mod n)8. Poruka se dekodira računanjem t = Sd (mod n)9. Prema Ojlerovoj teoremi i kineskoj teoremi o ostacima važi da je t = m

    (ne navodimo dokaz)

    Sigurnost sistema RSA bi se narušila ako bi se broj n mogao efikasno faktorisatiili ako bi se φ(n) moglo odrediti bez faktorizacije broja n

    21

  • Kineska teorema o ostacima

    Prema jednoj kineskoj legendi, general Han Xin je da bi izbegao da špijuni saznajusa kolikom je vojskom krenuo u boj, svom kuriru je davao samo informacijuo ostatku broja vojnika u pravougaonikoj formaciji. Njegove poruke su bile unarednoj formi: “Kada se vojnici organizuju u redove od po 9, preostaje njih4; ako su u redovima od po 11, niko ne preostaje; ako su u redovima od po 13,samo jedan preostaje, a ako su u redovima od po 19, preostaje njih trojica”.General je takođe svom kuriru preneo da je broj vojnika drugo najmanje rešenjeovog problema. Pitanje je kolikim brojem vojnika je general zapovedao? Ovajproblem odgovara tzv. Kineskoj teoremi o ostacima, jednom od standardnihproblema iz teorije brojeva. Formulišimo ovaj problem u standardnim terminima.

    Problem: Data su dva niza brojeva r: r0, r1, . . . , rn−1 i m: m0,m1, . . . ,mn−1pri čemu za svaki par brojeva niza m važi da su uzajamno prosti. Odreditinajmanji pozitivan broj x za koji važi:

    x mod m0 = r0x mod m1 = r1

    ...

    x mod mn−1 = rn−1 (4)

    Pokazuje se da uvek postoji x koje zadovoljava dati skup jednačina i da je onojedinstveno po modulu m0 ·m1 · . . . ·mn−1.

    Naivni pristup bi krenuo redom od 1 po skupu prirodnih brojeva i proveravaoda li tekući broj zadovoljava date uslove.

    int izracunajMinX(vector m, vector r){

    int n = m.size();// inicijalizujemo vrednost xint x = 1;

    // prema tvrdjenju Kineske teoreme o ostacima// ova petlja ce se uvek zaustavitiwhile (true){

    int j;// za svako i od 0 do n-1// proveravamo da li je ostatak pri deljenju sa m_j jednak r_jfor (j = 0; j < n; j++)

    if (x % m[j] != r[j])break;

    22

  • // ako su sve iteracije prosle uspesno// nasli smo vrednost za xif (j == n)

    return x;

    // inace nastavljamo sa pretragomx++;

    }return x;

    }

    int main(void){

    vector m = {3, 4, 5};vector r = {2, 3, 1};

    // m i r moraju biti istih dimenzijaif (m.size() != r.size()){

    cout

  • z0 =M

    m0, z1 =

    M

    m1, . . . , zn−1 =

    M

    mn−1

    Za svako 0 ≤ i < n važi sledeće:

    1. zi ≡ 0 (mod mj) za j 6= i2. zi i mi su uzajamno prosti brojevi

    Ako je broj wi deljiv svim brojevima mj osim eventualno sa mi, onda je ondeljiv i sa zi, odnosno mora da predstavlja neki njegov umnožak.

    Uvedimo dalje naredne oznake:

    y0 ≡ z−10 (mod m0)

    y1 ≡ z−11 (mod m1)

    ...

    yn−1 ≡ z−1n−1 (mod mn−1)

    Ovi modularni multiplikativni inverzi postoje jer važi da su brojevi zi i mi uza-jamno prosti, i možemo ih odrediti proširenim Euklidovim algoritmom. Prime-timo da važi:

    1. yi · zi ≡ 0 (mod mj) za j 6= i2. yi · zi ≡ 1 (mod mi)

    Ova svojstva nam omogućavaju da definišemo brojeve w0, w1, . . . , wn−1 kojezadovoljavaju zahteve iz prethodne tabele na sledeći način:

    w0 = y0 · z0 mod M

    w1 = y1 · z1 mod M

    ...

    wn−1 = yn−1 · zn−1 mod M

    Proverimo da li će zaista ovako definisano x = r0 ·w0 + . . .+rn−1 ·wn−1 (mod M)davati ostatak ri pri deljenju sa mi.

    x ≡ r0y0z0 + r1y1z1 + . . .+ rn−1yn−1zn−1 (mod M) (mod mi)≡ r0y0z0 + r1y1z1 + . . .+ rn−1yn−1zn−1 (mod mi)≡ riyizi (mod mi)≡ ri (mod mi) (5)

    24

  • Druga jednačina važi na osnovu toga što je M mod mi = 0, treća na osnovutoga što je yi ·zi ≡ 0 (mod mj) za j 6= i, a četvrta na osnovu yi ·zi ≡ 1 (mod mi).

    Pretpostavimo da postoje dva različita rešenja u i v. Tada važi

    m0|(u− v),m1|(u− v), . . . ,mn−1|(u− v)

    S obzirom da su svi m0,m1, . . . ,mn−1 uzajamno prosti, važi i da m0m1 · . . . ·mn−1|(u−v), odnosno važi da je u ≡ v (mod m0m1 · . . . ·mn−1), odnosno rešenjeje jedinstveno po modulu m0m1 · . . . ·mn−1.

    // proizvod brojeva x i y po modulu mlong long pm(long long x, long long y, long long m) {

    return ((x % m) * (y % m)) % m;}

    // proizvod brojeva x, y i z po modulu mlong long pm(long long x, long long y, long long z, long long m) {

    return pm(pm(x, y, m), z, m);}

    // zbir brojeva x i y po modulu mlong long zm(long long x, long long y, long long m) {

    return ((x % m) + (y % m)) % m;}

    // na osnovu kineske teoreme o ostacima se izracunava rezultat tako da// za sve elemente nizova m i a vazi da je rezultat mod m[i] = r[i]// funkcija vraca da li je rezultat bilo moguce nacibool kto(long long m[], long long r[], int n, long long& rezultat) {

    long long M = 1;// racunamo proizvod svih modulafor (int i = 0; i < n; i++)

    M *= m[i];

    rezultat = 0;for (int i = 0; i < n; i++) {

    long long zi = M / m[i];long long zi_inv;if (!modInverz(zi, m[i], zi_inv))

    return false;rezultat = zm(rezultat, pm(r[i], zi_inv, zi, M), M);

    }return true;

    }

    25

  • int main(){// ucitavamo ulazne podatkelong long r[3], m[3];for (int i = 0; i < 3; i++)

    cin >> r[i] >> m[i];

    // rezultat izracunavamo na osnovu kineske teoreme o ostacimalong long rezultat;kto(m, r, 3, rezultat);

    // ispisujemo rezultatcout