"Наихудшие" случаи для алгоритмов нахождения наибольшего общего делителя
"Наихудшим" случаем для классического алгоритма Евклида нахождения наибольшего общего делителя считается случай соседних чисел Фибоначчи. Чтобы оценить быстродействие, попытаемся, например, определить, при каком n будет заметна задержка при вычислении:
GCD[Fibonacci[n
+
2
] Fibonacci[n
+
1
]]
Возьмем, например, следующую программу.
Do[Print[n,
":"
, GCD[Fibonacci[n
+
2
], Fibonacci[n
+
1
]]], {n,
10000
}]
Увы! Алгоритм, реализованный в системе Mathematica, столь эффективен, что даже на весьма посредственном ПК эта программа выполняется без каких бы то ни было задержек.
Если же программу модифицировать так:
Do[Print[n,":",GCD[Fibonacci[2An+2], Fibonacci[2An+1]]],{n,10000}], то задержка будет весьма ощутимой уже при n = 25. Но не забывайте, что 225 + 2 =33554434, а число Fibonacci[2^25+2] имеет 7012462 цифры!
Так что алгоритм, реализованный в системе Mathematica, справляется с классическим "наихудшим" случаем не просто вполне удовлетворительно, а блестяще! Правда, известны алгоритмы, для которых наихудший случай представляют числа u = аn +аn-1, и v= аn-1, где .
Давайте исследуем и этот случай. Нам понадобятся следующие определения.
a[n_] :
=
FullSimplify[((
1
+
Sqrt[
2
])
^
n
-
(
1
-
Sqrt[
2
])
^
n)
/
(
2Sqrt
[
2
])]
u :
=
a[n]
+
a[n
-
1
]
v :
=
a[n
-
1
]
Попытаемся выполнить программу.
Do[Print[n,
":"
, GCD[u, v]], {n,
100
}]
Окажется, что уже при n = 98 выражения "не хотят" упрощаться! Поэтому определение а[n_] лучше изменить.
a[n_] :
=
(Expand[(
1
+
Sqrt[
2
])
^
n]
-
Expand[(
1
-
Sqrt[
2
])
^
n])
/
(
2
Sqrt[
2
])
Однако при таком подходе понятно, что значительное время тратится также на раскрытие скобок. Однако даже при n = 70 000 вычисления не занимают слишком много времени. При этом значении и числа u и v являются 26 794-значными, и основное время уходит на их вычисление, а не на вычисление их наибольшего общего делителя, который вычисляется в мгновенье ока! Отсюда можем сделать вывод, что функция GCD реализована весьма эффективно, и если при ее использовании возникают неприятности, то, скорее всего, они связаны с вычислением не самой функции, а ее аргументов!
Пример 6.2.
Взаимная простота чисел Ферма. Числа Ферма возрастают довольно быстро и являются взаимно простыми. Поэтому их наибольший общий делитель должен быть равен 1. Выясним, до какого номера n система Mathematica сможет в приемлемое время проверить взаимную простоту этих чисел.
Вот нужная нам функция.
fermat[n_] :
=
2
^
(
2
^
n)
+
1
Теперь можно написать и программу.
Do[Print(n,
":"
, GCD[fermat[n
+
1
], fermat[n]]], {n,
100
}]
Задержка начинает ощущаться при n = 28. Однако вычисления заканчиваются (и притом довольно быстро, если учитывать количество цифр в числах) и при n= 29. Напомню, что уже 22-е число Ферма имеет более миллиона (1 262 612) знаков, а 23-е число Ферма является 2 525 223-значным! Шутки ради 24-е число Ферма, являющееся 5 050 446-значным, я перетащил в Word. Текстовый редактор с записью этого числа работает медленнее, чем система Mathematica! Вот уж воистину супервычислитель: решает задачу быстрее, чем иные успевают произнести ее формулировку! 25-е число Ферма является 10 100 891-значным, и Word его не выносит: замедляется работа меню, даже курсор движется так медленно, что работать становится практически невозможно.