Me encontré con un problema teórico interesante hace varios años. Nunca encontré una solución, y continúa obsesionándome cuando duermo.
Supongamos que tiene una aplicación (C #) que contiene algún número en un int, llamado x. (El valor de x no es fijo). Cuando se ejecuta el programa, x se multiplica por 33 y luego se escribe en un archivo.
El código fuente básico se ve así:
int x = getSomeInt();
x = x * 33;
file.WriteLine(x); // Writes x to the file in decimal format
Algunos años después, descubres que necesitas recuperar los valores originales de X. Algunos cálculos son simples: simplemente divida el número en el archivo por 33. Sin embargo, en otros casos, X es lo suficientemente grande como para que la multiplicación provoque un desbordamiento de enteros. Según los documentos , C # truncará los bits de orden superior hasta que el número sea menor que int.MaxValue
. ¿Es posible, en este caso,:
- Recuperar X mismo o
- ¿Recuperar una lista de posibles valores para X?
Me parece (aunque mi lógica ciertamente podría ser defectuosa) que uno o ambos deberían ser posibles, ya que el caso más simple de suma funciona (Esencialmente, si agrega 10 a X y se ajusta, puede restar 10 y terminar con X nuevamente ) y la multiplicación es simplemente una suma repetida. También ayuda (creo) el hecho de que X se multiplica por el mismo valor en todos los casos, una constante 33.
Esto ha estado bailando alrededor de mi cráneo en momentos extraños durante años. Se me ocurrirá, pasaré un tiempo tratando de pensarlo, y luego me olvidaré de eso durante unos meses. Estoy cansado de perseguir este problema! ¿Alguien puede ofrecer una idea?
(Nota al margen: Realmente no sé cómo etiquetar este. Se aceptan sugerencias.)
Editar: Permítanme aclarar que si puedo obtener una lista de posibles valores para X, hay otras pruebas que podría hacer para ayudarme a reducirlo al valor original.
m
es sólo 2 ^ 32 o 2 ^ 64, además de la exponenciación dea
módulom
es sencillo (simplemente ignorar desbordamiento allí)r*s^-1 mod m
y necesitas encontrar ambosr
ys
. Aquí tenemosr*s mod m
y sabemos todo menosr
.Respuestas:
Multiplicar por 1041204193.
Cuando el resultado de una multiplicación no cabe en un int, no obtendrá el resultado exacto, pero obtendrá un número equivalente al resultado exacto módulo 2 ** 32 . Eso significa que si el número por el que multiplicaste fue coprimo a 2 ** 32 (lo que significa que tiene que ser impar), puedes multiplicar por su inverso multiplicativo para recuperar tu número. Wolfram Alpha o el algoritmo Euclidiano extendido pueden decirnos que el módulo inverso multiplicativo 2 33 de 33 es 1041204193. Entonces, multiplique por 1041204193, y tendrá la x original de vuelta.
Si tuviéramos, por ejemplo, 60 en lugar de 33, no podríamos recuperar el número original, pero podríamos reducirlo a unas pocas posibilidades. Al factorizar 60 en 4 * 15, calcular el inverso de 15 mod 2 ** 32, y multiplicar por eso, podemos recuperar 4 veces el número original, dejando solo 2 bits de alto orden del número a fuerza bruta. Wolfram Alpha nos da 4008636143 para el inverso, que no cabe en un int, pero está bien. Simplemente encontramos un número equivalente a 4008636143 mod 2 ** 32, o lo forzamos a un int de todos modos para que el compilador lo haga por nosotros, y el resultado también será un inverso de 15 mod 2 ** 32. ( Obtenemos -286331153. )
fuente
Esto puede ser más adecuado como una pregunta para Math (sic) SE. Básicamente se trata de aritmética modular, ya que soltar los bits más a la izquierda es lo mismo.
No soy tan bueno en matemáticas como las personas que están en matemáticas (sic) SE, pero intentaré responder.
Lo que tenemos aquí es que el número se multiplica por 33 (3 * 11), y su único denominador común con su mod es 1. Eso es porque, por definición, los bits en la computadora son potencias de dos, y por lo tanto su mod es alguna potencia de dos.
Podrá construir la tabla donde para cada valor anterior calcule el siguiente valor. Y la pregunta es si los siguientes números corresponden a uno solo anterior.
Si no fuera 33, sino un primo o algún poder de un primo, creo que la respuesta sería sí, pero en este caso ... ¡pregunte por Math.SE!
Prueba programática
Esto está en C ++ porque no sé C #, pero el concepto aún se mantiene. Esto parece mostrar que puedes:
Después de llenar dicho mapa, siempre podrá obtener la X anterior si conoce la siguiente. Solo hay un valor único en todo momento.
fuente
maxval+1
es 0 solo para tipos sin signo.Una forma de obtenerlo es usar la fuerza bruta. Lo siento, no sé C #, pero el siguiente es un pseudocódigo similar a C para ilustrar la solución:
Técnicamente, lo que necesita es que el
x*33%(INT_MAX+1) == test_value
desbordamiento de enteros realice automáticamente la%
operación por usted a menos que su idioma use enteros de precisión arbitraria (bigint).Lo que esto te da es una serie de números que pueden haber sido el número original. El primer número impreso sería el número que generaría una ronda de desbordamiento. El segundo número sería el número que generaría dos rondas de desbordamiento. Y así..
Entonces, si conoce mejor sus datos, puede adivinar mejor. Por ejemplo, las matemáticas de reloj comunes (desbordamiento cada 12 en punto) tienden a hacer que el primer número sea más probable ya que la mayoría de las personas están interesadas en las cosas que sucedieron hoy.
fuente
int
es decir, es un entero con signo de 4 bytes que se ajusta, por lo que su respuesta sigue siendo buena, ¡aunque el forzado bruto no sería la mejor manera de hacerlo si tiene muchas entradas! :)int
no se garantiza que C se ajuste (consulte los documentos de su compilador). Sin embargo, es cierto para los tipos sin signo.Podría pedirle al solucionador de SMT Z3 que le pida una asignación satisfactoria para la fórmula
x * 33 = valueFromFile
. Invertirá esa ecuación para usted y le dará todos los valores posibles dex
. Z3 admite la aritmética exacta del vector de bits, incluida la multiplicación.La salida se ve así:
fuente
Deshacer ese resultado le dará una cantidad finita de números distintos de cero (normalmente infinito, pero
int
es un subconjunto finito de ℤ). Si esto es aceptable, solo genera los números (ver otras respuestas).De lo contrario, debe mantener una lista del historial (de longitud finita o infinita) del historial de la variable.
fuente
Como siempre, hay una solución de un científico y una solución de un ingeniero.
Arriba encontrará una muy buena solución de un científico, que funciona siempre, pero requiere que calcule "inverso multiplicativo".
Aquí hay una solución rápida del ingeniero, que no lo obligará a probar todos los enteros posibles.
Cuales son las ideas?
Int -> Long
)Int.MaxValue * multiplier
El código ejecutable completo se encuentra en http://ideone.com/zVMbGV
Detalles:
val problemAsLong = (-1947051863).toLong & 0xFFFFFFFFL
Aquí convertimos nuestro número almacenado a Long, pero como Int y Long están firmados, tenemos que hacerlo correctamente.
Entonces, limitamos el número usando bit a bit Y con bits de Int.
val overflowBit = 0x100000000L
Este bit o multiplicación podría perderse por la multiplicación inicial.
Está un poco fuera del rango Int.
for(test <- 0 until multiplier)
Según 3rd Idea, el desbordamiento máximo está limitado por el multiplicador, así que no intentes más de lo que realmente necesitamos.
if((problemAsLong + overflowBit * test) % multiplier == 0)
Compruebe si al agregar un desbordamiento posiblemente perdido llegamos a una solución
val original = originalLong.toInt
El problema original estaba en el rango Int, así que volvamos a él. De lo contrario, podríamos recuperar incorrectamente los números, que fueron negativos.
println(s"$original (test = $test)")
No se rompa después de la primera solución, porque podría haber otras posibles soluciones.
PD: 3rd Idea no es estrictamente correcta, pero se dejó para ser entendible.
Int.MaxValue
es0x7FFFFFFF
, pero el desbordamiento máximo es0xFFFFFFFF * multiplier
.Por lo tanto, el texto correcto sería "El desbordamiento no fue más que
-1 * multiplier
".Esto es correcto, pero no todos lo entenderán.
fuente