-
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