На этом шаге мы рассмотрим еще несколько примеров использования рекуррентных соотношений.
Тонкость рекуррентных соотношений при делении размера задачи на целочисленную константу k ≥ 2 заключается в том, что в них не должно быть начального условия для n = 0. Математически оно никогда не достижимо, и мы не сможем получить значение i на шаге 3 метода расширения. Ловушка кроется в том, что аргументом функции T(n) должно быть только целое число, поэтому дробь n/k на самом деле предполагает целочисленное деление. А это значит, что следующим за T(1) расширением будет не T(1/k), а T(0). Поэтому для правильного применения метода расширения в рекуррентные соотношения нужно включать дополнительное начальное условие, чаще всего для n = 1.
В предыдущих примерах было довольно просто в процессе расширения обнаружить общую формулу на произвольном шаге i. Для следующих рекуррентных соотношений это сделать несколько сложнее, так как они содержат вычисление сумм, о которых шла речь в разделе "Суммы и произведения" (начало на 36 шаге).
Рассмотрим следующее рекуррентное соотношение:
а, если n = 0 T(п) = (3.24) T(n - 1) + b * n + c, если n > 0.
Процесс его расширения:
T(n) = T(n - 1) + b * n + с = [T(n - 2) + b(n - 1) + c] + b * n + c = = T(n - 2) + 2b * n - n + 2c = [T(n - 3) + b(n - 2) + c] + 2b * n - b + 2c = = T(n - 3) + 3b * n - b(1 + 2) + 3c = [T(n - 4) + b(n - 3) + c] + 3b * n - b(1 + 2) + 3c = = T(n - 4) + 4b * n - b(1 + 2 + 3) + 4c = . . . = T(n - i) + i * b * n - b(1 + 2 + 3 + ... + (i - 1)) + i * c,
i-1 T(n) = T(n-i) + i * b * n - b * ∑j + i * c = T(n-i) + i * b * n - b(i - 1) i/2 + i * c. (3.25) j=1
Два распространённых заблуждения при использовании сумм в формулах на шаге i:
Наконец, начальное условие T(0) достигается при i = n, поэтому подстановка его значения в (3.25) даёт формулу
T(n) = b * n2 - b * n(n - 1)/2 + c * n + a = b * n2 /2 + (c + b/2)/n + a ∈ Θ(n2),
Следующее рекуррентное соотношение появляется в алгоритмах типа "разделяй и властвуй", таких, например, как сортировка слиянием:
a, если n = 1, T(n) = (3.26) 2T(n/2) + b * n + c, если n > 1
Процесс его расширения:
T(n) = 2T(n/2) + b * n + с = 2[2T(n/4) + b * n/2 + с] + b * n + с = = 4T(n/4) + 2b * n + 2с + с = 4[2T(n/8) + b * n/4 + c] + 2b * n + 2c + c = = 8T(n/8) + 3b * n + 4c + 2c + c = 8[2T(n/16) + b * n/8 + c] + 3b * n + 4c + +2c + c = = 16T(n/16) + 4b o n + 8c + 4c + 2c + c = . . . = 2iT(n/2i) + i * b * n + c(1 + 2 + 4 + ... + 2i-1).
И здесь в квадратных скобках - расширения членов, содержащих T. В этом случае скобки особенно полезны, поскольку каждый из расширяемых членов должен умножаться на 2. Студентам всегда стоит использовать их, так как пренебрежение ими может приводить к многочисленным ошибкам.
В данном случае общая формула содержит частичную сумму геометрической прогрессии
i-1 T(n) = 2iT(n/2i) + i * b * n + c ∑ 2j = 2iT(n/2i) + i * b * n + c(2i - 1). (3.27) j=1
Начальное условие T(1) достигается при n/2i = 1, откуда следует, что i = log2n. Поэтому подстановка i в (3.27) даёт:
T(n) = nT(1) + b * n log2 n + c(n - 1) = b * n log2 n + n(a + c) - c ∈ Θ(n log n),
На следующем шаге мы рассмотрим основную теорему.