13

I saw the following interview question on some online forum. What is a good solution for this?

Get the last 1000 digits of 5^1234566789893943

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
J.W.
  • 17,991
  • 7
  • 43
  • 76
  • as mentioned in some answers here modpow is your answer just need to add, `x=modpow(5,1234566789893943,10^1000)` and take in mind the result is `~3322 bit` long (log10(10^1000)/log10(2)=1000/0.30103) so use bigint or string numbers with big enough bits/digis to avoid overflows during computation – Spektre Jul 18 '14 at 07:09
  • few use-able links for this task fast sqr: http://stackoverflow.com/q/18465326/2521214 , NTT+modpow: http://stackoverflow.com/q/18577076/2521214 and if you want to use binary number representation then also fast dec<->hex conversions: http://stackoverflow.com/a/18231860/2521214 – Spektre Jul 18 '14 at 07:22
  • 1
    Hint: [Exponentiation by squaring](http://en.wikipedia.org/wiki/Exponentiation_by_squaring) – fahrbach Jul 18 '14 at 03:32
  • Solve the congruence system x = 5^443 (mod 2^1000) and x = 0 (mod 5^1000) using the Chinese remainder theorem. – tmyklebu Jul 18 '14 at 09:46
  • 1
    If you can't solve the problem on paper, then you can't solve the problem with a computer. Think. – Colonel Panic Jul 23 '14 at 08:48

8 Answers8

12

Simple algorithm:

1. Maintain a 1000-digits array which will have the answer at the end
2. Implement a multiplication routine like you do in school. It is O(d^2).
3. Use modular exponentiation by squaring.

Iterative exponentiation:

array ans;
int a = 5;

while (p > 0) {

    if (p&1) {

       ans = multiply(ans, a)
    }

    p = p>>1;

    ans = multiply(ans, ans);
}

multiply: multiplies two large number using the school method and return last 1000 digits.

Time complexity: O(d^2*logp) where d is number of last digits needed and p is power.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vikram Bhat
  • 6,106
  • 3
  • 20
  • 19
  • 2
    How long do you think it will take to do this computation even on the fastest computer? – Ivaylo Strandjev Jul 18 '14 at 17:31
  • @IvayloStrandjev its is O(d^2*logp) which can be optimized using packing into integer but it can only be bettered using karatsuba multiplication O(d^1.5*logp) which is more complex so i think it fairly fast and simple for a solution to interview – Vikram Bhat Jul 18 '14 at 17:38
  • 1
    Take a look at my answer. The computation can be significantly optimized but requires harder theory. My point is that even on the fastest computer this approach would take many,many **centuries** – Ivaylo Strandjev Jul 18 '14 at 17:41
  • @IvayloStrandjev sorry but my solution is generic and would evaluate for any (a,p) pair without assumption . Moreover it wont take centuries as you claim for above example it takes about 1000^2*log(1234566789893943) iterations which is 5*10^7 would happen within seconds on reasonable computer – Vikram Bhat Jul 18 '14 at 17:47
  • Ahh sorry my mistake I have computed the number of operations incorrectly. I will remove my answer as it is far too complicated(although still general enough) – Ivaylo Strandjev Jul 18 '14 at 17:50
  • @IvayloStrandjev no need for removing your answer i think it is very smart. – Vikram Bhat Jul 18 '14 at 17:52
  • 1
    Actually you are right. I will keep it as it works for way higher exponents. Still your answer is more appropriate – Ivaylo Strandjev Jul 18 '14 at 17:53
  • 1
    Just one comment on the "school multiplication". Here we can cut the corners as we are not interested in the first 1000 digits (1000x1000->2000 digit multiplication). This cuts the time by half. – DrV Jul 18 '14 at 23:48
6

A typical solution for this problem would be to use modular arithmetic and exponentiation by squaring to compute the remainder of 5^1234566789893943 when divided by 10^1000. However in your case this will still not be good enough as it would take about 1000*log(1234566789893943) operations and this is not too much, but I will propose a more general approach that would work for greater values of the exponent.

You will have to use a bit more complicated number theory. You can use Euler's theorem to get the remainder of 5^1234566789893943 modulo 2^1000 a lot more efficiently. Denote that r. It is also obvious that 5^1234566789893943 is divisible by 5^1000.

After that you need to find a number d such that 5^1000*d = r(modulo 2^1000). To solve this equation you should compute 5^1000(modulo 2^1000). After that all that is left is to do division modulo 2^1000. Using again Euler's theorem this can be done efficiently. Use that x^(phi(2^1000)-1)*x =1(modulo 2^1000). This approach is way faster and is the only feasible solution.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
2

The technique we need to know is exponentiation by squaring and modulus. We also need to use BigInteger in Java.

Simple code in Java:

BigInteger m = //BigInteger of 10^1000

BigInteger pow(BigInteger a, long b) {
   if (b == 0) {
      return BigInteger.ONE;
   }
   BigInteger val = pow(a, b/2);
   if (b % 2 == 0)
       return (val.multiply(val)).mod(m);
   else
       return (val.multiply(val).multiply(a)).mod(m);
}

In Java, the function modPow has done it all for you (thank Java).

phuclv
  • 37,963
  • 15
  • 156
  • 475
Pham Trung
  • 11,204
  • 2
  • 24
  • 43
2

The key phrase is "modular exponentiation". Python has that built in:

Python 3.4.1 (v3.4.1:c0e311e010fc, May 18 2014, 10:38:22) [MSC v.1600 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> help(pow)
Help on built-in function pow in module builtins:

pow(...)
    pow(x, y[, z]) -> number

    With two arguments, equivalent to x**y.  With three arguments,
    equivalent to (x**y) % z, but may be more efficient (e.g. for ints).

>>> digits = pow(5, 1234566789893943, 10**1000)
>>> len(str(digits))
1000
>>> digits
4750414775792952522204114184342722049638880929773624902773914715850189808476532716372371599198399541490535712666678457047950561228398126854813955228082149950029586996237166535637925022587538404245894713557782868186911348163750456080173694616157985752707395420982029720018418176528050046735160132510039430638924070731480858515227638960577060664844432475135181968277088315958312427313480771984874517274455070808286089278055166204573155093723933924226458522505574738359787477768274598805619392248788499020057331479403377350096157635924457653815121544961705226996087472416473967901157340721436252325091988301798899201640961322478421979046764449146045325215261829432737214561242087559734390139448919027470137649372264607375942527202021229200886927993079738795532281264345533044058574930108964976191133834748071751521214092905298139886778347051165211279789776682686753139533912795298973229094197221087871530034608077419911440782714084922725088980350599242632517985214513078773279630695469677448272705078125
>>> 
Paddy3118
  • 4,704
  • 27
  • 38
1

Use congruence and apply modular arithmetic. Square and multiply algorithm. If you divide any number in base 10 by 10 then the remainder represents the last digit. i.e. 23422222=2342222*10+2

So we know:

5=5(mod 10)
5^2=25=5(mod 10)  
5^4=(5^2)*(5^2)=5*5=5(mod 10)
5^8=(5^4)*(5^4)=5*5=5(mod 10)

... and keep going until you get to that exponent

OR, you can realize that as we keep going you keep getting 5 as your remainder.

phuclv
  • 37,963
  • 15
  • 156
  • 475
Peter Sun
  • 21
  • 1
0

Convert the number to a string.

Loop on the string, starting at the last index up to 1000.

Then reverse the result string.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
bumbumpaw
  • 2,522
  • 1
  • 24
  • 54
  • and how you can calculate a number which must be stored at least on ~3000 (three thousands) bits? can we use a 64bits `double` or a 32bits `float`? I'm so confused, but the 64 bits are a bit closer to 3000 bits, than 32 bits, I guess... – holex Jul 18 '14 at 09:52
0

I posted a solution based on some hints here.

#include <vector>
#include <iostream>

using namespace std;

vector<char> multiplyArrays(const vector<char> &data1, const vector<char> &data2, int k) {
    int sz1 = data1.size();
    int sz2 = data2.size();
    vector<char> result(sz1+sz2,0);

    for(int i=sz1-1; i>=0; --i) {
        char carry = 0;
        
        for(int j=sz2-1; j>=0; --j) {
            char value = data1[i] * data2[j]+result[i+j+1]+carry;  
            
            carry = value/10;            
            result[i+j+1] = value % 10;
        }

        result[i]=carry;
    }
    if(sz1+sz2>k){
        vector<char> lastKElements(result.begin()+(sz1+sz2-k), result.end());
        return lastKElements;
    }
    else
        return result;
}

vector<char> calculate(unsigned long m, unsigned long n, int k) {
    if(n == 0) {
        return vector<char>(1, 1);
    } else if(n % 2) { // odd number
        vector<char> tmp(1, m);
        vector<char> result1 = calculate(m, n-1, k);
        return multiplyArrays(result1, tmp, k);
    } else {
        vector<char> result1 = calculate(m, n/2, k);
        return multiplyArrays(result1, result1, k);
    }
}

int main(int argc, char const *argv[]){


    vector<char> v=calculate(5,8,1000);
    for(auto c : v){
        cout<<static_cast<unsigned>(c);
    }

}
phuclv
  • 37,963
  • 15
  • 156
  • 475
J.W.
  • 17,991
  • 7
  • 43
  • 76
-2

I don't know if Windows can show a big number (Or if my computer is fast enough to show it) But I guess you COULD use this code like and algorithm:

ulong x = 5; //There are a lot of libraries for other languages like C/C++ that support super big numbers. In this case I'm using C#'s default `Uint64` number.
for(ulong i=1; i<1234566789893943; i++)
{
    x = x * x; //I will make the multiplication raise power over here
}
string term = x.ToString(); //Store the number to  a string. I remember strings can store up to 1 billion characters.

char[] number = term.ToCharArray(); //Array of all the digits
int tmp=0;
while(number[tmp]!='.') //This will search for the period.
tmp++;

tmp++; //After finding the period, I will start storing 1000 digits from this index of the char array

string thousandDigits = ""; //Here I will store the digits.

for (int i = tmp; i <= 1000+tmp; i++)
{
    thousandDigits += number[i]; //Storing digits
}

Using this as a reference, I guess if you want to try getting the LAST 1000 characters of this array, change to this in the for of the above code:

string thousandDigits = "";

for (int i = 0; i > 1000; i++)
{
    thousandDigits += number[number.Length-i]; //Reverse array... ¿?
}

As I don't work with super super looooong numbers, I don't know if my computer can get those, I tried the code and it works but when I try to show the result in console it just leave the pointer flickering xD Guess it's still working. Don't have a pro Processor. Try it if you want :P

phuclv
  • 37,963
  • 15
  • 156
  • 475
c_str
  • 369
  • 1
  • 4
  • 14
  • 2
    This is wrong. 5^1234566789893943 is way bigger than 2^64. so it will overflow in a 64 bit int. – Martín Fixman Jul 24 '14 at 15:09
  • @MartínFixman I think you have problems of attention. At least did you read what I put before the code? lol – c_str Jul 25 '14 at 19:36
  • There's simply no computer that can handle a ~536165540000000 bit int, necessary to compute 5^1234566789893943. – Martín Fixman Jul 26 '14 at 20:39
  • Again. Did you read the FIRST paragraph of what I said? @MartínFixman – c_str Jul 27 '14 at 00:29
  • 2
    "I don't know if Windows can show a big number (Or if my computer is fast enough to show it) But I guess you COULD use this code like and algorithm:"? Windows can't show such a big number, your computer isn't fast enough to show it, and OP couldn't use this algorithm. – Martín Fixman Jul 27 '14 at 02:07
  • @MartínFixman again... Did you learn to read? *you COULD use this code like and algorithm* As I said, *LIKE AN ALGORITHM*... Damn... You programmers are so fcked up when reading... You don't know to read lol – c_str Aug 16 '14 at 03:36