Lidar com números extremamente grandes em um idioma que não pode?


15

Estou tentando pensar em como eu faria cálculos em números extremamente grandes (até o infinito - não interfere em flutuações) se a construção da linguagem for incapaz de lidar com números maiores que um determinado valor.

Tenho certeza de que não sou o primeiro nem o último a fazer essa pergunta, mas os termos de pesquisa que estou usando não estão me dando um algoritmo para lidar com essas situações. Em vez disso, a maioria das sugestões oferece uma mudança de idioma ou variável, ou fala sobre coisas que parecem irrelevantes para minha pesquisa. Então, eu preciso de um pouco de orientação.

Eu esboçaria um algoritmo como este:

  1. Determine o comprimento máximo da variável inteira para o idioma.

  2. Se um número tiver mais da metade do comprimento máximo da variável, divida-o em uma matriz. (dê uma pequena sala de jogos)

  3. Ordem da matriz [0] = os números mais à direita [n-max] = números mais à esquerda

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

Como eu estabeleci metade do comprimento da variável como ponto de marcação, então eu posso calcular os décimos, centésimos, etc. pela metade desses cinco dígitos me dá um pouco de espaço para brincar.

Portanto, se eu adicionar ou multiplicar, posso ter uma função de verificador variável que veja que o sexto dígito (à direita) da matriz [0] é o mesmo local que o primeiro dígito (à direita) da matriz [1].

Dividir e subtrair têm seus próprios problemas nos quais ainda não pensei.

Gostaria de saber sobre as melhores implementações de suporte a números maiores do que o programa pode.


1
A primeira coisa que vem à mente é de Java BigInteger: docs.oracle.com/javase/6/docs/api/java/math/BigInteger.html
Ivan

É um idioma que qualquer pessoa aqui possa conhecer e ser capaz de fazer recomendações específicas, ou é algo obscuro e proprietário?
FrustratedWithFormsDesigner

Ainda não sei para qual idioma eu gostaria disso. Eu sei php mais, mas eu não quero fazê-lo nesse idioma. O Lisp é atraente, pois pelo que li, ele não tem limites de comprimento. No entanto, minha falha pessoal de querer saber como funciona me faz querer ter a liberdade de fazê-lo em qbasic se eu estivesse preso em uma ilha. (É para se divertir também, eu estou sempre pensando sobre como calcular grandes números e algumas calculadoras online são demasiado pesado para a tarefa.)
Mallow

Respostas:


25

Você está procurando uma biblioteca aritmética de precisão arbitrária (também chamada de "precisão múltipla" ou "número grande") para o idioma em que está trabalhando. Por exemplo, se você estiver trabalhando com C, poderá usar a Biblioteca GNU Bignum -> http://gmplib.org/

Se você quiser entender como isso funciona, você também pode escrever sua própria grande biblioteca de números e usá-la. A maneira mais simples de lidar com isso é com matrizes, onde cada elemento é um dígito do número com o qual você está trabalhando. Depois disso, você precisa implementar todas as funções para adicionar, subtrair, multiplicar, dividir, exponenciar e assim por diante.


5
de curso "dígito" é relativo, muitas bibliotecas bignum usar dígitos na base de 256 (unsigned byte []) 2 ^ 64 (unsigned long [])
roquete aberração

1
Apenas certificando-me de que entendi, 'dígito' poderia ser uma matriz de 256 dígitos base? Eu acho que eu poderia estar enganado, fazendo matemática na base 256 é mais fácil do que base 88, mas ainda uma tarefa bastante ... (Pelo menos quando eu fiz isso na noite passada em uma folha de papel haha)
Mallow

1
"fazer contas na base 256 é mais fácil que na base 88". Falso. Eles são igualmente fáceis.
S.Lott

2
@ S.Lott: hum ... quando você tem operações bit a bit disponíveis, fazer contas na base 256 é definitivamente mais fácil do que na base 88.
Jason S

1
Construir sua própria adição / subtração / multiplicação é bastante simples. A divisão é difícil, a menos que você a implemente em binário, onde ela se torna um exercício de subtração condicional e mudança de bits.
Jason S

13

É um problema bem conhecido: aritmética de precisão arbitrária

Quando o idioma que você está usando não resolver esse problema, tente primeiro encontrar uma biblioteca de terceiros. Se você não o encontrar ou estiver curioso, tente implementá-lo; o artigo da wikipedia tem boas referências a implementações clássicas.


6

Ao lidar com grandes números, provavelmente uma das decisões mais fundamentais de design é como vou representar o grande número?

Será uma cadeia de caracteres, uma matriz, uma lista ou uma classe de armazenamento personalizada (doméstica).

Depois que essa decisão é tomada, as operações matemáticas reais podem ser divididas em partes menores e, em seguida, executadas com tipos de idioma nativo, como int ou número inteiro.

Incluí um exemplo de ADDITION muito rudimentar no C # .Net que armazena o número grande resultante como uma string. Os "números" de entrada também são cadeias de caracteres, portanto, é possível enviar números muito "grandes". Lembre-se de que o exemplo é apenas para números inteiros para simplificar.

Mesmo com cadeias, há um limite no número de caracteres ou "números" no número, conforme indicado aqui:

Qual é o tamanho máximo possível de uma string .NET?

Mas você pode adicionar alguns números realmente grandes, muito além dos tipos nativos int32 ou int64 para .Net.

Enfim, aqui está uma implementação de armazenamento de string.

/// <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;
}

Espero que isso lhe dê algumas idéias sobre sua própria implementação. Observe que o código de amostra provavelmente não está otimizado ou algo assim. Pretende dar algumas idéias de como isso poderia ser feito.


Legal!! Obrigado, ajuda a esclarecer as coisas com os restos que eu teria complicado um pouco mais.
Mallow

-2

Este funciona mais rápido para mim:

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();
        }

2
Este site trata de perguntas conceituais e espera-se que as respostas expliquem as coisas. Jogar dumps de código em vez de explicação é como copiar código do IDE para o quadro branco: pode parecer familiar e até às vezes compreensível, mas parece estranho ... apenas estranho. Whiteboard não tem compilador
mosquito

Como este site é TROCADO, tentamos ajudar um ao outro o máximo que podemos. Talvez eu deva fazer esses comentários no StackOverflow, mas tentei ajudar com minha abordagem. Se alguém ainda precisar de uma explicação, ele a solicitará. Faço aqui um dígito matemático de adição padrão por dígito, começando do final.
Andranik Sargsyan

Concordo com mosquito. Pelo menos explique o algoritmo antes de despejar o código.
Frank Hileman

Apenas despejar código sem explicação é como incentivar o copiar-e-ir-e-ir. Não vejo como isso poderia ser útil no SoftwareEngineering. Talvez no StackOverflow seja, mas ambos os sites têm propósitos totalmente diferentes.
Laiv
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.