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

时间:2013-12-14 18:25:07

标签: c++ math

我想编写计算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;
} 

4 个答案:

答案 0 :(得分:5)

有些建议修改当前方法: 注意:接下来是更好的方法!

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

<<运算符向左移位。将所有位向左移位1会使数字的整数值加倍,假设没有位从值的左侧“移开”。因此,1 << n将计算2 n

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


但是,以上所有内容仅仅是当前方法中的优化。值得理解这些改进以更好地理解语言。但是,他们不会考虑更大的图景。

Modular multiplication允许你找到(A * B)mod C为((A mod C)*(B mod C))mod C.这对我们有什么帮助?

我们可以以一种仅将NX限制为机器整数精度的方式重写整个算法,而不是2 N

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大到2 32 - 1,假设unsigned int是32位,没有计算溢出。

通过这种方法,即使是非常大的输入,我也能很快找到答案。例如,111111111会返回667332。我使用任意精度计算器bc验证了2 677332 mod 111111111 == 1。

速度非常快。它在我的电脑上在不到0.07秒的时间内计算出2 2323860 mod 4294967293 == 1。


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

它通常是这样的,无论是数学还是其他算法方面。并且,你应该不会惊讶地发现离散数学是我们的许多图形和集合算法的来源。编程语言本身就是大局的一小部分。

答案 1 :(得分:1)

对于介于1和k之间的每个ceil(sqrt(n)),请计算2^k mod n2^(k ceil(sqrt(n))) mod n。然后计算每个2^k的模数逆。将所有inverse(2^k)分类为数组foo,将2^(k ceil(sqrt(n))分类为数组bar。两个数组之间至少有一个共同的值;找到它。说inverse(2^a) = 2^(b ceil(sqrt(n)))。然后是2^(a + b ceil(sqrt(n))) = 1 (mod n)

答案 2 :(得分:1)

你的教授的幽默感如何?

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

始终按照规定打印出问题的正确答案。

答案 3 :(得分:0)

pow在计算中非常昂贵,但是如果你有2作为它的第一个参数,你可以更好地向左移动,因为左移等于乘以2:

cheak = (1 << cntr);