Gérer de très grands nombres dans une langue qui ne le peut pas?

15

J'essaie de réfléchir à la façon dont je procéderais pour faire des calculs sur des nombres extrêmement grands (à l'infini - intergers sans flottants) si la construction du langage est incapable de gérer des nombres supérieurs à une certaine valeur.

Je suis sûr que je ne suis ni le premier ni le dernier à poser cette question, mais les termes de recherche que j'utilise ne me donnent pas d'algorithme pour gérer ces situations. La plupart des suggestions proposent plutôt un changement de langue ou un changement de variable, ou parlent de choses qui semblent non pertinentes pour ma recherche. J'ai donc besoin d'un peu d'orientation.

Je dessinerais un algorithme comme celui-ci:

  1. Déterminez la longueur maximale de la variable entière pour la langue.

  2. Si un nombre est plus de la moitié de la longueur de la longueur maximale de la variable, divisez-le dans un tableau. (donnez une petite salle de jeux)

  3. Ordre des tableaux [0] = les nombres les plus à droite [n-max] = les nombres les plus à gauche

    Ex. Num: 29392023 Array [0]: 23, Array [1]: 20, array [2]: 39, array [3]: 29

Puisque j'ai établi la moitié de la longueur de la variable comme point de repère, je peux alors calculer les unités, les dixièmes, les centièmes, etc. Placer via la marque à mi-chemin de sorte que si une longueur maximale variable était de 10 chiffres de 0 à 9999999999, alors je sais que en divisant cela par cinq chiffres, donnez-moi une salle de jeu.

Donc, si j'ajoute ou multiplie, je peux avoir une fonction de vérificateur de variables qui voit que le sixième chiffre (à droite) du tableau [0] est au même endroit que le premier chiffre (à droite) du tableau [1].

La division et la soustraction ont leurs propres problèmes auxquels je n'ai pas encore pensé.

J'aimerais connaître les meilleures implémentations de prise en charge d'un plus grand nombre que le programme.

Mauve
la source
1
La première chose qui me vient à l'esprit est le BigInteger de Java: docs.oracle.com/javase/6/docs/api/java/math/BigInteger.html
Ivan
Est-ce un langage que quiconque ici pourrait connaître et être capable de faire des recommandations spécifiques, ou est-ce quelque chose d'obscur et de propriétaire?
FrustratedWithFormsDesigner
Je ne sais pas encore pour quelle langue je voudrais cela. Je connais le PHP le plus mais je ne veux pas le faire dans cette langue. Lisp est attrayant car d'après ce que j'ai lu, il n'a pas de limites de longueur. Cependant, mon défaut personnel de vouloir savoir comment cela fonctionne me donne envie d'avoir la liberté de le faire dans qbasic si j'étais coincé sur une île. (C'est aussi pour le plaisir, je pense toujours à calculer de grands nombres et certaines calculatrices en ligne sont trop lourdes pour la tâche.)
Mallow

Réponses:

25

Vous recherchez une bibliothèque d'arithmétique de précision arbitraire (également appelée "précision multiple" ou "grand nombre") pour la langue avec laquelle vous travaillez. Par exemple, si vous travaillez avec C, vous pouvez utiliser la bibliothèque GNU Bignum -> http://gmplib.org/

Si vous voulez comprendre comment cela fonctionne, vous pouvez également écrire votre propre bibliothèque big num et l'utiliser. La façon la plus simple de gérer cela est avec les tableaux, où chaque élément est un chiffre du nombre avec lequel vous travaillez. Après cela, vous devez implémenter toutes les fonctions pour ajouter, soustraire, multiplier, diviser, exposer, etc.

Daniel Scocco
la source
5
bien sûr, "digit" est relatif, de nombreuses bibliothèques bignum utilisent des chiffres en base 256 (octet non signé []) à 2 ^ 64 (unsigned long [])
ratchet freak
1
Juste pour être sûr d'avoir bien compris, 'digit' pourrait être un tableau de 256 chiffres de base? Je pense que je peux me tromper, faire des calculs sur la base 256 est plus facile que la base 88 mais c'est quand même une tâche assez difficile ... (Du moins quand je l'ai fait hier soir sur une feuille de papier haha)
Mallow
1
"il est plus facile de faire des calculs sur la base 256 que sur la base 88". Faux. Ils sont tout aussi faciles.
S.Lott
2
@ S.Lott: euh ... quand vous avez des opérations au niveau du bit disponibles, faire des calculs sur la base 256 est définitivement plus facile que la base 88.
Jason S
1
Construire votre propre addition / soustraction / multiplication est assez simple. La division est cependant difficile, sauf si vous l'implémentez en binaire, où elle devient un exercice de soustraction conditionnelle et de décalage de bits.
Jason S
13

C'est un problème bien connu: arithmétique de précision arbitraire

Lorsque la langue que vous utilisez ne résout pas ce problème, essayez d'abord de trouver une bibliothèque tierce qui le fait. Si vous ne le trouvez pas ou si vous êtes curieux, essayez de l'implémenter; l'article de wikipedia contient de bonnes références aux implémentations classiques.

jgomo3
la source
6

Lorsqu'il s'agit de grands nombres, l'une des décisions de conception les plus fondamentales est probablement de savoir comment vais-je représenter le grand nombre?

Sera-ce une chaîne, un tableau, une liste ou une classe de stockage personnalisée (homegrown)?

Une fois cette décision prise, les opérations mathématiques réelles peuvent être décomposées en parties plus petites, puis exécutées avec des types de langue natifs tels que int ou entier.

J'ai inclus un exemple ADDITION très rudimentaire dans C # .Net qui stocke le grand nombre résultant sous forme de chaîne. Les "numéros" entrants sont également des chaînes, donc on devrait pouvoir envoyer de très "grands" nombres. Gardez à l'esprit que l'exemple est uniquement pour les nombres entiers pour rester simple.

Même avec des chaînes, il y a une limite dans le nombre de caractères ou "nombres" dans le nombre, comme indiqué ici:

Quelle est la longueur maximale possible d'une chaîne .NET?

Mais vous pouvez ajouter de très gros nombres, bien au-delà des types natifs int32 ou int64 pour .Net.

Quoi qu'il en soit, voici une implémentation de stockage de chaînes.

/// <summary>
/// Adds two "integers".  The integers can be of any size string.
/// </summary>
/// <param name="BigInt1">The first integer</param>
/// <param name="BigInt2">The second integer</param>
/// <returns>A string that is the addition of the two integers passed.</returns>
/// <exception cref="Exception">Can throw an exception when parsing the individual parts     of the number.  Callers should handle. </exception>
public string AddBigInts(string BigInt1, string BigInt2)
{
    string result = string.Empty;

    //Make the strings the same length, pre-pad the shorter one with zeros
    int length = (BigInt1.Length > BigInt2.Length ? BigInt1.Length : BigInt2.Length);
    BigInt1 = BigInt1.PadLeft(length, '0');
    BigInt2 = BigInt2.PadLeft(length, '0');

    int remainder = 0;

    //Now add them up going from right to left
    for (int i = (BigInt1.Length - 1); i >= 0; i--)
    {
        //If we don't encounter a number, this will throw an exception as indicated.
        int int1 = int.Parse(BigInt1[i].ToString());
        int int2 = int.Parse(BigInt2[i].ToString());

        //Add
        int add = int1 + int2 + remainder;

        //Check to see if we need a remainder;
        if (add >= 10)
        {
            remainder = 1;
            add = add % 10;
        }
        else
        {
            remainder = 0;
        }

        //Add this to our "number"
        result = add.ToString() + result;
    }

    //Handle when we have a remainder left over at the end
    if (remainder == 1)
    {
        result = remainder + result;
    }

    return result;
}

J'espère que cela vous donne quelques idées sur votre propre mise en œuvre. Veuillez noter que l'exemple de code n'est probablement pas optimisé ou quelque chose comme ça. Il vise à donner quelques idées sur la façon dont cela pourrait être fait.

Jon Raynor
la source
Cool!! Merci, cela aide à clarifier les choses avec des restes que j'aurais trop compliqués.
Mallow
-2

Celui-ci fonctionne plus vite pour moi:

static string Add(string a, string b)
        {
            string c = null;

            if (Compare(a, b) < 0)
            {
                c = a;
                a = b;
                b = c;
            }

            StringBuilder sb = new StringBuilder();

            b = b.PadLeft(a.Length, '0');

            int r = 0;

            for (int i = a.Length - 1; i >= 0; i--)
            {
                int part = a[i] + b[i] + r - 96;

                if (part <= 9)
                {
                    sb.Insert(0, part);

                    r = 0;
                }
                else
                {
                    sb.Insert(0, part - 10);

                    r = 1;
                }
            }

            if (r == 1)
            {
                sb.Insert(0, "1");
            }

            return sb.ToString();
        }
Andranik Sargsyan
la source
2
Ce site est sur les questions conceptuelles et les réponses sont censées expliquer les choses. Lancer des vidages de code au lieu d'explications revient à copier du code de l'IDE vers le tableau blanc: cela peut sembler familier et même parfois compréhensible, mais cela semble bizarre ... juste bizarre. Le tableau blanc n'a pas de compilateur
moucher
Ce site est un échange, nous essayons donc de nous entraider autant que possible. Je devrais peut-être faire de tels commentaires dans StackOverflow, mais j'ai essayé de m'aider avec mon approche. Si quelqu'un a encore besoin d'une explication, il la demandera. Je fais ici un ajout mathématique standard chiffre par chiffre, en commençant par la fin.
Andranik Sargsyan
D'accord avec moucheron. Expliquez au moins l'algorithme avant de vider le code.
Frank Hileman
Vider simplement du code sans explication revient à encourager le copier-coller. Je ne vois pas comment cela pourrait être utile en SoftwareEngineering. Peut-être que StackOverflow l'est, mais les deux sites ont en fait des objectifs totalement différents.
Laiv