大整数模幂运算

时间:2016-10-05 04:37:12

标签: c modulus exponentiation

如何用1计算(x y )mod z &lt; = x,y&lt; = 10 1000 并且z任何正整数1 <= z <1。 2 31

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

我知道这是错误的,因为(x y )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);
}

4 个答案:

答案 0 :(得分:2)

由于模块化指数的使用非常多,因此有适用于它的库。以下是使用GMP读取a,b和c并输出 b 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编译。

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

答案 1 :(得分:1)

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

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

因此,按照您的方式转换x是可以的,唯一的问题是y。方便的是,你只用y做两件事 - 你把它除以2,然后在除以2之后检查余数。如果将y表示为数组,那么这两件事都是微不足道的。首先,为了简单起见,反转y,以便最低有效数字首先出现,并且还存储数字,而不是数组中的数字字符(如存储5,不是&#39; 5&#39;)。您可能还考虑在每个元素中存储多于一个数字,但这只会通过常量来改进它。

现在检查余数,只检查数组的第一个元素是否可被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这一事实并不会使其无法缓慢发挥作用。

答案 2 :(得分:0)

我猜您正在尝试构建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

答案 3 :(得分:0)

感谢@ 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);
}