【问题标题】:Computing (a^x)mod n using addition chaining. Algorithm in C++使用加法链计算 (a^x)mod n。 C++ 中的算法
【发布时间】:2017-07-16 13:33:29
【问题描述】:
unsigned long qe2(unsigned long x, unsigned long y , unsigned long n)
{
    unsigned long s,t,u;

    s=1; t=x; u=y;

    while(u)
    {
        if(u&1)
            s = (s*t)%n;
        u>>=1;
        t= ( t*t)%n;
    }
    return s;
}

我正在阅读密码学,并且在 Bruice Schneier 的《应用密码学》一书中,我发现上述算法可以以较低的计算成本计算 (x^y)mod n。它基本上使用一种称为加法链接的算法来减少计算中的乘法量。我可以在笔和纸上使用这个过程,但是尽管一遍又一遍地阅读上面的代码,我还是无法理解它是如何工作的。所以请指点我正确的方向(某种文章的链接),在那里我可以分析上述算法,或者如果你能在这里给出解释,那将非常有帮助。

附: : 书中没有解释代码。

【问题讨论】:

  • 了解其工作原理的最佳方法可能是使用调试器逐行执行代码。然后观察变量值在每一步是如何变化的。
  • “加法链”是用来描述这类算法的通用术语。但是,这是一种简化的特殊情况,通常称为 right-to-left binary exponentiation algorithm,您可以使用该搜索词找到很多文档。
  • 您应该针对特定问题提出特定问题。由于 Stack Overflow 向您隐藏了关闭原因:“要求我们推荐或查找书籍、工具、软件库、教程或其他非现场资源的问题对于 Stack Overflow 来说是无关紧要的,因为它们往往会吸引固执己见的答案和垃圾邮件。相反,请描述问题以及迄今为止为解决该问题所做的工作。"

标签: c++ math cryptography modular-arithmetic


【解决方案1】:

指数y写成2的幂的和,即二进制。

考虑一个实际示例:(x**11) % M。数学上,

(x**11) % M == ((((((x**1) % M) * x**2) % M) * x**8) % M)

这很有用,因为一个简单的循环足以计算 x 的幂,即 2 的幂。比如要计算x**(2**i)

for (j = 0; j < i; j++)
    x = x * x;

如果我们看一个计算basis**exponent的函数,我们可以详细检查逻辑:

unsigned long power(unsigned long basis,
                    unsigned long exponent)
{
    unsigned long result = 1u;
    unsigned long factor = basis;
    unsigned long i;

    for (i = 1u; i < exponent; i = i * 2u) {
        /* i is some power of two,
           and factor = basis**i.
        */

        /* If i is in the sum (of powers of two) for exponent,
           we include the corresponding power of basis
           in the product. */
        if (i & exponent)
            result = result * factor;

        /* Update factor for the next i. */
        factor = factor * factor;
    }

    return result;
}

请注意,测试(i &amp; exponent) 是二元与运算,如果结果为非零则为真,如果为零则为假。因为i 是2 的幂,所以在二进制中它有一个1,所有其他二进制数字为零,所以它基本上测试用二进制编写的指数是否在那个位置有一个1

OP 的代码更简单,因为它没有使用单独的变量ifactor,而是将exponent 右移(删除最右边的二进制数字),并使用basis 本身。也就是说,

unsigned long power(unsigned long basis,
                    unsigned long exponent)
{
    unsigned long result = 1u;

    while (exponent > 0u) {
        if (exponent & 1u)
            result = result * basis;
        basis = basis * basis;
        exponent = exponent >> 1;
    }

    return result;
}

模运算是最后的皱纹,但它也是微不足道的。如果您以某个正整数为模计算乘积,则可以将模运算符应用于每个项和每个临时结果,而不会影响结果:

(a * b * c * d * ... * z) % m
= ( (a % m) * (b % m) * (c % m) * ... * (z % m) ) % m
= ( ... (((((a % m) * (b % m)) % m) * (c % m)) % m) * ... * (z % m)) % m

这显然很有用,因为您可以计算任意数量的项的乘积,所有项都小于m,所有中间项都小于m*m

这种取幂的时间复杂度是O(log N),其中N是指数。

【讨论】:

  • 这对我有帮助,谢谢,但我认为代码中存在错误:在函数 power() 的第二个示例中,结果最初为零(我认为它应该是 1).. 和它一直保持为零直到结束,因此该函数将始终返回零。
  • @RatulThakur:当然,两个power() 函数都应该以result = 1u 开头。固定的。接得好;谢谢!
猜你喜欢
  • 2012-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
  • 1970-01-01
  • 2013-11-14
  • 2023-03-29
相关资源
最近更新 更多