views:

1603

answers:

12

I need to be able to calculate (a^b) % c for very large values of a and b (which individually are pushing limit and which cause overflow errors when you try to calculate a^b). For small enough numbers, using the identity (a^b)%c = (a%c)^b%c works, but if c is too large this doesn't really help. I wrote a loop to do the mod operation manually, one a at a time:

private static long no_Overflow_Mod(ulong num_base, ulong num_exponent, ulong mod) 
    {
        long answer = 1;
        for (int x = 0; x < num_exponent; x++)
        {
            answer = (answer * num_base) % mod;
        }
        return answer;
    }

but this takes a very long time. Is there any simple and fast way to do this operation without actually having to take a to the power of b AND without using time-consuming loops? If all else fails, I can make a bool array to represent a huge data type and figure out how to do this with bitwise operators, but there has to be a better way.

+1  A: 

Short of writing your own fast modular exponentiation, the simplest idea I can come up with, is to use the F# BigInt type: Microsoft.FSharp.Math.Types.BigInt which supports operations with arbitrarily large scale - including exponentiation and modular arithmetic.

It's a built-in type that will be part of the full .NET framework with the next release. You don't need to use F# to use BitInt - you can make use of it directly in C#.

LBushkin
+1  A: 

I'd recommend checking over the Decimal documentation and seeing if it meets your requirements since it is a built in type and can use the mod operator. If not then you're going to need an arbitrary precision library like java's Bignum.

Qberticus
Not using Java. C#.
Malfist
no shit, i was saying like, as in he would need to find an equivalent. way to not read.
Qberticus
He said *like* Java's Bignum. Not *use" Java's Bignum. Given that he knows about C#'s decimal type, it's probably a good bet that he knows the question is about C#.
Tim Lesher
A: 

Can you factor a, b, or c? Does C have a known range?

These are 32 bit integers! Go check this site

For instance, here is how you get the mod of n%d where d 1>>s (1,2,4,8,...)

  int n = 137;     // numerator
  int d = 32;      // denom d will be one of: 1, 2, 4, 8, 16, 32, ...
  int m;           // m will be n % d
  m = n & (d - 1);

There is code for n%d where d is 1>>s - 1 (1, 3, 7, 15, 31, ...)

This is only going to really help if c is small though, like you said.

johnnycrash
do you mean, "where d is 1 << (s-1)"?
Nathan Fellman
yeah. I can't even copy from another website without making a mistake!
johnnycrash
+6  A: 

Fast Modular Exponentiation (I think that's what it's called) might work.

Given a, b, c and a^b (mod c):

1. Write b as a sum of powers of 2. (If b=72, this is 2^6 + 2^3 )
2. Do:
    (1) a^2 (mod c) = a*
    (2) (a*)^2 (mod c) = a*
    (3) (a*)^2 (mod c) = a*
    ...
    (n) (a*)^2 (mod c) = a*

3. Using the a* from above, multiply the a* for the powers of 2 you identified. For example:
    b = 72, use a* at 3 and a* at 6.
    a*(3) x a*(6) (mod c)

4. Do the previous step one multiplication at a time and at the end, you'll have a^b % c.

Now, how you're going to do that with data types, I don't know. As long as your datatype can support c^2, i think you'll be fine.

If using strings, just create string versions of add, subtract, and multiply (not too hard). This method should be quick enough doing that. (and you can start step 1 by a mod c so that a is never greater than c).

EDIT: Oh look, a wiki page on Modular Exponentiation.

apandit
+1  A: 

You could try this:

C#: Doing a modulus (mod) operation on a very large number (> Int64.MaxValue)
http://www.del337ed.com/blog/index.php/2009/02/04/c-doing-a-modulus-mod-operation-on-a-very-large-number-int64maxvalue/

ChristopheD
+1  A: 

You can try factoring 'a' into sufficiently small numbers.

If the factors of 'a' are 'x', 'y', and 'z', then

a^b = (x^b)(y^b)(z^b).

Then you can use your identity: (a^b)%c = (a%c)^b%c

Robert Cartaino
Except that factoring is a much more difficult problem than modulus
Eclipse
I would just start out dividing a/2 in a loop until it didn't divide evenly.... Then a/3, etc with prime numbers until 'a' was "sufficiently small" for a^b not to overflow. Then complete the operation as outlined by the original poster.
Robert Cartaino
+1  A: 

It seems to me like there's some kind of relation between power and mod. Power is just repeated multiplication and mod is related to division. We know that multiplication and division are inverses, so through that connection I would assume there's a correlation between power and mod.

For example, take powers of 5:

5 % 4 = 1
25 % 4 = 1
125 % 4 = 1
625 % 4 = 1
...

The pattern is clear that 5 ^ b % 4 = 1 for all values of b.

It's less clear in this situation:

5 % 3 = 2
25 % 3 = 1
125 % 3 = 2
625 % 3 = 1
3125 % 3 = 2
15625 % 3 = 1
78125 % 3 = 2
...

But there's still a pattern.

If you could work out the math behind the patterns, I wouldn't be surprised if you could figure out the value of the mod without doing the actual power.

Kai
The pattern is obvious and well-known. It's Fermat's Little Theorem (and the identity is used in the OP).
Matthew Flaschen
Ah yes, you're right, thanks for pointing me to it. It's only obvious if you've considered this kind of problem before and I'm grateful to have derived the idea myself :)
Kai
Where is Fermat's little theorem used in the OP?
Tobias
Sorry, I spoke too quick. This isn't Fermat. Both Kai's post and the OP are basic modular multiplicatioon.
Matthew Flaschen
A: 

Python has pow(a,b,c) which returns (a**b)%c (only faster), so there must be some clever way to do this. Maybe they just do the identity you mentioned.

Nosredna
A: 

Looks like homework in cryptography.

Hint: check out Fermat's little theorem.

Tobias
+8  A: 

I guess you are looking for : http://en.wikipedia.org/wiki/Montgomery_reduction or the simpler way based on Modular Exponentiation (from wikipedia)

Bignum modpow(Bignum base, Bignum exponent, Bignum modulus) {

    Bignum result = 1;

    while (exponent > 0) {
        if ((exponent & 1) == 1) {
            // multiply in this bit's contribution while using modulus to keep result small
            result = (result * base) % modulus;
        }
        // move to the next bit of the exponent, square (and mod) the base accordingly
        exponent >>= 1;
        base = (base * base) % modulus;
    }

    return result;
}
Ryan Oberoi
If the modulus is smaller than the exponent, I think you could improve this algorithm by taking the modulus of the count of 1 bits of the exponents and only do that many loop iterations. One could actually do even better than that in some cases. See Kai's answer.
Brian
i think you can break out whenever result = 0 result = (result * base) % modulus; if (result == 0) break;
johnnycrash
The formatting screwed up my comment. Add "if (result == 0) break;" after the calculation of result.
johnnycrash
Actually before you mutliply result*base, check base==0 and break then.
johnnycrash
You can break out at the bottom after "exponent >>= 1;" if exponent == 0 and avoid having to do the last mulitply and mod.
johnnycrash
+3  A: 

Here's an example of Fast Modular Exponentiation (suggested in one of the earlier answers) in java. Shouldn't be too hard to convert that to C#

http://www.math.umn.edu/~garrett/crypto/a01/FastPow.html

and the source...

http://www.math.umn.edu/~garrett/crypto/a01/FastPow.java

Jason Braucht
A: 

.NET in general has poor performance for simple math operations. I'd actually look into adding an unmanaged dll into your project.

The GMP library is probably a good candidate as it's fast and has well optimized big integer support... http://gmplib.org/

In fact, I think GMP would support everything you need to do natively, and would be much, much faster than anything you'd write in .NET. I'd just try porting your C# example into an unmanaged C++ project that uses GMP. Then build the C++ project and add it as a reference in your .NET project. You'd end up with a kind of hybrid half managed, half unmanaged application but for heavy math operations like this it can be worth it.

Steve Wortham