I know the result of a math.imul statement and would like to find one of the solutions that gives the known result. How would I solve for a value of x that works?
Math.imul( x , 2654435761); // the result of this statement is 1447829970
I know the result of a math.imul statement and would like to find one of the solutions that gives the known result. How would I solve for a value of x that works?
Math.imul( x , 2654435761); // the result of this statement is 1447829970
2
Answers
Basically, you want to find
x
in(x * 2654435761) % 232 == 1447829970
There’s an iterative solution for
(x * y) % 2n == z
with knowny
andz
. It is based on the fact that any odd number is coprime with any 2n, which means there has to be exactly 1 solution for any oddy
and arbitraryz
andn
.Calculation therefore starts with modulo 21:
((x % 21) * (y % 21)) % 21 == z % 21
Only 2 solutions for
x % 21
exist: 0 and 1. The algorithm picks the one which works. Then goes up by a power of 2, where the second possible solution will be equal to the last one increased by 21. Again, the valid one is picked, and the process proceeds until modulo 232 is reached.However, if
y
is even, thenz
must have at least as many low-order zero bits asy
has. Otherwise, there’s no solution.PS: This works in O(n) time with fixed-width integers. But a variation for arbitrary-width integers (such as BigInt) may explode up to O(n3) depending on how multiplication is implemented.
While blakkwaters answer is good, there is a simpler and more efficient iterative technique to "extend" a solution that is valid for a couple of bits to a solution that is valid for more bits.
To "unmultiply" by odd numbers, you only need to multiply by the modular multiplicative inverse of that odd number, which you can get like this (using Hensel lifting, which doubles the number of correct bits at every step, instead of adding one more correct bit):
"Unmultiplying"
z
by an even numbery
is only possible ifz
is at least as even asy
(in the sense of having at least as many trailing zeroes), and in that case we can just drop the trailing zeroes ofy
from bothy
andz
.Math.clz32(z & -z) ^ 31
is a trick to count the trailing zeroes. That gives 63 ifz = 0
, but that’s OK here. Ifz = 0
the result will still be zero which is correct. Ify = 0
then there is only a solution ifz = 0
as well, so having the trailing zero count be 63 is OK.I didn’t put the tests if
y
andz
are uint32 as a stylistic choice but of course you can put those back.