【问题标题】:Efficient way to solve modulo equation with huge numbers求解具有大量数字的模方程的有效方法
【发布时间】:2021-07-21 01:57:58
【问题描述】:

我正在尝试解决以下等式(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++;
}

有什么建议吗?

【问题讨论】:

    标签: performance equation modulo


    【解决方案1】:

    从这里开始

    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

    【讨论】:

    • 这太疯狂了。我爱数学。会再读几遍您的评论,以准确了解您的所作所为。
    【解决方案2】:

    如果(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 = 0x = 7),您可以使用 SIMD 并行处理 x 的 8 个值。

    【讨论】:

    • 这是一个很好的方法,遗憾的是它没有我需要的那么快。我需要为0xFFFF0x310CDAF20000 + i, for i from 0 to 0xFFFF 解决这个问题。我想我会明白如何解决那个案例,然后将解决方案应用于其余案例。
    • @DanielCohen:在这种情况下,您将确定 temp,然后在循环结束时检查 temp 是否是您仍在寻找的任何(最多 65536 个)值.这样一来,它就不应该贵 65536 倍(可能只有 4 倍)。
    • 我最终使用 wolframscript 来获得我需要的东西。这显然不是一个合适的解决方案,但似乎这是一个困难的数学问题,因此使用数学计算引擎似乎是合理的。
    猜你喜欢
    • 1970-01-01
    • 2019-03-11
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-16
    • 1970-01-01
    相关资源
    最近更新 更多