如何在不到1秒的时间内计算2^x mod n=1

How to Calculate 2^x mod n = 1 in less than 1 second

本文关键字:mod 计算 1秒 时间      更新时间:2023-10-16

我想写一个程序,计算2^x mod n = 1,我们有ninteger,但我们应该计算x。我写了代码,但我的代码在大n中工作太慢了。你能给我一个在1秒内解决这个问题的好方法吗
这是我的代码:

#include <iostream>
#include <cmath>
using namespace std;
int main()
{
    long long int n,cntr=1,cheak;
    cin >> n;
    while (1)
    {
        if (n % 2 == 0)
        {
            break;
        }
        cheak=pow(2, cntr);
        if (cheak % n == 1)
            break;
        cntr++;
    }
    cout << cntr << endl;
} 

当前方法的一些建议修改: nbsp注意:下面是更好的方法

  • 将您的long long int更改为unsigned long long int。这会给你多一点
  • while (1)更改为while (cntr < 64)unsigned long long的大小可能只有64位。(它保证至少是64位,但不能大于64位。)然而,您需要检查循环是否成功
  • 更改cheak以将2n计算为1ull << cntr。确保包含ull后缀,表示这是unsigned long long

<<运算符将比特向左移位。将所有位向左移动1将使该数字的整数值加倍,假设没有位从该值的左侧"移位"。因此,1 << n将计算2n

后缀ull表示整数常数是unsigned long long。如果省略此后缀,1将被视为整数,并且超过31的移位值将无法执行您想要的操作。


然而,以上所有内容都只是对当前方法的改进。为了更好地理解这种语言,了解这些提炼是值得的。然而,他们并不着眼于大局。

模乘可以让你找到(A*B)mod C作为((A mod C)*(B mod C))mod C。这对我们有什么帮助?

我们可以重写整个算法,只将NX限制为机器整数的精度,而不是2N:

int main()
{
    unsigned int modulus;
    unsigned int raised = 2;
    int power = 1;
    std::cin >> modulus;
    if (modulus % 2 == 1)
    {
        while (raised % modulus != 1)
        {
            raised = ((unsigned long long)raised * 2) % modulus;
            power++;
        }
        std::cout << power << std::endl;
    } else
    {
        std::cout << "modulus must be odd" << std::endl;
    }
}

上面对unsigned long long的强制转换允许modulus大到232-1,假设unsigned int是32位,而没有计算溢出。

有了这种方法,即使输入量很大,我也能很快找到答案。例如,111111111返回667332。我使用任意精度计算器bc验证了2677332mod11111==1。

速度很快。在我的电脑上,它在不到0.07秒内计算出了222323860mod4294967293==1。


Epilog:这突出了编程中的一个重要原则:实际上,这是一个数学问题,而不是编程问题。要找到一个有效的解决方案,需要比了解C++更多地了解问题领域。一旦我们确定了正确的数学方法,实际的C++代码就微不足道了。

它经常是这样的,无论是数学还是其他算法方面。而且,当你了解到离散数学是我们许多图和集合算法的来源时,你不应该感到惊讶。编程语言本身只是全局的一小部分。

对于1和ceil(sqrt(n))之间的每个k,计算2^k mod n2^(k ceil(sqrt(n))) mod n。然后计算每个CCD_ 31的模逆。将所有inverse(2^k) s排序为数组foo,将2^(k ceil(sqrt(n)) s排序为阵列bar。在这两个数组之间将至少有一个共同的值;找到它。说inverse(2^a) = 2^(b ceil(sqrt(n)))。然后CCD_ 37。

你的教授的幽默感怎么样?

#include <iostream>
int main() { std::cout << 0 << 'n'; }

始终按说明打印问题的正确答案。

pow在计算中相当昂贵,但如果将2作为其第一个参数,则可以更好地向左移动,因为向左移动等于乘以2:

cheak = (1 << cntr);