2

How to calculate (xy) mod z with 1 <= x, y <= 101000 and z any positive integer 1 <= z < 231 ?

What I have done so far is: scan x and y as a string, get the modulo, then calculate (xy) mod z.

I know this is wrong because (xy) mod z is not equal to ((x mod z)(y mod z)) mod z. Then how do I solve this?

Edit: sorry I made the bottom constraint of x and y so high when creating the question. I just want to make other focus on the big integer problem, not the modular exponentiation :).

#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);
}
Soleil
  • 6,404
  • 5
  • 41
  • 61
Ronald Sumbayak
  • 135
  • 2
  • 11

4 Answers4

3

Since modular exponentiaion is used so much, there are libraries for it. The following is an example that reads a, b, and c and outputs ab mod c using GMP.

#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;
}

Compile it with -lgmp.

By the way, ab ≡ ab mod Φ(c) (mod c), where Φ is Euler's totient function

v7d8dpo4
  • 1,399
  • 8
  • 9
1

First, I assume that z is rather small (as in, fits into long). Also observe that

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

So it is fine to convert x the way you do, the only problem is y. Conveniently, you only do two things with y -- you divide it by two, and you check the remainder after division by two. Both of those things are trivial if you represent y as an array. First, for simplicity reverse the y, so that the least significant digit goes first, and also store digits, not digit characters in the array (as in, store 5, not '5'). You might also consider storing more than just one digit in each element, but this only improves it by a constant.

Now to check the remainder just check if the first element of the array is divisible by two (the number is even if its least significant digit is even). To divide by two, do something along the lines of:

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;

Plug this into your power routine, and it will work just fine. Note that your power method is logarithmic in y, so the fact that y can be up to 10^1000 doesn't make it unmanageably slow.

Ishamael
  • 12,583
  • 4
  • 34
  • 52
  • So that is how to calculate x^y? Or I get it wrong? How about the big int part? – Ronald Sumbayak Oct 05 '16 at 05:13
  • Short summary of my answer is "you don't need to treat `x` as bigint, it is OK to take it modulo `z`, the way you already do. You do need to treat `y` as a bigint, however the only two things your `power` routine does to `y` is %2 and /2, both of which are trivial if you represent `y` as an array of digits" – Ishamael Oct 05 '16 at 05:15
  • To clarify: I'm suggesting to store `y` in an array of integers, where each elements stores a single digit. For instance, if `y` is 154, it will be represented as an array {4, 5, 1}. – Ishamael Oct 05 '16 at 05:17
  • To clarify more: as I say at the end, your solution is already good in terms of speed, your `power` is logarithmic in `y`, so it will work fast for `y` up to `10^1000`, so the only thing you need to fix is not to take `y` modulo `z`, and to do that you need to represent is as an array of digits, and implement division and modulo by hand, as described in the answer – Ishamael Oct 05 '16 at 05:22
  • So i have to treat y as a string and do the n/2 with big int string operation? Ok, I'll try it. – Ronald Sumbayak Oct 05 '16 at 06:12
1

I guess you are trying to build a Diffie-Hellman Key Exchange algorithm. Try importing the OpenSSL library and then use it's BN_mod_exp() function.

BN_mod_exp() computes a to the p-th power modulo m (r=a^p % m). This function uses less time and space than BN_exp().

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

Taha Paksu
  • 15,371
  • 2
  • 44
  • 78
0

Thanks to @v7d8dpo4 explanation for Euler's Totient Function. I edited my code as of following:

#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);
}
Ronald Sumbayak
  • 135
  • 2
  • 11