【问题标题】:Big Integer Modular Exponentiation大整数模幂运算
【发布时间】:2016-10-05 04:37:12
【问题描述】:

如何用 1 计算 (xy) mod z 1000 和 z 任意正整数 1 31 ?

到目前为止,我所做的是: 将 x 和 y 扫描为字符串,取模,然后计算 (xy) mod z。

我知道这是错误的,因为 (xy) mod z 不等于 ((x mod z)(y mod z)) mod z。那我该如何解决呢?

编辑:抱歉,我在创建问题时将 x 和 y 的底部约束设置得如此之高。我只是想把其他焦点放在大整数问题上,而不是模幂:)。

#define MOD z

long long power (long long k, long long n) {
    if (n == 1) return k;
    else {
        long long p = power (k, n/2);
        if (n % 2 == 0) return (p * p) % MOD;
        else return (((p * p) % MOD) * k) % MOD;
    }
}

long long convert (char *n) {
    long long number = 0;
    int ln = strlen (n);
    
    for (int x = 0; x < ln; x++) {
        number = number * 10;
        number = (number + (n[x] - '0')) % MOD;
    }
    
    return number % MOD;
}

int main () {
    char s_x[1111], s_y[1111];
    scanf ("%s %s", s_x, s_y);
    
    long long x, y, r;
    x = convert (s_x);
    y = convert (s_y);
    r = power (x, y);
        
    printf ("%lld\n", r);
}

【问题讨论】:

  • K*x % x 始终为 0。设置 K = pow(x, y-1) 没有任何改变。
  • 我想你的意思可能是 pow(x,y)%z,而不是 pow(x,y)%x。
  • 对不起。错字。已编辑。
  • @RonaldSumbayak 你应该明确z 的范围。显示xy 而不是z 的范围很奇怪。至少有一个答案假设了一定的范围,大大简化了问题,而且你没有澄清这个假设是否正确。请edit您的问题澄清这一点。
  • 好的,谢谢。已编辑。

标签: c modulus exponentiation


【解决方案1】:

由于模块化指数被大量使用,因此有一些库。以下是读取 a、b 和 c 并使用 GMP 输出 ab mod c 的示例。

#include <stdio.h>
#include <gmp.h>

int main(void)
{
  mpz_t a, b, c, d;
  mpz_inits (a, b, c, d, NULL);
  printf ("a: ");
  mpz_inp_str (a, stdin, 10);
  printf ("b: ");
  mpz_inp_str (b, stdin, 10);
  printf ("c: ");
  mpz_inp_str (c, stdin, 10);
  mpz_powm (d, a, b, c); // compute d = a ^ b mod c
  gmp_printf ("a ^ b mod c = %Zd\n", d);
  return 0;
}

-lgmp编译它。

顺便说一下,ab ≡ ab mod Φ(c) (mod c),其中 Φ 是Euler's totient function

【讨论】:

  • 如果 a^b ≡ (a^(b mod Φ (c))) mod c, did (a^(b mod Φ (c))) mod c 等于 ((a mod c )^(b mod Φ (c))) mod c?
  • @RonaldSumbayak 是的。在谈论整数 mod c 时,a 和 (a mod c) 是相同的。
【解决方案2】:

首先,我假设z 相当小(例如,适合长)。还要注意

(x ^ y) % z = ((x % z) ^ y) % z

所以可以按照你的方式转换x,唯一的问题是y。方便的是,你只用y 做两件事——你把它除以二,然后检查除以二后的余数。如果您将y 表示为一个数组,那么这两件事都是微不足道的。首先,为了简单起见,反转y,使最低有效位在前,并且在数组中存储数字,而不是数字字符(如存储5,而不是'5')。您还可以考虑在每个元素中存储不止一个数字,但这只会通过一个常数来改进它。

现在要检查余数,只需检查数组的第一个元素是否可被 2 整除(即使其最低有效数字为偶数,该数字也是偶数)。要除以二,请执行以下操作:

for (int i = 0; i < y_len; ++ i) {
    if (i && y[i] % 2) y[i - 1] += 5;
    y[i] /= 2;
}
if (y_len && y[y_len - 1] == 0) -- y_len;

将其插入您的power 例程中,它会正常工作。请注意,您的power 方法在y 中是对数的,因此y 可以达到10^1000 的事实并不会使其变得难以管理。

【讨论】:

  • 那么x^y是怎么计算的呢?还是我理解错了?大的 int 部分怎么样?
  • 我的回答的简短摘要是“您不需要将x 视为bigint,可以按照您已经这样做的方式取模z。您确实需要对待y 作为一个 bigint,但是你的 power 例程对 y 做的唯一两件事是 %2 和 /2,如果你将 y 表示为一个数字数组,这两者都是微不足道的“
  • 澄清一下:我建议将y 存储在一个整数数组中,其中每个元素存储一个数字。例如,如果y 是 154,它将表示为数组 {4, 5, 1}。
  • 澄清更多:正如我最后所说,你的解决方案在速度方面已经很好了,你的powery 中是对数的,所以它对y 的工作速度很快到10^1000,所以你唯一需要解决的就是不要把y取模z,而要做到这一点,你需要表示为一个数字数组,并手动实现除法和取模,如所述在答案中
  • 所以我必须将 y 视为一个字符串并使用大 int 字符串操作执行 n/2 吗?好的,我试试看。
【解决方案3】:

我猜你正在尝试构建一个Diffie-Hellman Key Exchange 算法。尝试导入 OpenSSL 库,然后使用它的 BN_mod_exp() 函数。

BN_mod_exp() 计算 a 的 p 次幂模 m (r=a^p % m)。这个函数比 BN_exp() 使用的时间和空间更少。

来源:https://www.openssl.org/docs/manmaster/crypto/BN_add.html

【讨论】:

    【解决方案4】:

    感谢 @v7d8dpo4 对 Euler 的 Totient 函数的解释。 我编辑了我的代码如下:

    #define MOD z
    
    long long power (long long k, long long n) {
        if (n == 1) return k;
        else {
            long long p = power (k, n/2);
            if (n % 2 == 0) return (p * p) % MOD;
            else return (((p * p) % MOD) * k) % MOD;
        }
    }
    
    long long convert (char *n, int mod) {
        long long number = 0;
        int ln = strlen (n);
    
        for (int x = 0; x < ln; x++) {
            number = number * 10;
            number = (number + (n[x] - '0')) % mod;
        }
    
        return number % mod;
    }
    
    int main () {
        char s_x[1111], s_y[1111];
        scanf ("%s %s", s_x, s_y);
    
        long long x, y, r;
        x = convert (s_x, MOD);
        y = convert (s_y, totient (MOD)); // totient (x) is Euler's Totient Function of x
        r = power (x, y);
    
        printf ("%lld\n", r);
    }
    

    【讨论】:

      猜你喜欢
      • 2017-04-03
      • 1970-01-01
      • 2020-05-14
      • 2014-08-21
      • 2016-01-21
      • 2011-01-13
      • 1970-01-01
      • 2017-07-28
      相关资源
      最近更新 更多