¿Maneja números extremadamente grandes en un idioma que no puede?

15

Estoy tratando de pensar en cómo haría los cálculos en números extremadamente grandes (hasta el infinito - no hay números flotantes) si la construcción del lenguaje es incapaz de manejar números mayores que un cierto valor.

Estoy seguro de que no soy el primero ni el último en hacer esta pregunta, pero los términos de búsqueda que estoy usando no me están dando un algoritmo para manejar esas situaciones. Más bien, la mayoría de las sugerencias ofrecen un cambio de idioma o un cambio variable, o hablan de cosas que parecen irrelevantes para mi búsqueda. Entonces necesito un poco de orientación.

Dibujaría un algoritmo como este:

  1. Determine la longitud máxima de la variable entera para el idioma.

  2. Si un número tiene más de la mitad de la longitud máxima de la variable, se divide en una matriz. (dar una pequeña sala de juegos)

  3. Orden de matriz [0] = los números más a la derecha [n-max] = los números más a la izquierda

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

Como establecí la mitad de la longitud de la variable como el punto de marca, puedo calcular las unidades, décimas, centésimas, etc. Colocar a través de la marca de la mitad, de modo que si una longitud máxima variable fuera de 10 dígitos de 0 a 9999999999, sé que al dividir eso a cinco dígitos me da un poco de sala de juegos

Entonces, si sumo o multiplico, puedo tener una función de verificación variable que vea que el sexto dígito (desde la derecha) de la matriz [0] es el mismo lugar que el primer dígito (desde la derecha) de la matriz [1].

Dividir y restar tiene sus propios problemas que aún no he pensado.

Me gustaría saber acerca de las mejores implementaciones para soportar números más grandes que el programa.

Malva
fuente
1
Lo primero que viene a la mente es BigInteger de Java: docs.oracle.com/javase/6/docs/api/java/math/BigInteger.html
Ivan
¿Es este un lenguaje que cualquiera aquí podría conocer y poder hacer recomendaciones específicas, o es algo oscuro y patentado?
FrustratedWithFormsDesigner
No sé para qué idioma me gustaría esto todavía. Sé más sobre php pero no quiero hacerlo en ese idioma. Lisp es atractivo ya que, por lo que he leído, no tiene límites de longitud. Sin embargo, mi defecto personal de querer saber cómo funciona me hace querer tener la libertad de hacerlo en qbasic si estuviera atrapado en una isla. (También es divertido, siempre estoy pensando en calcular números grandes y algunas calculadoras en línea son demasiado engorrosas para la tarea.)
Mallow

Respuestas:

25

Está buscando una biblioteca de aritmética de precisión arbitraria (también llamada "precisión múltiple" o "gran número") para el idioma con el que está trabajando. Por ejemplo, si está trabajando con C, puede usar la Biblioteca GNU Bignum -> http://gmplib.org/

Si desea comprender cómo funciona, también puede escribir su propia biblioteca de números grandes y usarla. La forma más sencilla de manejar esto es con matrices, donde cada elemento es un dígito del número con el que está trabajando. Después de eso, debe implementar todas las funciones para sumar, restar, multiplicar, dividir, exponer y así sucesivamente.

Daniel Scocco
fuente
55
por supuesto, "dígito" es relativo, muchas bibliotecas bignum usan dígitos en la base 256 (byte sin signo []) a 2 ^ 64 (sin signo largo [])
monstruo de trinquete
1
Solo asegurándome de que entendí, 'dígito' podría ser una matriz de 256 dígitos base. Creo que podría estar equivocado, hacer matemáticas en la base 256 es más fácil que en la base 88, pero sigue siendo una tarea ... (Al menos cuando lo hice anoche en una hoja de papel jaja)
Mallow
1
"hacer matemáticas en la base 256 es más fácil que en la base 88". Falso. Son igualmente fáciles.
S.Lott
2
@ S.Lott: um ... cuando tienes operaciones bit a bit disponibles, hacer matemáticas en la base 256 es definitivamente más fácil que en la base 88.
Jason S
1
Construir su propia suma / resta / multiplicación es bastante sencillo. Sin embargo, la división es difícil, a menos que la esté implementando en binario, donde se convierte en un ejercicio de sustracción condicional y desplazamiento de bits.
Jason S
13

Es un problema bien conocido: aritmética de precisión arbitraria

Cuando el idioma que está utilizando no resuelve este problema, primero intente encontrar una biblioteca de terceros que lo haga. Si no lo encuentra o tiene curiosidad, intente implementarlo; El artículo de Wikipedia tiene buenas referencias a implementaciones clásicas.

jgomo3
fuente
6

Cuando se trata de grandes números, probablemente una de las decisiones de diseño más fundamentales es cómo voy a representar el gran número.

¿Será una cadena, una matriz, una lista o una clase de almacenamiento personalizada (local).

Después de tomar esa decisión, las operaciones matemáticas reales pueden desglosarse en partes más pequeñas y luego ejecutarse con tipos de idioma nativo como int o integer.

He incluido un muy rudimentario ejemplo de ADICIÓN en C # .Net que almacena el gran número resultante como una cadena. Los "números" entrantes también son cadenas, por lo que uno debería poder enviar números muy "grandes". Tenga en cuenta que el ejemplo es solo para números enteros para que sea simple.

Incluso con cadenas hay un límite en el número de caracteres o "números" en el número, como se indica aquí:

¿Cuál es la longitud máxima posible de una cadena .NET?

Pero podría agregar algunos números realmente grandes, mucho más allá de los tipos nativos int32 o int64 para .Net.

De todos modos, aquí hay una implementación de almacenamiento de cadenas.

/// <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 eso te dé algunas ideas sobre tu propia implementación. Tenga en cuenta que el código de muestra probablemente no esté optimizado ni nada de eso. Está destinado a dar algunas ideas de cómo podría hacerse.

Jon Raynor
fuente
¡¡Frio!! Gracias, ayuda a aclarar las cosas con los restos que habría complicado demasiado.
Mallow
-2

Este funciona más rápido para mí:

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
fuente
2
Este sitio trata sobre preguntas conceptuales y se espera que las respuestas expliquen las cosas. Lanzar volcados de código en lugar de una explicación es como copiar código del IDE a la pizarra: puede parecer familiar e incluso a veces comprensible, pero se siente extraño ... simplemente extraño. La pizarra no tiene compilador
mosquito
Este sitio es INTERCAMBIO, por lo que tratamos de ayudarnos lo más que podamos. Tal vez debería hacer tales comentarios en StackOverflow, pero intenté ayudar con mi enfoque. Si alguien todavía necesita una explicación, la pedirá. Hago aquí una suma matemática estándar dígito a dígito, comenzando desde el final.
Andranik Sargsyan
De acuerdo con mosquito. Al menos explique el algoritmo antes de descargar el código.
Frank Hileman
Simplemente descargar el código sin explicación es como alentar el copy-past-and-go. No veo cómo eso podría ser útil en SoftwareEngineering. Quizás en StackOverflow lo sea, pero ambos sitios tienen propósitos totalmente diferentes.
Laiv