Хорошо, вы хотите рассчитать 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 * 5 = 5 mod 221
5 * 5 = 25 mod 221
25 * 5 = 125 mod 221
125 * 5 = 183 mod 221
183 * 5 = 31 mod 221
31 * 5 = 155 mod 221
155 * 5 = 112 mod 221
112 * 5 = 118 mod 221
118 * 5 = 148 mod 221
148 * 5 = 77 mod 221
77 * 5 = 164 mod 221
164 * 5 = 157 mod 221
157 * 5 = 122 mod 221
122 * 5 = 168 mod 221
168 * 5 = 177 mod 221
177 * 5 = 1 mod 221
1 * 5 = 5 mod 221
5 * 5 = 25 mod 221
25 * 5 = 125 mod 221
125 * 5 = 183 mod 221
183 * 5 = 31 mod 221
31 * 5 = 155 mod 221
155 * 5 = 112 mod 221
112 * 5 = 118 mod 221
118 * 5 = 148 mod 221
148 * 5 = 77 mod 221
77 * 5 = 164 mod 221
164 * 5 = 157 mod 221
157 * 5 = 122 mod 221
122 * 5 = 168 mod 221
168 * 5 = 177 mod 221
177 * 5 = 1 mod 221
1 * 5 = 5 mod 221
5 * 5 = 25 mod 221
25 * 5 = 125 mod 221
125 * 5 = 183 mod 221
183 * 5 = 31 mod 221
31 * 5 = 155 mod 221
155 * 5 = 112 mod 221
112 * 5 = 118 mod 221
118 * 5 = 148 mod 221
148 * 5 = 77 mod 221
77 * 5 = 164 mod 221
164 * 5 = 157 mod 221
157 * 5 = 122 mod 221
122 * 5 = 168 mod 221
168 * 5 = 177 mod 221
177 * 5 = 1 mod 221
1 * 5 = 5 mod 221
5 * 5 = 25 mod 221
25 * 5 = 125 mod 221
125 * 5 = 183 mod 221
183 * 5 = 31 mod 221
31 * 5 = 155 mod 221
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 * (5^1 mod 221) = 5 mod 221
5 * (5^2 mod 221) = 125 mod 221
125 * (5^4 mod 221) = 112 mod 221
112 * (5^16 mod 221) = 112 mod 221
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
и т. д.
Приведенный выше алгоритм формализует эту идею.