J'ai rencontré un problème théorique intéressant il y a plusieurs années. Je n'ai jamais trouvé de solution et elle continue de me hanter quand je dors.
Supposons que vous ayez une application (C #) contenant un certain nombre dans un entier, appelé x. (La valeur de x n'est pas fixe). Lorsque le programme est exécuté, x est multiplié par 33, puis écrit dans un fichier.
Le code source de base ressemble à ceci:
int x = getSomeInt();
x = x * 33;
file.WriteLine(x); // Writes x to the file in decimal format
Quelques années plus tard, vous découvrez que vous avez besoin des valeurs d'origine de X en arrière. Certains calculs sont simples: divisez simplement le nombre dans le fichier par 33. Cependant, dans d'autres cas, X est suffisamment grand pour que la multiplication provoque un débordement d'entier. Selon les documents , C # tronquera les bits de poids fort jusqu'à ce que le nombre soit inférieur à int.MaxValue
. Est-il possible, dans ce cas, soit:
- Récupérer X lui-même ou
- Récupérer une liste de valeurs possibles pour X?
Il me semble (bien que ma logique puisse certainement être erronée) qu'un ou les deux devraient être possibles, car le cas plus simple de l'addition fonctionne (Essentiellement, si vous ajoutez 10 à X et qu'il se termine, vous pouvez soustraire 10 et finir avec X à nouveau ) et la multiplication est simplement une addition répétée. Il est également utile (je crois) que X soit multiplié par la même valeur dans tous les cas - une constante de 33.
Cela dansait autour de mon crâne à des moments étranges depuis des années. Cela me viendra à l'esprit, je passerai un peu de temps à y réfléchir, puis je l'oublierai pendant quelques mois. J'en ai assez de courir après ce problème! Quelqu'un peut-il offrir un aperçu?
(Note latérale: je ne sais vraiment pas comment marquer celui-ci. Suggestions bienvenues.)
Edit: Permettez-moi de préciser que si je peux obtenir une liste de valeurs possibles pour X, il y a d'autres tests que je pourrais faire pour m'aider à le réduire à la valeur d'origine.
m
n'est que de 2 ^ 32 ou 2 ^ 64, plus l'exponentiation dea
modulom
est simple (il suffit d'ignorer le débordement là-bas)r*s^-1 mod m
et vous devez trouver les deuxr
ets
. Ici, nous avonsr*s mod m
et nous savons tout, maisr
.Réponses:
Multipliez par 1041204193.
Lorsque le résultat d'une multiplication ne rentre pas dans un entier, vous n'obtiendrez pas le résultat exact, mais vous obtiendrez un nombre équivalent au résultat exact modulo 2 ** 32 . Cela signifie que si le nombre que vous multipliez était coprime à 2 ** 32 (ce qui signifie simplement qu'il doit être impair), vous pouvez multiplier par son inverse multiplicatif pour récupérer votre nombre. Wolfram Alpha ou l' algorithme euclidien étendu peut nous dire le modulo inverse multiplicatif de 2 ** 32 32 est 1041204193. Donc, multipliez par 1041204193, et vous avez le x d'origine.
Si nous avions, disons, 60 au lieu de 33, nous ne pourrions pas récupérer le nombre d'origine, mais nous pourrions le réduire à quelques possibilités. En factorisant 60 en 4 * 15, en calculant l'inverse de 15 mod 2 ** 32, et en multipliant par cela, nous pouvons récupérer 4 fois le nombre d'origine, ne laissant que 2 bits de poids fort du nombre à la force brute. Wolfram Alpha nous donne 4008636143 pour l'inverse, qui ne rentre pas dans un int, mais ça va. Nous trouvons juste un nombre équivalent à 4008636143 mod 2 ** 32, ou le forçons dans un int de toute façon pour que le compilateur le fasse pour nous, et le résultat sera également un inverse de 15 mod 2 ** 32. ( Nous obtenons -286331153. )
la source
Cela peut être mieux adapté comme question à Math (sic) SE. Vous avez essentiellement affaire à l'arithmétique modulaire, car la suppression des bits les plus à gauche est la même chose.
Je ne suis pas aussi bon en maths que les gens qui sont en maths (sic) SE, mais je vais essayer de répondre.
Ce que nous avons ici, c'est que le nombre est multiplié par 33 (3 * 11), et son seul dénominateur commun avec votre mod est 1. C'est parce que, par définition, les bits de l'ordinateur sont des puissances de deux, et donc votre mod est une puissance de deux.
Vous pourrez construire la table où pour chaque valeur précédente vous calculez la valeur suivante. Et la question devient: les chiffres suivants correspondent-ils à un seul précédent?
Si ce n'était pas 33, mais un nombre premier ou une puissance d'un nombre premier, je pense que la réponse serait oui, mais dans ce cas… demandez à Math.SE!
Test programmatique
C'est en C ++ parce que je ne connais pas C #, mais le concept tient toujours. Cela semble montrer que vous pouvez:
Après avoir rempli une telle carte, vous pourrez toujours obtenir le X précédent si vous connaissez le suivant. Il n'y a qu'une seule valeur à tout moment.
la source
maxval+1
est 0 uniquement pour les types non signés.Une façon de l'obtenir est d'utiliser la force brute. Désolé, je ne connais pas C # mais ce qui suit est un pseudo-code de type c pour illustrer la solution:
Techniquement, ce dont vous avez besoin est
x*33%(INT_MAX+1) == test_value
mais le débordement d'entier fera automatiquement l'%
opération pour vous à moins que votre langue n'utilise des entiers de précision arbitraire (bigint).Ce que cela vous donne, c'est une série de chiffres qui pourraient être le numéro d'origine. Le premier nombre imprimé serait le nombre qui générerait un tour de débordement. Le deuxième nombre serait le nombre qui générerait deux tours de débordement. Etc..
Donc, si vous connaissez mieux vos données, vous pouvez faire une meilleure estimation. Par exemple, les calculs d'horloge courants (débordement toutes les 12 heures) ont tendance à rendre le premier nombre plus probable, car la plupart des gens s'intéressent aux événements qui se sont produits aujourd'hui.
la source
int
un entier signé de 4 octets qui encapsule, donc votre réponse est toujours bonne, bien que le forçage brut ne soit pas la meilleure façon de procéder si vous avez beaucoup d'entrées! :)int
n'est pas garanti que C se termine (voir les documents de votre compilateur). C'est vrai pour les types non signés.Vous pouvez le solveur SMT Z3 pour lui demander de vous donner une affectation satisfaisante pour la formule
x * 33 = valueFromFile
. Il inversera cette équation pour vous et vous donnera toutes les valeurs possibles dex
. Z3 prend en charge l'arithmétique bitvector exacte, y compris la multiplication.La sortie ressemble à ceci:
la source
Annuler ce résultat vous donnera un nombre fini non nul de nombres (normalement infini, mais
int
est un sous-ensemble fini de ℤ). Si cela est acceptable, générez simplement les chiffres (voir les autres réponses).Sinon, vous devez conserver une liste de l'historique (de longueur finie ou infinie) de l'historique de la variable.
la source
Comme toujours, il y a une solution d'un scientifique et une solution d'un ingénieur.
Ci-dessus, vous trouverez une très bonne solution d'un scientifique, qui fonctionne toujours, mais vous oblige à calculer «l'inverse multiplicatif».
Voici une solution rapide de l'ingénieur, qui ne vous obligera pas à essayer tous les entiers possibles.
Quelles sont les idées?
Int -> Long
)Int.MaxValue * multiplier
Le code exécutable complet se trouve sur http://ideone.com/zVMbGV
Détails:
val problemAsLong = (-1947051863).toLong & 0xFFFFFFFFL
Ici, nous convertissons notre numéro stocké en Long, mais comme Int et Long sont signés, nous devons le faire correctement.
Nous limitons donc le nombre en utilisant ET au niveau du bit avec des bits de Int.
val overflowBit = 0x100000000L
Ce bit ou sa multiplication pourrait être perdu par la multiplication initiale.
C'est un premier peu en dehors de la plage Int.
for(test <- 0 until multiplier)
Selon 3rd Idea, le débordement maximal est limité par le multiplicateur, alors n'essayez pas plus que ce dont nous avons vraiment besoin.
if((problemAsLong + overflowBit * test) % multiplier == 0)
Vérifiez si en ajoutant un débordement éventuellement perdu, nous arrivons à une solution
val original = originalLong.toInt
Le problème d'origine était dans la plage Int, alors revenons-y. Sinon, nous pourrions récupérer de manière incorrecte des nombres, qui étaient négatifs.
println(s"$original (test = $test)")
Ne vous cassez pas après la première solution, car il pourrait y avoir d'autres solutions possibles.
PS: la 3e idée n'est pas strictement correcte, mais laissée pour être compréhensible.
Int.MaxValue
est0x7FFFFFFF
, mais le débordement maximal est0xFFFFFFFF * multiplier
.Le texte correct serait donc "Le débordement n'était pas supérieur à
-1 * multiplier
".C'est exact, mais tout le monde ne le comprendra pas.
la source