求解大数模方程的有效方法
Efficient way to solve modulo equation with huge numbers
我正在尝试求解以下等式 (x * 0x5DEECE66D + 11) mod 2^47 = 0x310CDAF20000
其中 WolframAlpha is able to solve in a few seconds
我想出了以下 C++
代码,将模数替换为 &
,遗憾的是效率不够高,需要很长时间才能完成:
auto target = 0x310CDAF20000LL;
auto devider = 0x5DEECE66DLL;
auto mask = 1LL << 48 - 1;
auto i = 1LL;
while (1) {
if ((i * devider + 11) & mask == target) {
printf("%llx", i);
break;
}
i++;
}
有什么建议吗?
如果 (x * 0x5DEECE66D + 11) mod 2^47 = 0x310CDAF20000
为真,则 (x * 0x5DEECE66D) mod 2^47 = 310CDAF1FFF5
将为真(对于非负数 x
可能取决于 mod
的定义方式)。
将所有内容向左移动 17 位不会改变任何内容。换句话说,如果 (x * 0x5DEECE66D) mod 2^47 = 310CDAF1FFF5
那么 (x * 2EF767336800000) mod 2^64 = 866D78FFFA800000
.
对于计算机,对于 64 位(无符号)整数和标准的“截断以适合”,这意味着可以删除 mod
。
下一步;如果 (x * 2EF767336800000) mod 2^64 = y
那么 ( (x+1) * 2EF767336800000) mod 2^64 = (y + 2EF767336800000) mod 2^64
.
换句话说,从x = 0
开始(这里很明显是(x * 2EF767336800000) mod 2^64 = 0
)你可以使用“add then AND”来确定x = 1
的结果,然后做同样的事情对于 x = 2
,然后 ...
这导致:
auto target = 0x310CDAF20000LL;
auto devider = 0x5DEECE66DLL;
auto i = 1LL;
auto t = (target - 11) << 17;
auto d = devider << 17;
uint64_t temp = 0;
do {
temp += d;
i++;
} while(temp != t);
更进一步就是意识到如果 (x * 2EF767336800000) mod 2^64 = y
那么 ( (x+8) * 2EF767336800000) mod 2^64 = (y + 2EF767336800000 * 8) mod 2^64
。这意味着如果您有 8 个起始值(x = 0
到 x = 7
),您可以使用 SIMD 并行为 x
执行 8 个值。
从这里开始
25214903917 * x = 53931282661365 (mod 2^47)
根据欧拉定理,
25214903917 ^ phi(2^47) = 1 (mod 2^47)
在这种情况下,欧拉的 totient 很容易计算为 (2^47)/2 = 2^46。所以
25214903917 * 25214903917 ^ (phi(2^47) -1) = 1 (mod 2^47)
25214903917 * 25214903917 ^ (2^46 -1) = 1 (mod 2^47)
25214903917 * (53931282661365 * 25214903917 ^ (2^46 -1)) = 53931282661365 (mod 2^47)
x = 53931282661365 * 25214903917 ^ (2^46 -1) (mod 2^47)
您可以使用平方取幂来计算取幂。代码非常简单(C#)
long desiredResult = 53931282661365;
long q = 25214903917;
long invq = 1;
long tmp = q;
for(int i = 0; i < 46; ++i)
{
// tmp is q^(2^i)
invq = (invq * tmp) & 0x7FFFFFFFFFFF;
tmp = (tmp * tmp) & 0x7FFFFFFFFFFF;
}
// invq is 105417217348453
long x = (invq * desiredResult) & 0x7FFFFFFFFFFF;
// x is 91896827357865
long test = (q * x) & 0x7FFFFFFFFFFF;
// test is 53931282661365
整个解是n * 2^47 + x
,对应WolframAlpha
我正在尝试求解以下等式 (x * 0x5DEECE66D + 11) mod 2^47 = 0x310CDAF20000
其中 WolframAlpha is able to solve in a few seconds
我想出了以下 C++
代码,将模数替换为 &
,遗憾的是效率不够高,需要很长时间才能完成:
auto target = 0x310CDAF20000LL;
auto devider = 0x5DEECE66DLL;
auto mask = 1LL << 48 - 1;
auto i = 1LL;
while (1) {
if ((i * devider + 11) & mask == target) {
printf("%llx", i);
break;
}
i++;
}
有什么建议吗?
如果 (x * 0x5DEECE66D + 11) mod 2^47 = 0x310CDAF20000
为真,则 (x * 0x5DEECE66D) mod 2^47 = 310CDAF1FFF5
将为真(对于非负数 x
可能取决于 mod
的定义方式)。
将所有内容向左移动 17 位不会改变任何内容。换句话说,如果 (x * 0x5DEECE66D) mod 2^47 = 310CDAF1FFF5
那么 (x * 2EF767336800000) mod 2^64 = 866D78FFFA800000
.
对于计算机,对于 64 位(无符号)整数和标准的“截断以适合”,这意味着可以删除 mod
。
下一步;如果 (x * 2EF767336800000) mod 2^64 = y
那么 ( (x+1) * 2EF767336800000) mod 2^64 = (y + 2EF767336800000) mod 2^64
.
换句话说,从x = 0
开始(这里很明显是(x * 2EF767336800000) mod 2^64 = 0
)你可以使用“add then AND”来确定x = 1
的结果,然后做同样的事情对于 x = 2
,然后 ...
这导致:
auto target = 0x310CDAF20000LL;
auto devider = 0x5DEECE66DLL;
auto i = 1LL;
auto t = (target - 11) << 17;
auto d = devider << 17;
uint64_t temp = 0;
do {
temp += d;
i++;
} while(temp != t);
更进一步就是意识到如果 (x * 2EF767336800000) mod 2^64 = y
那么 ( (x+8) * 2EF767336800000) mod 2^64 = (y + 2EF767336800000 * 8) mod 2^64
。这意味着如果您有 8 个起始值(x = 0
到 x = 7
),您可以使用 SIMD 并行为 x
执行 8 个值。
从这里开始
25214903917 * x = 53931282661365 (mod 2^47)
根据欧拉定理,
25214903917 ^ phi(2^47) = 1 (mod 2^47)
在这种情况下,欧拉的 totient 很容易计算为 (2^47)/2 = 2^46。所以
25214903917 * 25214903917 ^ (phi(2^47) -1) = 1 (mod 2^47)
25214903917 * 25214903917 ^ (2^46 -1) = 1 (mod 2^47)
25214903917 * (53931282661365 * 25214903917 ^ (2^46 -1)) = 53931282661365 (mod 2^47)
x = 53931282661365 * 25214903917 ^ (2^46 -1) (mod 2^47)
您可以使用平方取幂来计算取幂。代码非常简单(C#)
long desiredResult = 53931282661365;
long q = 25214903917;
long invq = 1;
long tmp = q;
for(int i = 0; i < 46; ++i)
{
// tmp is q^(2^i)
invq = (invq * tmp) & 0x7FFFFFFFFFFF;
tmp = (tmp * tmp) & 0x7FFFFFFFFFFF;
}
// invq is 105417217348453
long x = (invq * desiredResult) & 0x7FFFFFFFFFFF;
// x is 91896827357865
long test = (q * x) & 0x7FFFFFFFFFFF;
// test is 53931282661365
整个解是n * 2^47 + x
,对应WolframAlpha