2009-06-12 24 views
7

Necesito ser capaz de calcular (a^b)% c para valores muy grandes de ayb (que individualmente están presionando el límite y que causan errores de desbordamiento cuando intenta calcular un^segundo). Para números suficientemente pequeños, usar la identidad (a^b)% c = (a% c)^b% c funciona, pero si c es demasiado grande, esto realmente no ayuda. Escribí un bucle para hacer la operación mod manualmente, una a la vez un:Forma rápida de modificar manualmente un número

private static long no_Overflow_Mod(ulong num_base, ulong num_exponent, ulong mod) 
    { 
     long answer = 1; 
     for (int x = 0; x < num_exponent; x++) 
     { 
      answer = (answer * num_base) % mod; 
     } 
     return answer; 
    } 

pero esto lleva un tiempo muy largo. ¿Hay alguna manera simple y rápida de hacer esta operación sin tener que tomar un poder de b y sin usar bucles que consumen mucho tiempo? Si todo lo demás falla, puedo crear una matriz de bool para representar un tipo de datos enorme y descubrir cómo hacerlo con operadores bit a bit, pero tiene que haber una forma mejor.

+2

suena como un problema de Euler ... Si es así, se debe indicar claramente que en la pregunta en lugar de tratar de hacer trampa ... – Guffa

+0

Conocer el rango de a, b y c podría ayudarnos. – Nosredna

+0

Cheat? ......... –

Respuesta

1

A menos que escriba su propio fast modular exponentiation, la idea más simple que puedo idear es usar el tipo F # BigInt: Microsoft.FSharp.Math.Types.BigInt que admite operaciones a gran escala arbitrariamente, incluida la exponenciación y la aritmética modular.

Es un tipo incorporado que formará parte del completo .NET Framework con la próxima versión. No necesita usar F # para usar BitInt; puede usarlo directamente en C#.

1

Recomiendo revisar la documentación de Decimal y ver si cumple con sus requisitos, ya que es un tipo incorporado y puede usar el operador de mod. Si no, entonces necesitarás una biblioteca de precisión arbitraria como Bignum de Java.

+0

Sin usar Java. DO#. – Malfist

+0

no es mierda, estaba diciendo, ya que necesitaría encontrar un equivalente.forma de no leer. – Qberticus

+0

Dijo * como * Bignum de Java. No * utilice "Java Bignum. Dado que conoce el tipo decimal de C#, probablemente sea una buena apuesta que sepa que la pregunta es sobre C#. –

0

¿Se puede factorizar a, b, o c? ¿C tiene un rango conocido?

¡Estos son enteros de 32 bits! Ir comprobar esto site

Por ejemplo, aquí es cómo se obtiene el mod de n% d donde d 1 >> s (1,2,4,8, ...)

int n = 137;  // numerator 
    int d = 32;  // denom d will be one of: 1, 2, 4, 8, 16, 32, ... 
    int m;   // m will be n % d 
    m = n & (d - 1); 

Hay código para n% d donde d es 1 >> s - 1 (1, 3, 7, 15, 31, ...)

Esto solo ayudará si c es pequeño, como dijiste.

+1

¿quiere decir," donde d es 1 << (s-1) " ? –

+1

Sí. No puedo copiar desde otro sitio web sin cometer ningún error! – johnnycrash

6

La exponenciación modular rápida (creo que así es como se llama) podría funcionar.

 
Given a, b, c and a^b (mod c): 

1. Write b as a sum of powers of 2. (If b=72, this is 2^6 + 2^3) 
2. Do: 
    (1) a^2 (mod c) = a* 
    (2) (a*)^2 (mod c) = a* 
    (3) (a*)^2 (mod c) = a* 
    ... 
    (n) (a*)^2 (mod c) = a* 

3. Using the a* from above, multiply the a* for the powers of 2 you identified. For example: 
    b = 72, use a* at 3 and a* at 6. 
    a*(3) x a*(6) (mod c) 

4. Do the previous step one multiplication at a time and at the end, you'll have a^b % c. 

Ahora, cómo vas a hacer eso con los tipos de datos, no sé. Siempre que su tipo de datos pueda admitir c^2, creo que estará bien.

Si usa cadenas, solo cree cadenas de versiones para sumar, restar y multiplicar (no demasiado). Este método debe ser lo suficientemente rápido para hacer eso. (y puede comenzar el paso 1 con un mod c de modo que a nunca sea mayor que c).

EDITAR: Oh, mira, una página wiki en Modular Exponentiation.

1

Usted puede tratar de factorizar 'a' en números suficientemente pequeños.

Si los factores de 'a' son 'x', 'y', y 'z', entonces

a^b = (x^b) (y^b) (z^b).

continuación, puede utilizar su identidad: (a^b)% c = (a% c)^b% c

+0

Excepto que factorizar es un problema mucho más difícil que el módulo – Eclipse

+0

Empezaría dividiendo a/2 en un bucle hasta que no se dividiera de manera uniforme .... Entonces a/3, etc. con números primos hasta que 'a' era "suficientemente pequeño" para que a^b no se desborde. Luego complete la operación como se indica en el póster original. –

1

Me parece que hay algún tipo de relación entre el poder y mod. El poder simplemente se repite la multiplicación y el mod se relaciona con la división. Sabemos que la multiplicación y la división son inversas, por lo que a través de esa conexión asumiría que hay una correlación entre el poder y la mod.

Por ejemplo, tome potencias de 5:

5 % 4 = 1 
25 % 4 = 1 
125 % 4 = 1 
625 % 4 = 1 
... 

El patrón es claro que 5^b% 4 = 1 para todos los valores de b.

Es menos claro en esta situación:

5 % 3 = 2 
25 % 3 = 1 
125 % 3 = 2 
625 % 3 = 1 
3125 % 3 = 2 
15625 % 3 = 1 
78125 % 3 = 2 
... 

Pero todavía hay un patrón.

Si pudieras calcular los cálculos detrás de los patrones, no me sorprendería si pudieras descubrir el valor de la modificación sin utilizar la potencia real.

+1

El patrón es obvio y notorio .Es el Pequeño Teorema de Fermat (y la identidad se usa en el OP). –

+0

Ah sí, tienes razón, gracias por señalarme. Es obvio si lo has considerado así. del problema anterior y estoy agradecido de haber derivado la idea yo mismo :) – Kai

+1

¿Dónde se usa el pequeño teorema de Fermat en el OP? – Tobias

2

Python tiene pow (a, b, c) que devuelve (a ** b)% c (solo más rápido), por lo que debe haber alguna forma inteligente de hacerlo. Quizás solo hagan la identidad que mencionaste.

+0

¿Por qué votar abajo? Esto fue correcto –

9

Te supongo buscas: http://en.wikipedia.org/wiki/Montgomery_reduction o la manera más simple basado en modular Exponenciación (de Wikipedia)

Bignum modpow(Bignum base, Bignum exponent, Bignum modulus) { 

    Bignum result = 1; 

    while (exponent > 0) { 
     if ((exponent & 1) == 1) { 
      // multiply in this bit's contribution while using modulus to keep result small 
      result = (result * base) % modulus; 
     } 
     // move to the next bit of the exponent, square (and mod) the base accordingly 
     exponent >>= 1; 
     base = (base * base) % modulus; 
    } 

    return result; 
} 
+0

Si el módulo es más pequeño que el exponente, creo que se puede mejorar este algoritmo tomando el módulo de la cuenta de 1 bits de los exponentes y solo hacer muchas iteraciones de bucle. Uno podría incluso hacerlo mejor que eso en algunos casos. Ver la respuesta de Kai. – Brian

+0

creo que puede salir cada vez que result = 0 result = (result * base)% modulus; if (resultado == 0) break; – johnnycrash

+0

El formateo arruinó mi comentario. Agregar "if (result == 0) break;" después del cálculo del resultado. – johnnycrash

Cuestiones relacionadas