Рассмотрим старую, хорошо известную проблему :
В математике наибольший общий делитель (НОД)… двух или более ненулевых целых чисел - это наибольшее положительное целое число, которое делит числа без остатка.
Определение gcd на удивление простое:
где mod - оператор по модулю (то есть остаток после целочисленного деления).
На английском языке это определение гласит, что наибольший общий делитель любого числа и ноль - это это число, а наибольший общий делитель двух чисел m и n - наибольший общий делитель числа n и остатка после деления m на n .
Если вы хотите узнать, почему это работает, см. Статью в Википедии об алгоритме Евклида .
Давайте в качестве примера вычислим gcd (10, 8). Каждый шаг равен предыдущему:
- gcd (10, 8)
- gcd (10, 10 mod 8)
- gcd (8, 2)
- gcd (8, 8 mod 2)
- gcd (2, 0)
- 2
На первом этапе 8 не равно нулю, поэтому применяется вторая часть определения. 10 mod 8 = 2, потому что 8 переходит в 10 один раз с остатком 2. На шаге 3 снова применяется вторая часть, но на этот раз 8 mod 2 = 0, потому что 2 делит 8 без остатка. На шаге 5 второй аргумент равен 0, поэтому ответ равен 2.
Вы заметили, что gcd появляется как слева, так и справа от знака равенства? Математик сказал бы, что это определение является рекурсивным, потому что определяемое вами выражение повторяется внутри его определения.
Рекурсивные определения обычно выглядят элегантно. Например, рекурсивное определение суммы списка:
sum l =
if empty(l)
return 0
else
return head(l) + sum(tail(l))
где head
- первый элемент в списке, а tail
это остальная часть списка. Обратите внимание, что sum
повторяется внутри определения в конце.
Возможно, вы вместо этого предпочтете максимальное значение в списке:
max l =
if empty(l)
error
elsif length(l) = 1
return head(l)
else
tailmax = max(tail(l))
if head(l) > tailmax
return head(l)
else
return tailmax
Вы можете определить умножение неотрицательных целых чисел рекурсивно, чтобы превратить его в серию сложений:
a * b =
if b = 0
return 0
else
return a + (a * (b - 1))
Если этот рассказ о преобразовании умножения в серию сложений не имеет смысла, попробуйте расширить несколько простых примеров, чтобы увидеть, как это работает.
Сортировка слиянием имеет прекрасное рекурсивное определение:
sort(l) =
if empty(l) or length(l) = 1
return l
else
(left,right) = split l
return merge(sort(left), sort(right))
Рекурсивные определения есть повсюду, если вы знаете, что искать. Обратите внимание, что все эти определения имеют очень простые базовые случаи, например , gcd (m, 0) = m. Рекурсивные случаи сводят на нет проблему, чтобы перейти к простым ответам.
Понимая это, теперь вы можете оценить другие алгоритмы в статье Википедии о рекурсии !