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

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

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

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

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