Вычисление функций на последовательностях
Индуктивные функции на последовательностях и индуктивные расширения
В рассмотренных выше примерах при добавлении к последовательности еще одного элемента новое значение функции на последовательности можно было вычислить, зная только старое значение функции и добавленный элемент. Обозначим через Sn последовательность
Sn = {a0, a1, ..., an-1}
длины n. С помощью знака & обозначим операцию приписывания нового элемента справа к последовательности (ее называют также конкатенацией):
Sn+1 = Sn&an = {a0, a1, ..., an-1, an}
Пусть f(S) - некоторая функция на множестве последовательностей, например, сумма элементов последовательности. Функция называется индуктивной, если при добавлении нового элемента к последовательности новое значение функции можно вычислить, зная только старое значение функции и добавленный элемент. На математическом языке функция
f:W -> Y
где W - множество всех последовательностей, составленных из элементов некоторого множества X, индуктивна, если существует функция G от двух аргументов
G:Y*X -> Y
такая, что для любой последовательности S из W и любого элемента a из X значение функции f на последовательности S, к которой добавлен элемент a, вычисляется с помощью функции G:
f(S&a) = G(f(S), a).
Функция G по паре (y, a), где y - старое значение функции f на последовательности S и a - элемент, добавленный к последовательности, вычисляет новое значение y, равное значению функции f на новой последовательности.
В примере с суммой элементов последовательности функция G равна сумме элементов y и a:
G(y, a) = y+a.
В примере с максимальным элементом последовательности функция G равна максимуму:
G(y, a) = max(y,a).
В примере со схемой Горнера вычисления значения многочлена в точке t, где коэффициенты многочлена заданы в последовательности по убыванию степеней, функция G равна
G(y, a) = yt+a.
Во всех трех случаях рассматриваемая функция на последовательности индуктивна.
Общая схема вычисления значения индуктивной функции на последовательности выглядит следующим образом:.
алгоритм значение индуктивной функции( вх: последовательность S ) | дано: последовательность S | надо: вычислить функцию y = f(S) начало алгоритма | y := значение функции f на пустой последовательности; | встать в начало последовательности S; | цикл пока в последовательности S есть | | непрочитанные элементы | | прочесть очередной элемент | | последовательности S в (вых:x); | | y := G(y, x); | конец цикла | ответ := y; конец алгоритма
Таким образом, для каждой конкретной индуктивной функции надо лишь правильно задать ее значение на пустой последовательности (инициализация) и определить, как новое значение функции вычисляется через старое при добавлении к последовательности очередного элемента, т.е. задать функцию G(y,x). Схема вычисления для всех индуктивных функций одна и та же.
Однако не все функции на последовательностях являются индуктивными. Рассмотрим следующий пример. Пусть коэффициенты многочлена заданы в последовательности по убыванию степеней. Надо вычислить значение производной многочлена в точке x = 2. Обозначим через
S = {a0, a1, ..., an}
последовательность коэффициентов многочлена
p(x) = a0xn+a1xn-1+...+an
и через f(S) значение производной многочлена p'(x) в точке x =2:
f(S) = p'(2)
Покажем, что функция f не индуктивна. Достаточно указать две последовательности S1 и S2, такие, что значения функции f на них совпадают, но при добавлении к последовательностям S1 и S2 одного и того же элемента a новые значения функции уже не равны:
f(S1) = f(S2), f(S1&a) ≠ f(S2&a)
Возьмем последовательности
S1 = {1}, S2 = {1, -4,1}.
Им соответствуют многочлены
p1(x) = 1, p2(x) = x2-4x+1
p'1(x) = 0, p'2(x)= 2x-4
Значения обеих производных в точке x=2 равны нулю, т.е.
f(S1) = p'1(2) = 0, f(S2) = p'2(2) = 2*2-4 = 0
Припишем теперь к обеим последовательностям элемент a = 1:
S1&1 = {1,1}, S2&1 = {1, -4,1,1}.
Новым последовательностям соответствуют многочлены
g1(x) = x+1, g2(x) = x3-4x2+x+1
g'1(x) = 1, g'2(x) = 3x2-8x+1
Значения производных в точке x=2 равны соответственно
f(S1&1) = g'1(2) = 1 f(S2&1) = g'2(2) = 12-16+1 = -3
Мы видим, что значения f(S1) и f(S2) совпадают, но значения f(S1&1) и f(S2&1) не совпадают. Следовательно, функция f не индуктивна.
Как поступать в случае, когда функция f не индуктивна? Общий рецепт следующий: надо придумать индуктивную функцию F, такую, что, зная значение F, легко можно вычислить исходную функцию f. Функция F называется индуктивным расширением функции f.
Приведем формальные определения. Пусть исходная функция на множестве W всех последовательностей
f:W -> Y
не индуктивна. Индуктивная функция
F:W -> Z
называется индуктивным расширением функции f, если существует отображение
P:Z -> Y
такое, что для всякой последовательности S, принадлежащей W, выполняется равенство
f(S) = P(F(S))
(т.е. функция f равна композиции отображений F и P, f = P*F.) Отображение P обычно называют проекцией множества Z на Y.
Как построить индуктивное расширение функции f? Это творческий момент, готового рецепта на все случаи не существует. Неформальный рецепт следующий: надо понять, какой информации не хватает для того, чтобы уметь вычислять новое значение функции на последовательности при добавлении к последовательности нового элемента. Эту информацию надо хранить дополнительно к значению функции. Отсюда и появился термин расширение: вычисляется более сложная, расширенная, функция, чтобы по ней затем восстановить исходную. Как правило, значением индуктивного расширения F является пара (y, h), где y - значение исходной функции f, а h - некоторая дополнительная информация, позволяющая перевычислять значение y при добавлении нового элемента к последовательности. Таким образом, множество Z значений индуктивного расширения
F:W -> Z
чаще всего является множеством пар (y, h), т.е. декартовым произведением:
Z = Y*H
Отображение P на практике должно легко вычисляться. Так оно и есть в случае декартова произведения - это просто проекция на первый аргумент.
P(y, h) = y
Рассмотрим пример с вычислением производной многочлена в точке; коэффициенты многочлена заданы в последовательности по убыванию степеней. При добавлении к последовательности
Sk = {a0, a1, ...,ak}
нового коэффициента ak+1 получаем последовательность
Sk+1 = S&ak+1 = {a0, a1, ...,ak, ak+1}
Пусть этим двум последовательностям соответствуют многочлены pk(x) и pk+1(x). Тогда
pk+1(x) = pk(x)*x + ak+1.
Дифференцируя это равенство, получим:
p'k+1(x) = p'k(x)*x + pk(x).
Мы видим, что для вычисления нового значения производной нужно знать старое значение производной, а также старое значение многочлена. Следовательно, дополнительно к значению производной многочлена надо хранить еще значение самого многочлена. Таким образом, индуктивным расширением функции, равной производной многочлена в точке t, является пара (значение производной, значение многочлена):
F:S -> (p'(t), p(t))
Новое значение производной вычисляется по приведенной выше формуле через старое значение производной и старое значение многочлена. После этого вычисляется новое значение многочлена по схеме Горнера.
Выпишем алгоритм вычисления производной многочлена.
вещ алг. значение производной(вх: цел n, вещ a[n+1], вещ t) | дано: n -- степень многочлена | a[n+1] -- массив коэффициентов многочлена по | возрастанию степеней | надо: найти значение производной многочлена в точке t начало алгоритма | вещ p, dp; цел i; | p := 0.0; // Инициализация значения многочлена | dp := 0.0; // Инициализация значения производной | цикл для i от 0 до n | | dp := dp * t + p; // Новое значение производной | | p := p * t + a[i]; // Новое значение многочлена | конец цикла | ответ := dp; конец алгоритма
Другой пример неиндуктивной функции - это среднее арифметическое значение элементов последовательности. Индуктивным расширением является пара (сумма элементов последовательности, длина последовательности):
F(S) = (сумма(S), длина(S)).
Легко видеть, что функция F индуктивна. При известном значении функции F не составляет труда вычислить исходную функцию:
среднее арифметическое(S) = сумма(S)/длина(S).
В данном случае отображение P не является в чистом виде проекцией, т.к. в процессе вычислений удобнее хранить сумму элементов прочитанного отрезка последовательности, а не среднее арифметическое. Вычисления проще и, кроме того, сумма определена на пустой последовательности в отличие от среднего арифметического.
Итак, в каждом конкретном случае при вычислении неиндуктивной функции f надо придумать ее индуктивное расширение F и в программе вычислять сначала индуктивное расширение F, а затем по значению F вычислять требуемое значение исходной функции f.