Quand j'ai vu cette question, j'ai l'intention de la résoudre, mais à ce moment j'étais très occupé. La fin de semaine dernière, j'ai pu gagner quelques heures de temps libre, alors j'ai considéré mon défi en attente.
Tout d'abord, je vous suggère de considérer la réponse ci-dessus. Je n'utilise jamais de bibliothèque GMP mais je suis sûr que c'est une meilleure solution qu'un code fait à la main. En outre, vous pourriez être intéressé à analyser le code de la calculatrice bc; ça peut marcher avec de gros nombres et j'ai l'habitude de tester mon propre code.
Ok, si vous êtes toujours intéressé par un code, faites-le vous-même (uniquement avec le langage de support C et la bibliothèque Standard C), je peux vous donner quelque chose.
Avant tout, un peu de théorie. Dans la théorie numérique de base (niveau arithmétique modulaire) theres est un algorithme qui m'inspire pour arriver à une solution; Multiplier and Power algorithme pour résoudre un ^N Module m:
Result := 1;
for i := k until i = 0
if n_i = 1 then Result := (Result * a) mod m;
if i != 0 then Result := (Result * Result) mod m;
end for;
où k est le nombre de chiffres moins l'un des N en représentation binaire, et n_i est i chiffre binaire.Par exemple (N est exposant):
N = 44 -> 1 0 1 1 0 0
k = 5
n_5 = 1
n_4 = 0
n_3 = 1
n_2 = 1
n_1 = 0
n_0 = 0
Lorsque nous faisons une opération de module, comme une division entière, nous pouvons perdre une partie du nombre, donc nous ne disposons que de modifier l'algorithme pour ne pas manquer les données pertinentes.
Voici mon code (attention à ce que ce soit un code adhoc, une forte dépendance de l'arc informatique.) Je joue avec la longueur de données du langage C, donc attention car mes données ne peuvent pas être identiques:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
enum { SHF = 31, BMASK = 0x1 << SHF, MODULE = 1000000000UL, LIMIT = 1024 };
unsigned int scaleBigNum(const unsigned short scale, const unsigned int lim, unsigned int *num);
unsigned int pow2BigNum(const unsigned int lim, unsigned int *nsrc, unsigned int *ndst);
unsigned int addBigNum(const unsigned int lim1, unsigned int *num1, const unsigned int lim2, unsigned int *num2);
unsigned int bigNum(const unsigned short int base, const unsigned int exp, unsigned int **num);
int main(void)
{
unsigned int *num, lim;
unsigned int *np, nplim;
int i, j;
for(i = 1; i < LIMIT; ++i)
{
lim = bigNum(i, i, &num);
printf("%i^%i == ", i, i);
for(j = lim - 1; j > -1; --j)
printf("%09u", num[j]);
printf("\n");
free(num);
}
return 0;
}
/*
bigNum: Compute number base^exp and store it in num array
@base: Base number
@exp: Exponent number
@num: Pointer to array where it stores big number
Return: Array length of result number
*/
unsigned int bigNum(const unsigned short int base, const unsigned int exp, unsigned int **num)
{
unsigned int m, lim, mem;
unsigned int *v, *w, *k;
//Note: mem has the exactly amount memory to allocate (dinamic memory version)
mem = ((unsigned int) (exp * log10((float) base)/9)) + 3;
v = (unsigned int *) malloc(mem * sizeof(unsigned int));
w = (unsigned int *) malloc(mem * sizeof(unsigned int));
for(m = BMASK; ((m & exp) == 0) && m; m >>= 1) ;
v[0] = (m) ? 1 : 0;
for(lim = 1; m > 1; m >>= 1)
{
if(exp & m)
lim = scaleBigNum(base, lim, v);
lim = pow2BigNum(lim, v, w);
k = v;
v = w;
w = k;
}
if(exp & 0x1)
lim = scaleBigNum(base, lim, v);
free(w);
*num = v;
return lim;
}
/*
scaleBigNum: Make an (num[] <- scale*num[]) big number operation
@scale: Scalar that multiply big number
@lim: Length of source big number
@num: Source big number (array of unsigned int). Update it with new big number value
Return: Array length of operation result
Warning: This method can write in an incorrect position if we don't previous reallocate num (if it's necessary). bigNum method do it for us
*/
unsigned int scaleBigNum(const unsigned short scale, const unsigned int lim, unsigned int *num)
{
unsigned int i;
unsigned long long int n, t;
for(n = 0, t = 0, i = 0; i < lim; ++i)
{
t = (n/MODULE);
n = ((unsigned long long int) scale * num[i] );
num[i] = (n % MODULE) + t; // (n % MODULE) + t always will be smaller than MODULE
}
num[i] = (n/MODULE);
return ((num[i]) ? lim + 1 : lim);
}
/*
pow2BigNum: Make a (dst[] <- src[] * src[]) big number operation
@lim: Length of source big number
@src: Source big number (array of unsigned int)
@dst: Destination big number (array of unsigned int)
Return: Array length of operation result
Warning: This method can write in an incorrect position if we don't previous reallocate num (if it's necessary). bigNum method do it for us
*/
unsigned int pow2BigNum(const unsigned int lim, unsigned int *src, unsigned int *dst)
{
unsigned int i, j;
unsigned long long int n, t;
unsigned int k, c;
for(c = 0, dst[0] = 0, i = 0; i < lim; ++i)
{
for(j = i, n = 0; j < lim; ++j)
{
n = ((unsigned long long int) src[i] * src[j]);
k = i + j;
if(i != j)
{
t = 2 * (n % MODULE);
n = 2 * (n/MODULE);
// (i + j)
dst[k] = ((k > c) ? ((c = k), 0) : dst[k]) + (t % MODULE);
++k; // (i + j + 1)
dst[k] = ((k > c) ? ((c = k), 0) : dst[k]) + ((t/MODULE) + (n % MODULE));
++k; // (i + j + 2)
dst[k] = ((k > c) ? ((c = k), 0) : dst[k]) + (n/MODULE);
}
else
{
dst[k] = ((k > c) ? ((c = k), 0) : dst[k]) + (n % MODULE);
++k; // (i + j)
dst[k] = ((k > c) ? ((c = k), 0) : dst[k]) + (n/MODULE);
}
for(k = i + j; k < (lim + j); ++k)
{
dst[k + 1] += (dst[k]/MODULE);
dst[k] %= MODULE;
}
}
}
i = lim << 1;
return ((dst[i - 1]) ? i : i - 1);
}
/*
addBigNum: Make a (num2[] <- num1[] + num2[]) big number operation
@lim1: Length of source num1 big number
@num1: First source operand big number (array of unsigned int). Should be smaller than second
@lim2: Length of source num2 big number
@num2: Second source operand big number (array of unsigned int). Should be equal or greater than first
Return: Array length of operation result or 0 if num1[] > num2[] (dosen't do any op)
Warning: This method can write in an incorrect position if we don't previous reallocate num2
*/
unsigned int addBigNum(const unsigned int lim1, unsigned int *num1, const unsigned int lim2, unsigned int *num2)
{
unsigned long long int n;
unsigned int i;
if(lim1 > lim2)
return 0;
for(num2[lim2] = 0, n = 0, i = 0; i < lim1; ++i)
{
n = num2[i] + num1[i] + (n/MODULE);
num2[i] = n % MODULE;
}
for(n /= MODULE; n; ++i)
{
num2[i] += n;
n = (num2[i]/MODULE);
}
return (lim2 > i) ? lim2 : i;
}
pour compiler:
gcc -o bgn <name>.c -Wall -O3 -lm //Math library if you wants to use log func
pour vérifier le résultat, utilisez la sortie directe et entrée bc. script shell simple:
#!/bin/bash
select S in ` awk -F '==' '{print $1 " == " $2 }' | bc`;
do
0;
done;
echo "Test Finished!";
Nous avons et tableau de unsigned int (4 octets) où nous à chaque int de tableau un certain nombre de 9 chiffres (% 1000000000UL); donc num [0] nous aurons les 9 premiers chiffres, num [1] nous aurons les chiffres 10 à 18, num [2] ... J'utilise la mémoire convencional pour travailler mais une amélioration peut le faire avec la mémoire dinamique. Ok, mais quelle longueur ça pourrait être le tableau? (ou combien de mémoire devons-nous allouer?). L'utilisation, nous pouvons déterminer le nombre de chiffres calculatrice bc (bc -l avec mathlib) a un numéro:
l(a^N)/l(10) // Natural logarith to Logarithm base 10
Si nous savons chiffres, nous savons entiers quantité dont nous avions besoin:
(l(a^N)/(9 * l(10))) + 1 // Truncate result
Si vous travaillez avec valeur telle que (2^k)^N on peut le résoudre logarithme de cette expression:
(k*N*l(2)/(9*l(10))) + 1 // Truncate result
pour déterminer exactement la longueur du réseau entier. Exemple:
256^800 = 2^(8*800) ---> l(2^(8*800))/(9*l(10)) + 1 = 8*800*l(2)/(9*l(10)) + 1
La valeur 1000000000UL (10^9) est très important constante. Une constante comme 10000000000UL (10^10) ne fonctionne pas parce que peut produire et débordement indétecté (essayez ce qui se passe avec le nombre 16^16 et 10^10 constante) et une constante plus petit comme 1000000000UL (10^8) sont corrects mais nous devons réserver plus de mémoire et faire plus de pas. 10^9 est la constante clé pour un entier non signé de 32 bits et un entier long non signé de 64 bits.
Le code a deux parties, Multiplier (facile) et Puissance par 2 (plus dur). Multiplier est juste une multiplication et une échelle et propager le débordement d'entier. Il faut le principe de la propriété associative en math pour faire exactement le principe inverse, donc si k (A + B + C) on veut kA + kB + kC où le nombre sera k * A * 10^18 + k * B * 10^9 + k C. Obiously, k L'opération C peut générer un nombre supérieur à 999 999 999, mais jamais plus grand que 0xFF FF FF FF FF FF FF. Un nombre supérieur à 64 bits ne peut jamais apparaître dans une multiplication car C est un nombre entier non signé de 32 bits et k est un nombre entier non signé de 16 bits. Dans Worts cas, nous aurons ce numéro:
k = 0x FF FF;
C = 0x 3B 9A C9 FF; // 999999999
n = k*C = 0x 3B 9A | 8E 64 36 01;
n % 1000000000 = 0x 3B 99 CA 01;
n/1000000000 = 0x FF FE;
Après Mul k B, nous devons ajouter 0x FF FE de la dernière multiplication des C (B = k B + (C/module)), etc. sur (nous avons 18 bits de décalage arithmétique, assez pour garantir des valeurs correctes).
Power est plus complexe, mais est en essencial, le même problème (multiplication et ajoutez), donc je donne quelques astuces sur le pouvoir de code:
- types de données sont importantes, très importantes
- Si vous essayez Pour multiplier un entier non signé avec un entier non signé, vous obtenez un autre entier non signé. Utilisez un cast explicite pour obtenir un long long non signé et ne perdez pas de données.
- Toujours utiliser un modificateur non signé, ne l'oubliez pas!
- Alimentation par 2 peut directement modifier l'index 2 avant l'indice actuel
- gdb est votre ami
J'ai développé une autre méthode qui ajoutent de grands nombres. Ces derniers je ne prouve pas tellement mais je pense que cela fonctionne bien. Ne sois pas cruel avec moi s'il y a un bug.
... et c'est tout!
PD1: Développé dans un
Intel(R) Pentium(R) 4 CPU 1.70GHz
Data length:
unsigned short: 2
unsigned int: 4
unsigned long int: 4
unsigned long long int: 8
nombres tels que 256^1024 il passe:
real 0m0.059s
user 0m0.033s
sys 0m0.000s
Un bucle qui Calculons i^i i allant i = 1 ... 1024 :
real 0m40.716s
user 0m14.952s
sys 0m0.067s
Pour les nombres tels que 65355^65355, le temps passé est fou. PD2: Ma réponse est si tardive mais j'espère que mon code sera utile.
PD3: Désolé, expliquez-moi en anglais est l'un de mes pires handicaps!
Dernière mise à jour: Je viens eu une idée avec la même algorithme, mais d'autres la mise en œuvre, améliorer la qualité et réduire la mémoire de quantité à utiliser (on peut utiliser les bits de complètement unsigned int). Le secret: n^2 = n * n = n * (n - 1 + 1) = n * (n - 1) + n. (Je ne ferai pas ce nouveau code, mais si quelqu'un est intéressé, peut être après les examens ...)
Je vais jouer le rôle de défenseur de Devil et vous demander pourquoi vous avez besoin d'un format de base 10 pour imprimer ces énormes nombres? Si c'est parce que les humains ont besoin de les lire, alors comment les humains vont-ils comprendre des nombres aussi grands (comparer, lire)? Si ce n'est pas pour les humains, alors pourquoi utiliser la base 10 du tout? –
Le mélange de texte formaté et de sortie binaire n'est généralement pas une bonne idée, donc si vous avez besoin de stocker le nombre exact dans un fichier que vous utilisez déjà pour le texte, cela pourrait devenir un problème. –
@Greg Je ne suggérais pas d'écrire un objet binaire dans un fichier, mais de changer la façon dont le nombre est rendu au texte. Les gens lisent heureusement les encodages hexadécimaux des nombres. –