Как вычислить модуль больших чисел?

Как рассчитать модуль 5 ^ 55 по модулю 221 без особого использования калькулятора?

Я предполагаю, что в теории чисел в криптографии есть несколько простых принципов для вычисления таких вещей.


person Priyank Bolia    schedule 01.02.2010    source источник
comment
Вот объяснение: devx.com/tips/Tip/39012   -  person tur1ng    schedule 01.02.2010
comment
ссылка на devx бесполезна, для таких вещей есть другие простые методы в теории чисел, насколько я знаю.   -  person Priyank Bolia    schedule 01.02.2010
comment
близко к чему, вы когда-нибудь читали криптографию?   -  person Priyank Bolia    schedule 01.02.2010
comment
@Priyank Bolia: Не волнуйтесь, вряд ли этот вопрос будет закрыт. Это хороший вопрос. Если он будет закрыт, будет много людей, проголосовавших за его открытие.   -  person jason    schedule 01.02.2010
comment
Да, многие из нас знают, что иногда информатика связана с математикой.   -  person Cascabel    schedule 01.02.2010
comment
Math Overflow — еще одно место, где можно разместить такой вопрос: mathoverflow.net.   -  person JB King    schedule 01.02.2010
comment
@JB King: MathOverflow предназначен для математики на уровне выпускников и выше; этот вопрос был бы неодобрительно там.   -  person jason    schedule 01.02.2010
comment
Связанный: https://www.quora.com/Whats-an-efficient-algorithm-to-convert-the-base-of-a-BIG-number   -  person Pacerier    schedule 22.03.2017


Ответы (10)


Итак, вы хотите вычислить a^b mod m. Сначала мы воспользуемся наивным подходом, а затем посмотрим, как мы можем его усовершенствовать.

Сначала уменьшите a mod m. Это означает, что нужно найти число a1, чтобы 0 <= a1 < m и a = a1 mod m. Затем несколько раз в цикле умножьте на a1 и снова уменьшите mod m. Таким образом, в псевдокоде:

a1 = a reduced mod m
p = 1
for(int i = 1; i <= b; i++) {
    p *= a1
    p = p reduced mod m
}

Делая это, мы избегаем чисел больше m^2. Это ключ. Причина, по которой мы избегаем чисел больше m^2, заключается в том, что на каждом шаге 0 <= p < m и 0 <= a1 < m.

В качестве примера давайте вычислим 5^55 mod 221. Во-первых, 5 уже уменьшено mod 221.

  1. 1 * 5 = 5 mod 221
  2. 5 * 5 = 25 mod 221
  3. 25 * 5 = 125 mod 221
  4. 125 * 5 = 183 mod 221
  5. 183 * 5 = 31 mod 221
  6. 31 * 5 = 155 mod 221
  7. 155 * 5 = 112 mod 221
  8. 112 * 5 = 118 mod 221
  9. 118 * 5 = 148 mod 221
  10. 148 * 5 = 77 mod 221
  11. 77 * 5 = 164 mod 221
  12. 164 * 5 = 157 mod 221
  13. 157 * 5 = 122 mod 221
  14. 122 * 5 = 168 mod 221
  15. 168 * 5 = 177 mod 221
  16. 177 * 5 = 1 mod 221
  17. 1 * 5 = 5 mod 221
  18. 5 * 5 = 25 mod 221
  19. 25 * 5 = 125 mod 221
  20. 125 * 5 = 183 mod 221
  21. 183 * 5 = 31 mod 221
  22. 31 * 5 = 155 mod 221
  23. 155 * 5 = 112 mod 221
  24. 112 * 5 = 118 mod 221
  25. 118 * 5 = 148 mod 221
  26. 148 * 5 = 77 mod 221
  27. 77 * 5 = 164 mod 221
  28. 164 * 5 = 157 mod 221
  29. 157 * 5 = 122 mod 221
  30. 122 * 5 = 168 mod 221
  31. 168 * 5 = 177 mod 221
  32. 177 * 5 = 1 mod 221
  33. 1 * 5 = 5 mod 221
  34. 5 * 5 = 25 mod 221
  35. 25 * 5 = 125 mod 221
  36. 125 * 5 = 183 mod 221
  37. 183 * 5 = 31 mod 221
  38. 31 * 5 = 155 mod 221
  39. 155 * 5 = 112 mod 221
  40. 112 * 5 = 118 mod 221
  41. 118 * 5 = 148 mod 221
  42. 148 * 5 = 77 mod 221
  43. 77 * 5 = 164 mod 221
  44. 164 * 5 = 157 mod 221
  45. 157 * 5 = 122 mod 221
  46. 122 * 5 = 168 mod 221
  47. 168 * 5 = 177 mod 221
  48. 177 * 5 = 1 mod 221
  49. 1 * 5 = 5 mod 221
  50. 5 * 5 = 25 mod 221
  51. 25 * 5 = 125 mod 221
  52. 125 * 5 = 183 mod 221
  53. 183 * 5 = 31 mod 221
  54. 31 * 5 = 155 mod 221
  55. 155 * 5 = 112 mod 221

Таким образом, 5^55 = 112 mod 221.

Теперь мы можем улучшить это, используя возведение в степень путем возведения в квадрат; это известный трюк, в котором мы уменьшаем возведение в степень до требуемого только log b умножения вместо b. Обратите внимание, что с алгоритмом, который я описал выше, возведением в степень путем возведения в квадрат улучшения, вы получите бинарный метод справа налево.

a1 = a reduced mod m
p = 1
while (b > 0) {
     if (b is odd) {
         p *= a1
         p = p reduced mod m
     }
     b /= 2
     a1 = (a1 * a1) reduced mod m
}

Таким образом, поскольку 55 = 110111 в двоичном формате

  1. 1 * (5^1 mod 221) = 5 mod 221
  2. 5 * (5^2 mod 221) = 125 mod 221
  3. 125 * (5^4 mod 221) = 112 mod 221
  4. 112 * (5^16 mod 221) = 112 mod 221
  5. 112 * (5^32 mod 221) = 112 mod 221

Поэтому ответ 5^55 = 112 mod 221. Причина, по которой это работает, заключается в том, что

55 = 1 + 2 + 4 + 16 + 32

так что

5^55 = 5^(1 + 2 + 4 + 16 + 32) mod 221
     = 5^1 * 5^2 * 5^4 * 5^16 * 5^32 mod 221
     = 5 * 25 * 183 * 1 * 1 mod 221
     = 22875 mod 221
     = 112 mod 221

На шаге, где мы вычисляем 5^1 mod 221, 5^2 mod 221 и т. д., мы отмечаем, что 5^(2^k) = 5^(2^(k-1)) * 5^(2^(k-1)), потому что 2^k = 2^(k-1) + 2^(k-1), поэтому мы можем сначала вычислить 5^1 и уменьшить mod 221, затем возвести это в квадрат и уменьшить mod 221, чтобы получить 5^2 mod 221, и т. д.

Приведенный выше алгоритм формализует эту идею.

person jason    schedule 01.02.2010
comment
Ну, в большинстве языков программирования для этого есть встроенный оператор. Например, в языках, производных от C, оператор % является оператором модуля. Таким образом, int p = 625 % 221 назначит 183 p. Вы можете добиться той же функциональности, разделив 625 на 221 как целочисленное деление и получив ответ 2. Затем вы берете 625 - 2 * 221, чтобы получить остаток. В данном случае 625 - 2 * 221 = 183 это ответ. - person jason; 01.02.2010
comment
это занимает слишком много умножения 54 раза, есть ли еще более быстрый метод. - person Priyank Bolia; 01.02.2010
comment
Да, как я описал в абзаце в конце, вы возводите в степень путем возведения в квадрат. - person jason; 01.02.2010
comment
На самом деле вы можете добиться большего успеха, чем возведение в степень путем возведения в квадрат, особенно в случае с большим показателем. Обратите внимание, что вы обнаружили, что 5^16 == 1 (mod 221). Следовательно, 5^k == 5^(k%16) (mod 221). - person Cascabel; 01.02.2010
comment
@Jefromi, есть ли простой способ найти период a^b mod c? Под простым, конечно, я подразумеваю сублогарифмический. - person JSchlather; 01.02.2010
comment
Если вам нужен только порядок одного такого $a$, нет другого способа, кроме итерации. Однако при достаточно больших показателях это все равно будет стоить того (в среднем) или при повторных вычислениях. Я полагаю, что если бы вы хотели получить заказы всех $a$, вы могли бы что-нибудь придумать... - person Cascabel; 01.02.2010
comment
@Jason: вы написали: Сначала уменьшите мод m. Это означает, что нужно найти число a1 такое, что 0 ‹= a1 ‹ m и a = a1 mod m. Похоже, последнее уравнение содержит опечатку, не должно ли быть a1 = a mod m< /i> вместо этого? - person Timofey; 10.04.2011
comment
@Jason: почему ты используешь упрощенный мод m? Похоже, что мод m прекрасно объясняет, что вы хотите сделать, не так ли? - person Timofey; 10.04.2011
comment
@Jason по большей части, если вы только что добавили ; (и несколько других символов) к вашему псевдокоду, это будет C. - person haneefmubarak; 02.09.2013
comment
я, может быть, немного тупой, но ПОЧЕМУ вы можете применять функцию по модулю на каждом «шаге»? - person pvgoddijn; 04.06.2018

Чтобы добавить к ответу Джейсона:

Вы можете ускорить процесс (что может быть полезно для очень больших показателей), используя двоичное расширение показателя степени. Сначала вычислите 5, 5 ^ 2, 5 ^ 4, 5 ^ 8 по модулю 221 — вы делаете это повторным возведением в квадрат:

 5^1 = 5(mod 221)
 5^2 = 5^2 (mod 221) = 25(mod 221)
 5^4 = (5^2)^2 = 25^2(mod 221) = 625 (mod 221) = 183(mod221)
 5^8 = (5^4)^2 = 183^2(mod 221) = 33489 (mod 221) = 118(mod 221)
5^16 = (5^8)^2 = 118^2(mod 221) = 13924 (mod 221) = 1(mod 221)
5^32 = (5^16)^2 = 1^2(mod 221) = 1(mod 221)

Теперь мы можем написать

55 = 1 + 2 + 4 + 16 + 32

so 5^55 = 5^1 * 5^2 * 5^4 * 5^16 * 5^32 
        = 5   * 25  * 625 * 1    * 1 (mod 221)
        = 125 * 625 (mod 221)
        = 125 * 183 (mod 183) - because 625 = 183 (mod 221)
        = 22875 ( mod 221)
        = 112 (mod 221)

Вы можете видеть, как для очень больших показателей это будет намного быстрее (я считаю, что это логарифм, а не линейный в b, но не уверен).

person Tom Smith    schedule 01.02.2010
comment
это даже лучшее объяснение - person Priyank Bolia; 01.02.2010
comment
Я подозреваю, что на самом деле гораздо быстрее (в общем) избежать возведения в степень путем возведения в квадрат, а вместо этого напрямую искать наименьший показатель степени $k$ такой, что $5^k == 5 (mod 221)$. Это, конечно, зависит от размера экспоненты по сравнению с модулем, но как только у вас есть эта экспонента, вам просто нужно одно вычисление (экспонента по модулю k) и поиск. Обратите внимание, что это определенно лучше, если вам нужно повторить аналогичные вычисления. (В общем случае вы не можете искать $a^k == 1 (mod 221)$, так как это происходит только в том случае, если $a$ и 221 взаимно просты) - person Cascabel; 01.02.2010
comment
ну, нет, обычно нахождение наименьшего показателя степени с этим свойством намного медленнее, чем возведение в квадрат и умножение. Но если вы знаете факторизацию модуля, вы можете легко вычислить лямбда-функцию Кармайкла, которая является множителем вашего k. - person President James K. Polk; 02.02.2010

То, что вы ищете, - это модульное возведение в степень, особенно модульное двоичное возведение в степень. Эта ссылка на википедию содержит псевдокод.

person job    schedule 01.02.2010

китайская теорема об остатках приходит на ум в качестве начальной точки, поскольку 221 = 13 * 17. Итак, перерыв это делится на 2 части, которые в конце объединяются: одна для мода 13 и одна для мода 17. Во-вторых, я считаю, что есть некоторое доказательство того, что a^(p-1) = 1 mod p для всех ненулевых a, что также помогает уменьшите свою проблему, поскольку 5 ^ 55 становится 5 ^ 3 для случая мода 13, поскольку 13 * 4 = 52. Если вы посмотрите на тему «Конечные поля», вы можете найти хорошие результаты о том, как решить эту проблему.

РЕДАКТИРОВАТЬ: причина, по которой я упоминаю факторы, заключается в том, что это создает способ разложения нуля на ненулевые элементы, как если бы вы попробовали что-то вроде 13 ^ 2 * 17 ^ 4 по модулю 221, ответ равен нулю, поскольку 13 * 17 = 221. Многие большие числа не будут простыми, хотя есть способы найти большие простые числа, поскольку они широко используются в криптографии и других областях математики.

person JB King    schedule 01.02.2010
comment
ну, во-первых, я не знаю факториалов, и я пытаюсь доказать, что число является простым, используя алгоритм Миллера-Рабина. Итак, я на противоположном конце. - person Priyank Bolia; 01.02.2010
comment
Здесь нет никаких факториалов, но есть факторизация, которая отличается. Факториал целого числа n определяется как произведение всех положительных целых чисел, меньших n, например. 2!=2, 3!=6 и т. д. и часто выражается с помощью ! символ. Факторизация отличается, и нет общего символа, используемого для выражения факторизуемого целого числа. - person JB King; 01.02.2010

Это часть кода, который я сделал для проверки IBAN. Не стесняйтесь использовать.

    static void Main(string[] args)
    {
        int modulo = 97;
        string input = Reverse("100020778788920323232343433");
        int result = 0;
        int lastRowValue = 1;

        for (int i = 0; i < input.Length; i++)
        {
            // Calculating the modulus of a large number Wikipedia http://en.wikipedia.org/wiki/International_Bank_Account_Number                                                                        
            if (i > 0)
            {
                lastRowValue = ModuloByDigits(lastRowValue, modulo);
            }
            result += lastRowValue * int.Parse(input[i].ToString());
        }
        result = result % modulo;
        Console.WriteLine(string.Format("Result: {0}", result));            
    }

    public static int ModuloByDigits(int previousValue, int modulo)
    {
        // Calculating the modulus of a large number Wikipedia http://en.wikipedia.org/wiki/International_Bank_Account_Number                        
        return ((previousValue * 10) % modulo);
    }
    public static string Reverse(string input)
    {
        char[] arr = input.ToCharArray();
        Array.Reverse(arr);
        return new string(arr);
    }
person Jaroslav Kubacek    schedule 11.02.2010

Ответ Джейсона на Java (примечание i < exp).

private static void testModulus() {
    int bse = 5, exp = 55, mod = 221;

    int a1 = bse % mod;
    int p = 1;

    System.out.println("1. " + (p % mod) + " * " + bse + " = " + (p % mod) * bse + " mod " + mod);

    for (int i = 1; i < exp; i++) {
        p *= a1;
        System.out.println((i + 1) + ". " + (p % mod) + " * " + bse + " = " + ((p % mod) * bse) % mod + " mod " + mod);
        p = (p % mod);
    }

}
person Martin Pfeffer    schedule 09.01.2016

Просто предоставьте еще одну реализацию ответа Джейсона от C.

После обсуждения с моими одноклассниками, основываясь на объяснении Джейсона, мне больше нравится рекурсивная версия, если вы не очень заботитесь о производительности:

Например:

#include<stdio.h>

int mypow( int base, int pow, int mod ){
    if( pow == 0 ) return 1;
    if( pow % 2 == 0 ){
        int tmp = mypow( base, pow >> 1, mod );
        return tmp * tmp % mod;
    }
    else{
        return base * mypow( base, pow - 1, mod ) % mod;
    }
}

int main(){
    printf("%d", mypow(5,55,221));
    return 0;
}
person Boris    schedule 15.10.2013

Это называется модульным возведением в степень (https://en.wikipedia.org/wiki/Modular_exponentiation). .

Предположим, у вас есть следующее выражение:

19 ^ 3 mod 7

Вместо прямого питания 19 вы можете сделать следующее:

(((19 mod 7) * 19) mod 7) * 19) mod 7

Но это также может занять много времени из-за большого количества последовательных умножений, поэтому вы можете умножать значения в квадрате:

x mod N -> x ^ 2 mod N -> x ^ 4 mod -> ... x ^ 2 |log y| mod N

Алгоритм модульного возведения в степень предполагает, что:

x ^ y == (x ^ |y/2|) ^ 2 if y is even
x ^ y == x * ((x ^ |y/2|) ^ 2) if y is odd

Итак, алгоритм рекурсивного модульного возведения в степень будет выглядеть в java так:

/**
* Modular exponentiation algorithm
* @param x Assumption: x >= 0
* @param y Assumption: y >= 0
* @param N Assumption: N > 0
* @return x ^ y mod N
*/
public static long modExp(long x, long y, long N) {
    if(y == 0)
        return 1 % N;

    long z = modExp(x, Math.abs(y/2), N);

    if(y % 2 == 0)
        return (long) ((Math.pow(z, 2)) % N);
    return (long) ((x * Math.pow(z, 2)) % N);
}

Особая благодарность @chux за обнаруженную ошибку с неправильным возвращаемым значением в случае сравнения y и 0.

person Stepan Pogosyan    schedule 15.10.2017
comment
Большое спасибо за ваш отзыв. Не могли бы вы предоставить входные данные, которые приводят к неправильному выводу? - person Stepan Pogosyan; 11.02.2018
comment
Большое спасибо за найденную ошибку. Я исправил на 1 % N. - person Stepan Pogosyan; 11.02.2018

person    schedule
comment
x * power и power * power могут переполняться, когда mod*mod > UINT_MAX + 1. - person chux - Reinstate Monica; 10.02.2018
comment
Да, @chux прав, мы должны принимать мод даже во время x * power и power * power. - person jack_1729; 30.10.2018
comment
@ jack_1729 Код может использовать более широкий целочисленный тип с x * power, чтобы избежать OF. Если их нет, код может использовать это. - person chux - Reinstate Monica; 30.10.2018

person    schedule
comment
Это медленнее, чем при возведении в степень? - person Pacerier; 22.03.2017