Как найти num в данном случае, чтобы в конце она имела тип int?

Как найти num в данном случае, чтобы в конце она имела тип int?

import java.math.BigInteger;

public class Decode {
    public static void main(String[] args) {
        int n = 143;
        BigInteger num = BigInteger.valueOf((long)Math.pow(42, 103));
        num = num % n;
        num = (int)num;
    }
}

пробовал с long, но он не подошел к таким большим числам:

int n = 143;
long num = (long)Math.pow(42, 103);
System.out.println(num); // 9223372036854775807 long не достаточно
num = num % n;
num = (int)num;

Ответы (3 шт):

Автор решения: Артём Оконечников

Math.pow возвращает double. Числа с плавающей запятой могут представить гораздо большее значение, чем целочисленное. Получить из double int без усечения не получится. Зато получится сразу получить BigInteger:

BigInteger base = new BigInteger("42");
BigInteger result = base.pow(103);

System.out.println(result);

После уточнения вопроса:

BigInteger n = new BigInteger("143");
BigInteger num = new BigInteger("42");
BigInteger pow = num.pow(103);
BigInteger mod = pow.mod(n);

System.out.println(mod); // Можно mod.intValue()
→ Ссылка
Автор решения: pazukdev
int intNum = new BigInteger("42").pow(103).remainder(new BigInteger("143")).intValue();
→ Ссылка
Автор решения: Stanislav Volodarskiy

Почему 42^103 не помещается в long? long может представлять числа до 2^63, а наша степень требует log2(42^103) = log2(42) * 103 > 555 бит.

В double 42^103 помещается. Но double точно представляет только 53 старших бита числа. Остальные 502 бит отброшены. А когда спрашивают про остаток от деления, то спрашивают в каком-то смысле про младшие биты. А они-то и отброшены.

Вариант с BigInteger решает задачу точно. Но задачу можно решить быстрее и без BigInteger.

Последнее действие в решении - взятие остатка по модулю. Правило умножения по модулю позволяет умножать не сами числа а их остатки:

(a * b) mod m = ((a mod m) * (b mod m)) mod m

Тогда цикл возведения в степень из

int pow = 1;
for (int i = 0; i < p; ++i) {
    pow *= b;
}

превращается в

int pow = 1;
for (int i = 0; i < p; ++i) {
    pow = (pow * b) % m;
}

Кажется что всё только усложнилось, но теперь нам не грозит переполнение. А так как мы умножаем "короткие" целые (int) а не "длинные" ('BigInteger`), то все работает намного быстрее.

Вариант с BigInteger:

import java.math.BigInteger;

public class Decode1 {
    public static void main(String[] args) {
        BigInteger m = new BigInteger(args[0]);
        BigInteger b = new BigInteger(args[1]);
        int p = Integer.parseInt(args[2]);

        System.out.println(b.pow(p).mod(m));
    }
}

Вариант с int и умножением по модулю:

public class Decode2 {
    public static int pow(int m, int b, int p) {
        int pow = 1;
        for (int i = 0; i < p; ++i) {
            pow = (pow * b) % m;
        }
        return pow;
    }

    public static void main(String[] args) {
        int m = Integer.parseInt(args[0]);
        int b = Integer.parseInt(args[1]);
        int p = Integer.parseInt(args[2]);

        System.out.println(pow(m, b, p));
    }
}

Сравним времена вычислений. Decode1 замедляется сильнее всего. Decode2 работает за время пропорциональное степени.

     power  Decode1  Decode2  Decode3
       103   0.054    0.050    0.059
      1000   0.055    0.053    0.052
     10000   0.070    0.051    0.048
    100000   0.124    0.063    0.050
   1000000   0.679    0.060    0.058
  10000000  12.160    0.138    0.049
 100000000   ?        0.914    0.052
1000000000   ?        8.795    0.050

Decode3 реализует быстрое возведение в степень по модулю. Эта программа работает так быстро, что время в таблице - это время загрузки программы, а исполняется она за время, которое трудно измерить:

public class Decode3 {
    public static int pow(int m, int b, int p) {
        int pow = 1;
        for (; p > 0; p /= 2) {
            if (p % 2 == 1) {
                pow = (pow * b) % m;
            }
            b = (b * b) % m;
        }
        return pow;
    }

    public static void main(String[] args) {
        int m = Integer.parseInt(args[0]);
        int b = Integer.parseInt(args[1]);
        int p = Integer.parseInt(args[2]);

        System.out.println(pow(m, b, p));
    }
}
→ Ссылка