1

So i'm supposed to find out the last 10 digits of 2^n(0<=n<=100) where n is the input. I found a method to handle large numbers but the program fails when n>64. Any leads on how to go about with this would be appreciated.

#include<stdio.h>
#include<math.h>


/* Iterative Function to calculate (x^y)%p in O(log y) */
int power(long long int x, long long int y, long long int p)
{
long long int res = 1; // Initialize result

x = x % p; // Update x if it is more than or
// equal to p

while (y > 0) {

    // If y is odd, multiply x with result
    if (y & 1)
        res = (res * x) % p;

    // y must be even now
    y = y >> 1; // y = y/2
    x = (x * x) % p;
}
return res;
}

// C function to print last 10 digits of a^b
void printLastDigits(long long int a,long long int b)
{    

long long int temp = pow(10,10);

// Calling modular exponentiation
temp = power(a, b, temp);

if (temp)
    printf("%d",temp);
}

int main()
{
long long int n;
scanf("%d",&n);
printLastDigits(2,n);
return 0;
}
Reddy90
  • 79
  • 6
  • 2
    This is a test of your observational skills. You're probably being expected to find patterns (as there is a pattern) and apply your knowledge to extrapolate (so you don't need to calculate `2^n`). – Adi219 Jan 14 '18 at 10:49
  • The idea of using modpow works, however your modpow does not work, due to the infamous "early overflow" of `x * x` (and `res * x`), before you get a chance to reduce it modulo `p`. – harold Jan 14 '18 at 10:51
  • @harold So is there a way around that? – Reddy90 Jan 14 '18 at 10:59
  • An easy way is to multiply 1 by 2, n times while discarding digits above the last 10. You could also find a pattern for each of the 10 last digits in powers of 2, but it involves carries. – Nelfeal Jan 14 '18 at 11:05
  • For `n <= 100` exponentiation by squaring is not that critical, you can easily multiply by 2^20 (max 5 steps, so still very fast) without premature overflow – harold Jan 14 '18 at 11:06
  • You could use a string of max 10 digits and miltiply each digit (using carry) in the string with 2 each time. – Paul Ogilvie Jan 14 '18 at 11:10
  • Using `unsigned long long` math is sufficient to solve this task. No need for int128, bignum nor strings. – chux - Reinstate Monica Jan 14 '18 at 13:56
  • Stack Overflow is a question-and-answer archive for programming problems, not a code-writing service for lazy people. – Toby Speight Feb 15 '18 at 16:02

4 Answers4

1

You don't need to worry about the 'high' bits, since multiplication by 2 left shifts them out of range of the lower part of the product you're interesting in. Just be sure you're using the unsigned long long type (of at least 64 bits) to hold integral types that are wide enough, e.g.,

#include <inttypes.h>
#include <stdio.h>

void low_digits (unsigned int n)
{
    unsigned long long base = 2, modulus = 10000000000ULL;

    for (unsigned int i = 1; i <= n; i++)
    {
        fprintf(stdout, "2^%u mod 10^10 = %llu\n", i, base);
        base = (base * 2) % modulus;
    }
}

You can test 2^1000 with a bignum calculator:

10715086071862673209484250490600018105614048117055336074437503883703\ 51051124936122493198378815695858127594672917553146825187145285692314\ 04359845775746985748039345677748242309854210746050623711418779541821\ 53046474983581941267398767559165543946077062914571196477686542167660\ 429831652624386837205668069376

while n = 1000 above yields: 5668069376


Others have noted, that this is a naive method, and modular exponentiation is far more efficient for sufficiently large values of (n). Unfortunately, this is going to require products that exceed the range of an unsigned 64-bit value, so unless you're prepared to implement [hi64][lo64] multi-precision mul / mod operations, it's probably beyond the scope of your task.

Fortunately, later versions of gcc and clang do provide an extended 128 bit integral type:

#include <inttypes.h>
#include <stdio.h>

void low_digits (unsigned int n)
{
    unsigned long long base = 2, modulus = 10000000000ULL;
    __extension__ unsigned __int128 u = 1, w = base;

    while (n != 0)
    {
        if ((n & 0x1) != 0)
            u = (u * w) % modulus; /* (mul-reduce) */

        if ((n >>= 1) != 0)
            w = (w * w) % modulus; /* (sqr-reduce) */
    }

    base = (unsigned long long) u;
    fprintf(stdout, "2^%u mod 10^10 = %llu\n", n, base);
}
Brett Hale
  • 21,653
  • 2
  • 61
  • 90
  • This is the basic iteration method, times x each time. – phy nju Jan 14 '18 at 11:55
  • This works slightly better if you cast to 128bit for the multiplication, and then let it go back to 64bit again - that way the compiler uses a faster 64x64->128 multiplication instead of a 128x128->128 multiplication (wasting multiplications on the upper halves, which are zero) – harold Jan 14 '18 at 12:28
  • @harold - true, but I think it's better to show the mod-exp algorithm without excessive casting. I don't think the compiler is smart enough to know that successive division always fits in a word, however, and the run-time is dominated by a call to something like `__umodti3` - e.g., you don't get a simple `divq` instruction on x86-64. – Brett Hale Jan 14 '18 at 12:43
1

The following uses strings to perform the multiplication:

void lastdigits(char digits[11], int n)
{
    int i, j, x, carry;
    for (i=0; i<n;i++) {
        for (j=9, carry=0; j>=0; j--) {
            x= digits[j]-'0';
            x *= 2;
            x += carry;
            if (x>9) {carry= 1; x -= 10;}
            else carry= 0;
            digits[j]= x+'0';
        }
    }
}

void test(void)
{
    char digits[11];

    strcpy(digits,"0000000001");
    lastdigits(digits,10);
    printf("%s\n",digits);

    strcpy(digits,"0000000001");
    lastdigits(digits,20);
    printf("%s\n",digits);

    strcpy(digits,"0000000001");
    lastdigits(digits,100);
    printf("%s\n",digits);
}

Output:

0000001024
0001048576
6703205376
Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
1

Since the other answers you've received don't actually show what you're doing wrong:

x = (x * x) % p;

You're assuming that x * x still fits in long long int. But if x is 0x100000000 (4294967296, for 10 decimal digits) and long long int is 64 bits, then it will not fit.

Either:

You need a way to accurately multiply two arbitrary 10-digit numbers. The result may have 20 digits and may not fit in long long int or even unsigned long long int. This means you'd need to use some bigint library or implement something like that yourself.

Or:

You need to avoid multiplying multiple possibly-10-digit numbers.

The answer you've accepted opts for simple repeated multiplication by 2. This is sufficient for your problem now, but beware that this does significantly increase the complexity if you want to allow very large exponents.

1

Let's say you are finding the last digit of 2^n, you just need to consider last digit and ignore every other digit

1. 2*2 = 4
2. 4*2 = 8
3. 8*2 = 16 (ignore last-but-one digit i.e 1)
4. 6*2 = 12 (ignore last-but-one digit i.e 1)
5. 2*2 = 4
6. 4*2 = 8
7. 8*2 = 16 (ignore last-but-one digit i.e 1)
8. 6*2 = 12 (ignore last-but-one digit i.e 1)
9. 2*2 = 4

... n-1 iterations

To find the last 2 digits of 2^n, ignore all digits except last 2 digits.

1. 2*2 = 4
2. 4*2 = 8
3. 8*2 = 16
4. 16*2 = 32
5. 32*2 = 64
6. 64*2 = 128 (Consider last 2 digits)
7. 28*2 = 56
8. 56*2 = 112 (Consider last 2 digits)
9. 12*2 = 24

... n-1 iterations

Similarly, to find the last 10 digits of 2^n, consider just last 10 digits at each iteration and repeat it for n-1 iterations.

Note:

With this approach, the biggest number you'll get during the calculation can be of 11 digits ~ 10^11, while for a 64-bit machine the max value is ~ 2^64 = ~ 10^18

Saketh Katari
  • 332
  • 2
  • 11