了解这种计算方式x ^ e mod n

问题描述

使用c ++进行RSA加密,发现cmath中的pow()函数给了我错误的结果。

在上网查看后,我遇到了一些可以为我完成上述过程的代码,但是我很难理解它。

这是代码:

long long int modpow(long long int base,long long int exp,long long int modulus) {
    base %= modulus;
    long long int result = 1;
  while (exp > 0) {
    if (exp & 1) {
    result = (result * base) % modulus; 
    }
    base = (base * base) % modulus;
    exp >>= 1; 
  }
  return result;
}

(此代码不是原始代码)

我正在努力理解此功能。

我知道exp >>=1;是左移1位,而(exp & 1)根据最低有效位返回1或0,但是我不明白这是如何影响最终答案的。

例如:

if (exp & 1) {
    result = (result * base) % modulus; 
        }

如果exp为奇数,(result * base) % modulus的目的是什么?

希望有人可以向我解释此功能,因为我不想将其复制过来。

解决方法

该代码被编写为“聪明”而不是清晰。这种神秘的风格通常不应该在核心库(性能至关重要)之外完成,即使使用它,也可以通过注释来解释正在发生的事情。 这是代码的带注释的版本。

long long int modpow(long long int base,long long int exp,long long int modulus)
{
  base %= modulus;                          // Eliminate factors; keeps intermediate results smaller
  long long int result = 1;                 // Start with the multiplicative unit (a.k.a. one)
                                            // Throughout,we are calculating:
                                            // `(result*base^exp)%modulus`
  while (exp > 0) {                         // While the exponent has not been exhausted.
    if (exp & 1) {                          // If the exponent is odd
      result = (result * base) % modulus;   // Consume one application of the base,logically
                                            // (but not actually) reducing the exponent by one.
                                            // That is: result * base^exp == (result*base)*base^(exp-1)
    }
    base = (base * base) % modulus;         // The exponent is logically even. Apply B^(2n) == (B^2)^n.
    exp >>= 1;                              // The base is squared and the exponent is divided by 2 
  }
  return result;
}

现在有意义吗?


对于那些想知道如何使密码更清晰的人,我提供了以下版本。有三个主要改进。

首先,按位运算已由等效的算术运算代替。如果要证明该算法有效,则将使用算术运算符,而不是按位运算符。实际上,无论数字如何表示,该算法都有效–不需要“位”的概念,更不用说“位运算符”的概念了。因此,实现该算法的自然方法是算术。使用按位运算符会消除清晰度,几乎没有任何好处。编译器足够聪明,可以生成相同的机器代码,只有一个例外。由于exp被声明为long long int而不是long long unsigned,因此与exp /= 2相比,在计算exp >>= 1时有一个额外的步骤。 (我不知道为什么对exp进行了签名;对于负指数,该函数在概念上既无意义,又在技术上不正确。)另请参见

第二,我创建了一个辅助函数来提高可读性。尽管改进很小,但不影响性能。我希望值得任何值得赞扬的编译器内联该函数。

// Wrapper for detecting if an integer is odd.
bool is_odd(long long int n)
{
    return n % 2 != 0; 
}

第三,添加了注释以说明正在发生的情况。虽然有些人(不是我)可能认为“标准的从右到左模块化二进制幂运算算法” 是每个C ++编码人员都需要的知识,但我还是希望对可能会阅读的人做出更少的假设我将来的代码。尤其是如果那个人是我,请在距离代码多年后再返回代码。

现在,我喜欢看当前编写的功能的代码:

// Returns `(base**exp) % modulus`,where `**` denotes exponentiation.
// Assumes `exp` is non-negative.
// Assumes `modulus` is non-zero.
// If `exp` is zero,assumes `modulus` is neither 1 nor -1.
long long int modpow(long long int base,long long int modulus)
{
    // NOTE: This algorithm is known as the "right-to-left binary method" of
    // "modular exponentiation".

    // Throughout,we'll keep numbers smallish by using `(A*B) % C == ((A%C)*B) % C`.
    // The first application of this principle is to the base.
    base %= modulus;
    // Intermediate results will be stored modulo `modulus`.
    long long int result = 1;

    // Loop invariant:
    //     The value to return is `(result * base**exp) % modulus`.
    // Loop goal:
    //     Reduce `exp` to the point where `base**exp` is 1.
    while (exp > 0) {
        if ( is_odd(exp) ) {
            // Shift one factor of `base` to `result`:
            // `result * base^exp == (result*base) * base^(exp-1)`
            result = (result * base) % modulus;
            //--exp;  // logically happens,but optimized out.
            // We are now in the "`exp` is even" case.
        }
        // Reduce the exponent by increasing the base: `B**(2n) == (B**2)**n`.
        base = (base * base) % modulus;
        exp /= 2;
    }

    return result;
}

生成的机器代码几乎相同。如果性能确实很关键,我可以回头回到exp >>= 1,但前提是不允许更改exp的类型。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...