Numere mari: împărțirea cu rest la un număr natural
Împărțirea unui număr mare A la un număr natural
„mic” (care încape în int sau
long long) funcționează ca împărțirea lungă
de la școală: mergem de la cifra cea mai
semnificativă spre cea mai puțin semnificativă,
acumulând restul la fiecare pas.
Spre deosebire de celelalte operații (care pornesc de la cifra unităților), împărțirea pornește de la stânga.
Algoritm
Facem împărțirea ca la școala primară. La fiecare pas avem un rest curent. Aducem „jos” cifra următoare (adică lipim cifra la rest), împărțim, cifra cât merge să împărțim e cifra rezultatului la acea poziție, noul rest e restul efectiv al împărțirii.
rest = 0
pentru i = lenA .. 1: // de la cea mai semnificativă la cea mai puțin
rest = rest * 10 + a[i]
c[i] = rest / x
rest = rest % x
c[0] = lenA
// elimin zero-urile din față la c
La final, rest conține restul
împărțirii (cât a rămas nedivizibil).
Pas cu pas:
1234 / 7
Împărțire lungă:
1234 / 7:
12 / 7 = 1 rest 5 → c[4] = 1, rest = 5
53 / 7 = 7 rest 4 → c[3] = 7, rest = 4
44 / 7 = 6 rest 2 → c[2] = 6, rest = 2
Aici am început de la cifra „1234” (prima cifră din stânga) -
dar reprezentarea noastră e inversă. Deci parcurgem
a[4]=1, a[3]=2, a[2]=3, a[1]=4 în această
ordine.
Reprezentare:
a = 1234→ a[0]=4, a[1]=4, a[2]=3, a[3]=2, a[4]=1
Algoritm (parcurgere i = 4, 3, 2, 1):
| i | a[i] | rest vechi | rest·10 + a[i] | c[i] = ÷7 | rest nou |
|---|---|---|---|---|---|
| 4 | 1 | 0 | 1 | 0 | 1 |
| 3 | 2 | 1 | 12 | 1 | 5 |
| 2 | 3 | 5 | 53 | 7 | 4 |
| 1 | 4 | 4 | 44 | 6 | 2 |
Rezultat brut:
c[0]=4, c[1]=6, c[2]=7, c[3]=1, c[4]=0.
Cifra de top (c[4] = 0) e zero.
Elimin prin:
while (c[0] > 1 && c[c[0]] == 0) c[0]--;
c[0] devine 3. Rezultat: cifrele
c[1]=6, c[2]=7, c[3]=1 → afișat în ordine inversă →
176.
Verificare: 1234 = 7 · 176 + 2 = 1232 + 2 = 1234
✓. Rest final = 2.
Cod
const int NMAX = 1005;
int a[NMAX], c[NMAX];
// Împarte A la x. Scrie câtul în c, returnează restul.
long long impartireLaNatural(int a[], long long x, int c[]) {
long long rest = 0;
int len = a[0];
for (int i = len; i >= 1; i--) {
rest = rest * 10 + a[i];
c[i] = rest / x;
rest = rest % x;
}
c[0] = len;
// elimin zero-urile din față
while (c[0] > 1 && c[c[0]] == 0) {
c[0] = c[0] - 1;
}
return rest;
}Complexitate: O(lenA). Fiecare
cifră e procesată o singură dată.
Atenție la long long:
rest * 10 + a[i] - dacă x ≤ 10⁹,
atunci rest < x ≤ 10⁹ și
rest * 10 < 10¹⁰ - depășește int,
dar încape în long long. Folosește
long long pentru siguranță.
Exemplu complet: citesc A și x, afișez câtul și restul
#include <fstream>
#include <cstring>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");
const int NMAX = 1005;
int a[NMAX], c[NMAX];
char s[NMAX];
void citire(int v[]) {
fin >> s;
int len = strlen(s);
v[0] = len;
for (int i = 1; i <= len; i++) {
v[i] = s[len - i] - '0';
}
}
void afisare(int v[]) {
for (int i = v[0]; i >= 1; i--) {
fout << v[i];
}
fout << "\n";
}
long long impartireLaNatural(int a[], long long x, int c[]) {
long long rest = 0;
int len = a[0];
for (int i = len; i >= 1; i--) {
rest = rest * 10 + a[i];
c[i] = rest / x;
rest = rest % x;
}
c[0] = len;
while (c[0] > 1 && c[c[0]] == 0) {
c[0] = c[0] - 1;
}
return rest;
}
int main() {
long long x;
citire(a);
fin >> x;
long long rest = impartireLaNatural(a, x, c);
afisare(c);
fout << rest;
return 0;
}date.in:
1234
7
date.out:
176
2
Aplicație 1: verificarea divizibilității
Numărul
A(cu 500 cifre) e divizibil cux?
Răspuns: da ⇔ restul împărțirii e 0.
long long rest = impartireLaNatural(a, x, c);
if (rest == 0) {
fout << "DA";
} else {
fout << "NU";
}Alternativă mai rapidă (dacă nu vrei câtul): calculezi doar restul, nu stochezi și câtul:
long long restModulo(int a[], long long x) {
long long rest = 0;
for (int i = a[0]; i >= 1; i--) {
rest = (rest * 10 + a[i]) % x;
}
return rest;
}Mai compact, cu un singur pas de calcul per cifră. Foarte
util pentru „calculează A mod x” când
A e număr mare.
Aplicație 2:
A mod x fără a stoca câtul
Problema: „dă-mi A mod 10^9+7” unde
A e un număr cu 1000 de cifre.
const long long MOD = 1000000007LL;
long long restModulo(int a[], long long x) {
long long rest = 0;
for (int i = a[0]; i >= 1; i--) {
rest = (rest * 10 + a[i]) % x;
}
return rest;
}
// ...
fout << restModulo(a, MOD);Acum rest-ul încape mereu în long long și ieși
cu rezultat corect modulo prim.
Aplicație 3: împărțire la 10 = „scoate ultima cifră”
Scoate cifra unităților dintr-un număr mare.
Împărțire la 10: câtul e numărul fără ultima cifră, restul e cifra scoasă. Împărțirea noastră funcționează. Dar există o variantă mai rapidă - shift:
// a = a / 10 (fără rest)
void shiftDreapta(int a[]) {
for (int i = 1; i < a[0]; i++) {
a[i] = a[i + 1];
}
a[a[0]] = 0;
if (a[0] > 1) {
a[0] = a[0] - 1;
}
}Complexitate O(lenA), la fel. Dar evită calculul
de împărțire propriu-zis - operație foarte rapidă.
Aplicație 4: conversie zecimală → altă bază
Convertește un număr mare din baza 10 în baza
B.
Ideea: repetat, împart la B și restul e
următoarea cifră în baza B (începând cu cea mai
puțin semnificativă):
int a[NMAX], cat[NMAX];
int cifreBaza[NMAX]; // cifrele în baza B
int nr = 0;
while (a[0] > 1 || a[1] > 0) { // cât timp a > 0
long long rest = impartireLaNatural(a, B, cat);
nr = nr + 1;
cifreBaza[nr] = rest;
// copiez cat înapoi în a
for (int i = 0; i <= cat[0]; i++) {
a[i] = cat[i];
}
}
// afișez invers
for (int i = nr; i >= 1; i--) {
fout << cifreBaza[i];
}Acesta e modul clasic de a converti un număr mare din baza 10 în orice altă bază. Vezi lecția de sisteme de numerație pentru detalii.
Greșeli frecvente
1. Parcurgere în ordine greșită
Împărțirea pornește de la cifra cea mai
semnificativă (deci de la i = a[0]
descrescător). Dacă parcurgi i = 1 la
a[0], obții un rezultat complet greșit.
2. Nu elimini zero-urile din față la cât
Dacă 1234 / 7 = 176 dar reprezentezi ca
0176, afișarea iese greșit sau comparațiile
ulterioare sunt confuze. Mereu:
while (c[0] > 1 && c[c[0]] == 0) c[0]--;3. Overflow la
rest * 10
Dacă x ≤ 10⁹ și rest < x, atunci
rest * 10 + a[i] ≤ 10¹⁰ -
depășește int. Folosește
long long pentru rest.
4. Împărțire la 0
x = 0 → diviziune prin zero → program crapă.
Verifică în cod dacă e cazul:
if (x == 0) {
fout << "diviziune prin zero";
return 0;
}5. Rezultatul câtului e
0
Dacă A < x, câtul e 0 și restul e chiar
A. Codul nostru gestionează corect:
c[0] = lenAla început- Toate
c[i]sunt 0 după împărțire - Bucla de eliminare zero-uri coboară
c[0]la 1 (păstrăm lungimea minimă de 1) - Afișează
0
Ce să reții
- Împărțire lungă: pornești de la cifra cea mai
semnificativă (
i = a[0]descrescător). - Pas:
rest = rest * 10 + a[i]; c[i] = rest / x; rest = rest % x; - Returnezi restul final.
- Elimină zero-urile din față la cât, cu
while (c[0] > 1 && c[c[0]] == 0) c[0]--. - Complexitate: O(lenA).
- Pentru „doar mod x”, poți omite stocarea câtului - mai rapid și mai simplu.
- Aplicații: divizibilitate, modulo numere mari, conversie în altă bază.
- Folosește
long longpentrurest- depășeșteintușor.
Cu această lecție, ai toate operațiile de bază pe numere mari: adunare, scădere, înmulțire cu natural, împărțire cu rest la natural. Înmulțirea a două numere mari e o operație avansată (algoritmi O(L²) naiv, O(L log L) cu FFT) - la olimpiade de liceu nu e cerută direct decât la Lot.