Sunteți pe pagina 1din 16

Alexandru Cohal Noiembrie 2013

Cuprins

Introducere ................................................................................... 3 Reprezentarea numerelor mari ................................................... 4 Citirea unui numr mare ............................................................ 5 Afiarea unui numr mare .......................................................... 6 Compararea a dou numere mari ............................................... 7 Suma a dou numere mari .......................................................... 8 Diferena a dou numere mari .................................................... 9 Produsul dintre un numr mare i o putere a lui 10 ............... 10 Produsul dintre un numr mare i un numr mic ................... 11 Produsul a dou numere mari .................................................. 12 mprirea dintre un numr mare i o putere a lui 10............. 13 mprirea dintre un numr mare i un numr mic ................ 14 mprirea a dou numere mari................................................ 15 Probleme ..................................................................................... 16 Legturi ...................................................................................... 16 Bibliografie................................................................................. 16

Introducere
Dac ntr-o problem avem nevoie s lucrm cu numere naturale mai mari dect 264 nu putem folosi niciunul dintre tipurile de date predefinite ale limbajului C/C++. Tipurile de date cele mai mari ale limbajului C/C++ sunt cele pe 8 bytes (octei): long long care poate reine valori n intervalul [ 9.223.372.036.854.775.808 , 9.223.372.036.854.775.807 ] = [263 , 263 1] unsigned long long care poate reine valori n intervalul [ 0 , 18.446.744.073.709.551.615 ] = [0, 264 1]. Prin urmare, trebuie s implementm propriul nostru tip de date pentru numere mari precum i funciile care s realizeze operaiile (comparare, adunare, scdere, nmulire, mprire) cu astfel de numere mari.

Reprezentarea numerelor mari


Vom reprezenta un numr natural mare ca pe un vector n care reinem n ordine cifrele sale ncepnd cu unitile. Exemplu: Numrul 1234 va fi reprezentat astfel:
0 1 2 3

4 3 2 1 Observaii: Se observ faptul c indicele poziiei din vector al oricrei cifre coincide cu puterea bazei corespunztoare acelei cifre. S considerm c numerele mari cu care vom lucra n continuare au maxim 100 de cifre. Declararea unui astfel de numr mare este:
char NrMare[DIMMAX];

Observaii: DIMMAX este o constant creia i-a fost atribuit valoarea 100 reprezentnd numrul maxim de cifre al unui numr mare. Tipul elementelor vectorului este char deoarece n fiecare poziie a vectorului va fi memorat o cifr (dac am fi folosit tipul int sau alt tip care s necesite mai mult de 1 byte am fi irosit memoria).

Citirea unui numr mare


Pentru a citi un numr mare vom citi de la tastatur ntreg numrul ntr-un ir de caractere, apoi vom transforma caracterele cifr n numere i le vom memora n ordine invers n vectorul ce va conine cifrele numrului mare. Pentru a uura calculele viitoare vom completa elemente vectorului care nu au fost ocupate de cifrele numrului mare cu valoarea 0. (Dac vectorul este declarat global atunci nu mai este nevoie de acest pas ntruct toate elementele sale sunt iniializate automat cu valoarea 0). Funcia citire() va avea doi parametri: Vectorul n care reinem cifrele numrului mare citit: char a[] Numrul de cifre ale numrului mare: int &lga Observaii: Am transmis parametrul lga prin referin pentru ca funcia citire() s poat modifica valoarea acestui parametru, valoarea rmnnd modificat i dup apel. Parametrul a nu este necesar s fie transmis prin referin, deoarece a este numele unui vector, fiind un pointer constant la primul element al vectorului. Este necesar ca funcia citire() s aib aceti doi parametri pentru cazul n care vrem s citim mai multe numere mari, apelnd astfel funcia pentru fiecare numr mare n parte.
void citire(char a[], int &lga) { char s[DIMMAX + 1]; int i; cin >> s; //determinam numarul de cifre lga = strlen(s); //transformam si retinem for (i=lga-1; i>=0; i--) a[lga - i - 1] = s[i] - '0'; //completam cu 0 for (i=lga; i<DIMMAX; ++i) a[i] = 0; }

Afiarea unui numr mare


Numrul mare va fi afiat astfel: Vom parcurge vectorul de cifre de la sfrit ctre nceput, afind succesiv fiecare cifr. La fel ca i funcia citire(), funcia afisare() va avea doi parametri: Vectorul n care au fost reinute cifrele numrului mare: char a[] Numrul de cifre ale numrului mare: int lga, care de aceast dat nu mai este o referin deoarece nu va mai fi modificat.
void afisare(char a[], int lga) { int i; //parcurgem si afisam for (i=lga-1; i>=0; --i) cout << (int) a[i]; cout << '\n'; }

Observaii: Deoarece elementele vectorului sunt de tipul char, dac am afia cifrele numrului mare astfel:
cout << a[i];

atunci cifrele ar fi fost considerate coduri ASCII i ar fi fost afiate caracterele corespunztoare. De aceea trebuie s facem conversia explicit de tip
(int) a[i]

i s form s se afieze cifra memorat.

Compararea a dou numere mari


S considerm dou numere mari memorate n vectorii a i b. Pentru a le compara, nti trebuie s comparm numrul lor de cifre. Dac numrul de cifre al lui a este mai mic dect numrul de cifre al lui b, atunci a este mai mic dect b (a < b). i invers: dac numrul de cifre al lui b este mai mic dect numrul de cifre al lui a, atunci b este mai mic dect a (a > b). Dac numerele a i b au acelai numr de cifre, atunci parcurgem cele dou numere ncepnd de la cifra cea mai semnificativ (cea cu indicele cel mai mare din vector) pn la ntlnirea a dou cifre distincte. Dac am ntlnit dou cifre diferite, ele determin ordinea dintre numerele a i b. n caz contrar, numerele sunt egale. Funcia comparare() va avea 4 parametri: Cei doi vectori n care au fost memorate cifrele celor dou numere mari Numrul de cifre ale celor dou numere. Funcia comparare() va returna o valoare care poate fi: -1 dac primul numr dat ca parametru este mai mare dect celalalt (a < b) 1 dac al doilea numr dat ca parametru este mai mare dect celalalt (a > b) 0 dac cele dou numere sunt egale (a = b).
int comparare(char a[], int lga, char b[], int lgb) { int i; //daca a are mai putine cifre decat b if (lga < lgb) return -1; //daca b are mai putine cifre decat a if (lgb < lga) return 1; //daca au acelasi numar de cifre, incep sa le compar for (i=lga-1; i>=0; --i) if (a[i] < b[i]) return -1; else if (a[i] > b[i]) return 1; //daca nu am gasit nicio cifra diferita return 0; }

Suma a dou numere mari


Pentru a calcula suma a dou numere mari vom parcurge simultan cele dou numere, ncepnd de la uniti (indicele 0 al vectorilor). Vom aduna cele dou cifre de pe poziii corespondente i vom lua n calcul i eventuala cifr de transport care s-a obinut de la adunarea precedent. Reinem n sum cifra obinut prin adunare (restul mpririi rezultatului adunrii celor dou cifre i a transportului la 10) i recalculm transportul (ctul mpririi rezultatului adunrii celor dou cifre i a transportului la 10). Funcia adunare() va avea 6 parametri: Cei doi vectori n care au fost memorate cifrele celor dou numere pe care vrem s le adunm i numrul lor de cifre Vectorul n care reinem rezultatul adunrii (char suma[]) Numrul de cifre al sumei (int &lgsuma) care va fi transmis prin referin.
void adunare(char a[], int lga, char b[], int lgb, char suma[], int &lgsuma) { int i, t = 0; //initializam lungimea vectorului suma cu maximul dintre lungimile celor doua numere lgsuma = max(lga, lgb); //parcurgem numerele cifra cu cifra si adunam for (i=0; i<lgsuma; ++i) { suma[i] = t + a[i] + b[i]; t = suma[i] / 10; suma[i] = suma[i] % 10; } //daca la sfarsit obtinem un transport nenul, mai avem o cifra if (t) suma[lgsuma++] = t; }

Observaii: Suma ar putea avea o cifr n plus fa de cel mai mare dintre numere (dac la sfritul adunrii cifra de transport este nenul). Din aceast cauz, trebuie s declarm vectorul suma cu un element n plus fata de maximul dintre numarul de elemente ale numerelor pe care vrem s le adunm (dac vectorii a i b au fost declarai ca avnd DIMMAX elemente, atunci vectorul suma trebuie s aiba DIMMAX + 1 elemente). Pentru c la citirea unui numr mare am completat elemente vectorului care nu au fost ocupate de cifrele numrului mare cu valoarea 0 putem efectua fr grija alterrii rezultatului adunarea cifr cu cifr chiar dac cele dou numere au lungimi diferite. Exemplu: Suma dintre numerele = 1234 i = 9999 va arta astfel:
0 1 2 3 4

a 4 3 2 1 0 b 9 9 9 9 0 a+b 3 3 2 1 1 8

Diferena a dou numere mari


Vom presupune c desczutul (numrul memorat n vectorul a) este mai mare dect scztorul (numrul memorat n vectorul b). Pentru a calcula diferena a dou numere mari vom parcurge desczutul ncepnd de la uniti (indicele 0 al vectorului). Vom scdea din cifra curent a desczutului cifra curent a scztorului i vom lua n calcul i eventuala cifr de transport obinut de la scderea precedent. Dac rezultatul este negativ, mprumutm 10 de pe poziia urmtoare (este posibil ntruct desczutul este mai mare dect scztorul) i n acest caz cifra de transport devine -1 Dac rezultatul este pozitiv, nu avem nevoie de mprumut i cifra de transport devine 0. Diferena ar putea avea mai puine cifre dect desczutul, astfel nct la sfrit verificm dac avem zerouri nesemnificative la nceputul numrului i actualizm numrul de cifre al rezultatului astfel nct prima cifr a vectorului diferenta (cea cu indicele cel mai mare) s fie diferit de zero. Parametrii funciei scadere() sunt similari cu cei ai funciei de adunare ( adunare() ).
void scadere(char a[], int lga, char b[], int lgb, char diferenta[], int &lgdiferenta) { int i, t = 0; //initializam lungimea vectorului diferenta lgdiferenta = lga; //parcurgem numerele cifra cu cifra si scadem for (i=0; i<lgdiferenta; ++i) { diferenta[i] = a[i] - b[i] + t; if (diferenta[i] < 0) { diferenta[i] += 10; t = -1; } else t = 0; } //verificam daca avem zerouri nesemnificative la inceputul rezultatului i = lgdiferenta - 1; while (diferenta[i] == 0) i--; //actualizam lungimea vectorului diferenta lgdiferenta = i + 1; }

Produsul dintre un numr mare i o putere a lui 10


Pentru a nmuli un numr mare cu 10 trebuie s adugm la sfritul numrului mare nr zerouri, adic s deplasm cifrele numrului mare reinute n vectorul a cu nr poziii la dreapta i s completm poziiile eliberate cu zerouri. Funcia inmultirePutere10() va avea 5 parametri: Vectorul n care a fost memorat numrul mare (char a[]) i numrul lui de cifre (int lga) Puterea lui 10 cu care nmulim numrul mare (int nr) Vectorul n care depunem calculul (char sol[]) i numrul lui de cifre (int &lgsol) (trimis prin referin).
void inmultirePutere10(char a[], int lga, int nr, char sol[], int &lgsol) { int i; //completez cu zerouri for (i=0; i<nr; ++i) sol[i] = 0; //copii cifrele numarului mare for (i=0; i<lga; ++i) sol[nr + i] = a[i]; //numarul de cifre al rezultatului lgsol = nr + lga; }

Exemplu: Produsul dintre numrul = 1234 i 103 va arta astfel:


0 1 2 3 4 5 6

a 4 3 2 1 0 0 0 103 0 0 0 4 3 2 1

10

Produsul dintre un numr mare i un numr mic


Pentru a nmuli un numr mare cu un numr mic (care se ncadreaz ntr-un tip de date al limbajului C/C++) putem aplica o metoda mai uoar dect s trecem numrul mic la reprezentarea numerelor mari i s aplicm produsul dintre dou numere mari. Se nmulete pe rnd, ncepnd cu cifra unitilor, fiecare cifr a numrului mare cu numrul mic, adunnd un eventual transport de la nmulirea precedent. La sfrit, dac mai avem un trasport care nu a fost cuprins n rezultat, l copiem cifr cu cifr n continuarea rezultatului. Parametrii funciei inmultireMic() sunt similari cu cei ai nmulirii unui numr mare cu o putere a lui 10 ( inmultirePutere10() ).
void inmultireMic(char a[], int lga, int nr, char produsMic[], int &lgprodusMic) { int i, cifra, t = 0; //parcurgem si inmultim for (i=0; i<lga; ++i) { cifra = a[i] * nr + t; t = cifra / 10; produsMic[i] = cifra % 10; } //initializam lungimea rezultatului lgprodusMic = lga; //daca mai avem transport, il adaugam la rezultat while (t != 0) { produsMic[lgprodusMic++] = t % 10; t = t / 10; } }

Observaii: i la acest tip de nmulire (ca i la nmulirea a dou numere mari) trebuie s avem grij la declararea dimensiunii vectorului rezultat (care va fi aleas n funcie de mrimea numrului mic).

11

Produsul a dou numere mari


La nceput, vom iniializa vectorul de cifre ale produsului cu 0. Pentru a calcula produsul dintre dou numere mari vom nmuli fiecare cifr a denmulitului (numrul memorat n vectorul a), pe rnd, cu fiecare cifr a nmulitorului (numrul memorat n vectorul b) i vom aduna fiecare produs parial obinut la rezultatul final (ale crui cifre sunt memorate n vectorul produs), pe poziia corespunztoare. Atunci cnd nmulim cifra a[j] cu toate cifrele numrului b, produsul parial obinut este nmulit cu 10 , adic cifrele sunt deplasate cu j poziii la dreapta. Parametrii funciei inmultire() sunt similari cu cei ai funciei de adunare ( adunare() ) i de scdere ( scadere() ).
void inmultire(char a[], int lga, char b[], int lgb, char produs[], int &lgprodus) { int i, j, t; //intializam cifrele vectorului cu 0 for (i=0; i<DIMMAX; ++i) produs[i] = 0; //parcurgem cele doua numere si calculam for (i=0; i<lga; ++i) { t = 0; for (j=0; j<lgb; ++j) { produs[i + j] = produs[i + j] + a[i] * b[j] + t; t = produs[i + j] / 10; produs[i + j] = produs[i + j] % 10; } //daca ne ramane un trasport la finalul unui produs partial, il punem pe pozitia urmatoare if (t) produs[i + j] = t; } //stabilim lungimea rezultatului lgprodus = lga + lgb - 1; if (produs[lgprodus]) lgprodus++; }

Observaii: Lungimea maxim a produsului este egala cu suma lungimilor celor doi termeni pe care i nmulim. Dac cei doi termeni ocup n totalitate toate poziiile vectorului (adica au DIMMAX cifre) atunci produsul nostru poate avea DIMMAX * 2 cifre. Aadar, i la nmulire (ca i la adunare) trebuie s avem grij la declararea dimensiunii vectorului rezultat.

12

mprirea dintre un numr mare i o putere a lui 10


La mprirea unui numr mare la 10 restul va fi compus din ultimele nr cifre ale numrului mare (cele de pe poziiile de la 0 la nr-1 din vectorul a), iar ctul va fi compus din restul cifrelor (cele de pe poziiile de la nr la lga-1 din vectorul a). Dac nr este mai mare dect lungimea numrului mare, atunci restul va fi egal cu numrul mare n ntregime, iar ctul va fi egal cu zero. Observaii: Deoarece mpritorul este un numr mic, iar restul este ntotdeauna mai mic dect mpritorul, restul va fi i el un numr mic. Parametrii funciei inmultireMic() sunt similari cu cei ai nmulirii unui numr mare cu o putere a lui 10 ( inmultirePutere10() ), iar n plus avem variabila care reine restul (int &rest) (transmis prin referin).
void impartirePutere10(char a[], int lga, int nr, char cat[], int &lgcat, int &rest) { int i; //determin restul for (i=min(nr-1, lga-1); i>=0; --i) rest = rest * 10 + a[i]; //determin catul for (i=nr; i<lga; ++i) cat[i - nr] = a[i]; //numarul de cifre ale catului lgcat = lga - nr; if (lgcat < 0) { lgcat = 1; cat[0] = 0; } }

Exemplu: Rezultatul mpririi numrului = 1234 la 103 va arta astfel:


0 1 2 3

a 4 3 2 1 ct 1 0 0 0 rest 234

13

mprirea dintre un numr mare i un numr mic


La fel ca la nmulirea dintre un numr mare i un numr mic, pentru a mpri un numr mare la un numr mic putem aplica o metoda mai uoar dect s trecem numrul mic la reprezentarea numerelor mari i s aplicm mprirea dintre dou numere mari. Aplicm aceeai simulare de mprire nvat la matematic, innd cont i de faptul c restul este i el un numr mic deoarece mpritorul este un numr mic. Parametrii funciei impartireMic() sunt aceeai ca la funcia inmultireMic() doar c mai avem n plus o variabil n care reinem restul (rest) (trimis prin referin).
void impartireMic(char a[], int lga, int b, char cat[], int &lgcat, int &rest) { int i; //initializez restul si lungimea catului rest = 0; lgcat = lga; //simulez impartirea for (i=lga-1; i>=0; --i) { rest = rest * 10 + a[i]; cat[i] = 0; while (b <= rest) { rest = rest - b; cat[i]++; } } //determin numarul de cifre ale catului while (!cat[lgcat - 1] && lgcat > 1) lgcat--; }

14

mprirea a dou numere mari


Dac cele dou numere mari sunt comparabile ca dimensiune, putem simula mprirea prin scderi repetate. n caz contrar, aceast metod este ineficient. Vom simula algoritmul de mprire nvat la matematic. Funcia impartire() va avea 8 parametri (vectorii corespunztori celor dou numere mari pe care vrem s le mprim (a, b), ctul (cat) i restul (rest) precum i numrul de cifre pentru fiecare dintre aceti vectori (lga, lgb, lgcat, lgrest)). Numerele de cifre ale ctului i ale restului vor fi transmise prin referin.
void impartire(char a[], int lga, char b[], int lgb, char cat[], int &lgcat, char rest[], int &lgrest) { int i; char aux[DIMMAX]; int lgaux; //initializez lungimile lgrest = 0; lgcat = lga; //simulez impartirea for (i=lga-1; i>=0; --i) { inmultirePutere10(rest, lgrest, 1, aux, lgaux); copie(aux, lgaux, rest, lgrest); rest[0] = a[i]; cat[i] = 0; //daca obtin un rest mai mare decat impartitor incep sa scad din rest impartitorul de cate ori pot while (comparare(b, lgb, rest, lgrest) != 1) { cat[i]++; scadere(rest, lgrest, b, lgb, rest, lgrest); } } //determin numarul de cifre ale catului si ale restului while (!cat[lgcat - 1] && lgcat > 1) lgcat--; while (!rest[lgrest - 1] && lgrest > 1) lgrest--; }

Observaii: S-a folosit funcia copie(char a[], int lga, char b[], int &lgb) care copie element cu element vectorul a n vectorul b i actualizeaz numrul de cifre ale vectorului b.

15

Probleme
Set (Campion) Sqr (Campion) Numar3 (Campion) Banda10 (Campion) Dale (Campion) Patrate2 (Infoarena) Pomi (Campion) Muguri (Campion) Test (Campion) Cutii (Campion) Aliniere (Campion) Aladdin2 (Infoarena) Biti2 (Infoarena) Petrecere (Campion) Fib (Campion) Sumb (Campion) Cos (Campion) Next (Infoarena) Tort (Infoarena) Culori3 (Infoarena)

Legturi
Ali algoritmi care pot fi folosii pentru nmulirea a dou numere mari: Algoritmul Karatsuba Algoritmul Toom - Cook

Bibliografie
Emanuela Cerchez, Marinel erban, Programarea n limbajul C/C++ pentru liceu volumul III, Editura Polirom, Iai, 2006

Alexandru Cohal alexandru.cohal@yahoo.com alexandru.c04@gmail.com Noiembrie 2013


16

S-ar putea să vă placă și