Индуктивные функции на пространстве последовательностей

Функция f на пространстве последовательностей над X называется индуктивной, если найдётся такая функция F f x , что для любого x X , любой ω Ω X будет выполняться равенство f ω x = F f ω x . В переводе на человеческий язык это определение звучит так: функция называется индуктивной, если её значение для удлинённой (за счёт добавления элемента x) последовательности можно выразить через значение этой же самой функции для исходной последовательности и через добавляемый элемент.

Функция F называется функцией перевычисления: она позволяет «перевычислить» индуктивную функцию при удлинении последовательности.

Задача вычисления различных функций на последовательностях очень распространена в программировании. Индуктивные функции среди прочих хороши тем, что алгоритм их вычисления очень прост.

Многократно воспользуемся определением индуктивной функции: f x 1 x n = F f x 1 x n 1 x n = F F f x 1 x n 2 x n 1 x n = F F F f x 1 x n 3 x n 2 x n 1 x n = = F F F F f x 1 x 2 x 3 x n . Такое постепенное «разворачивание» формулы приводит к вычислительному алгоритму: f f цикл пока есть непрочитанные элементы в ω x следующий элемент ω f F f x конец цикла вывод f

Подобные алгоритмы называют однопроходными, поскольку обработка последовательности в них ведётся за один проход: при каждом проходе цикла извлекается очередной элемент последовательности, обрабатывается, и больше программа не возвращается к этому элементу.

Часто последовательность представлена в программе как массив. При реализации алгоритма на Perl в этом случае цикл может выглядеть так:

while(@sequence)
{
	my $x=shift @sequence;
	
}

или так:

for my $x(@sequence)
{
	
}

В последнем случае массив @sequence остаётся нетронутым; в первом же варианте массив постепенно опустошается.

Если мы имеем дело с символьной последовательностью, представленной как строка $sequence, цикл принимает следующий вид:

for(my $i=0; $i<length $sequence; $i++)
{
	my $x=substr $sequence, $i, 1;
	
}

Ещё лаконичней такой вариант:

for my $x(split //, $sequence)
{
	
}

(выражение в круглых скобках — это строка $sequence, разбитая в список символов).

Ещё один важный способ получения элементов последовательности — чтение из файла. Чаще всего элементами служат или символы, или строки. В первом случае годится цикл

my $x;
while(read $sequence, $x, 1)
{
	
}

а во втором —

while(my $x=<$sequence>)
{
	
}

Пусть задано число t. Требуется вычислить значение многочлена p t = x 1 t n 1 + x 2 t n 2 + x 3 t n 3 + + x n 1 t + x n . Можно представлять себе искомое значение как значение функции на последовательности коэффициентов многочлена, расположенных по убыванию степеней t. Обозначим эту функцию как horner ω (почему именно так, выяснится позже). Как и раньше, исследуем эту функцию на предмет её индуктивности.

Преобразуем выражение для многочлена: horner ω = x 1 t + x 2 t + x 3 t + x 4 t + t + x n . Теперь видно, что происходит со значением многочлена при добавлении в последовательность коэффициентов ещё одного: старое значение умножается на t, после чего прибавляется новый коэффициент: horner ω x = horner ω t + x , F f x = f t + x .

Легко видеть, что для пустой последовательности функция доопределяется, исходя из равенства horner t = 0 , следовательно, horner = 0 при ненулевом t. При нулевом можно выбрать произвольное значение.

Следует заметить, что функция horner при t = 1 превращается в sum, а при t = 0 — в last.

Приведённый способ вычисления значения многочлена в точке t называется схемой Горнера. Этот способ очень эффективный, поскольку требует, помимо сложения, лишь n операций умножения, и вовсе не нуждается в возведениях в степень (мы полагаем, что возведение в степень, даже в натуральную, требует бо́льших вычислительных усилий, чем умножение).

Следующая программа вычисляет значение многочлена в заданной точке по схеме Горнера. Число t и коэффициенты многочлена в порядке убывания степеней заданы, как обычно, в командной строке:

#!/usr/bin/perl

use warnings;

my $t=shift;
my $p=0;

for my $x(@ARGV)
{
	$p=$p*$t+$x;
}

print "$p\n";

По определению min x 1 x n = x j , где i 1 n x j x i . Заметим, что таких x j может быть и несколько, однако, к счастью, все они равны между собой.

Эта функция индуктивна, поскольку min ω x = если x < min ω , то x иначе min ω . Таким образом, F f x = если x < f , то x иначе f = min f x .

Увы, функция min ω не определена для пустой последовательности. Попытка доопределить функцию с сохранением индуктивности приводит к равенству min x = x = min min x , которое означает, что min должно быть не меньше любого x. Числа с таким свойством не существует, однако, если в последовательности могут встретиться числа лишь из ограниченного числового множества (на практике так обычно и бывает), то можно взять наибольшее число из этого множества в качестве min . В теории, когда x может принимать любое, сколь угодно большое значение, можно позволить индуктивной функции принимать, помимо числовых значений, ещё идеальное значение + , от которого требуется лишь выполнение неравенства x < + для всех x.

Совершенно аналогично устанавливается индуктивность максимума, для которого max ω x = если x > max ω , то x иначе max ω . Для пустой последовательности максимум доопределяется значением .

Приводим программу, вычисляющую минимум и максимум чисел, заданных в командной строке:

#!/usr/bin/perl

use warnings;

my $min=INFINITY;
my $max=-INFINITY;

for my $x(@ARGV)
{
	($min, $max)=($x<$min? $x: $min, $x>$max? $x: $max);
}

print "$min\t$max\n";

Рассмотрим последовательности символов. Требуется вычислить количество слов в последовательности, если под словами понимаются непрерывные фрагменты в последовательности, состоящие из непробельных символов. Каждое слово, как правило, заключено между двумя пробельными символами. В особом случае слово примыкает к началу последовательности справа или к концу слева. Да, пустые слова (между двумя соседними пробельными символами) не считаются.

С точки зрения поставленной задачи все символы делятся на две категории: пробельные и непробельные. Никакие другие свойства символов не важны, поэтому можно кодировать символы логическими значениями нет (пробелы) и да (непробелы). Так что будем иметь дело с последовательностями логических значений ω Ω нет да . Искомую функцию обозначим wc ω (Word Count — счётчик слов).

Понятно, что знания wc ω и x недостаточно для вычисления wc ω x : счётчик слов увеличивается в том и только в том случае, если добавляемый элемент — непробел, а последний элемент — пробел (то есть происходит смена пробела на непробел). В результате получим выражение wc ω x = если x ¬ last ω , то wc ω + 1 иначе wc ω .

Вывод: функция wc не индуктивна.

В некоторых случаях можно сократить вычисление значения индуктивной функции за счёт устранения ненужных действий. Такая ситуация наступает, если после обработки части последовательности вычисленное значение индуктивной функции окажется стационарным, то есть таким, что никакое дальнейшее перевычисление не может его изменить. Более строго, значение y = f ω называется стационарным, если x X F y x = y . Если f ω = y , то, очевидно, f ω x = y , и, как бы мы ни удлиняли в дальнейшем последовательность, значение функции останется тем же самым стационарным значением y. Это означает, что, получив стационарное значение, можно прервать цикл обработки последовательности.

С учётом сказанного, внесём в алгоритм вычисления индуктивной функции дополнение: f f цикл пока есть непрочитанные элементы в ω x следующий элемент ω f F f x если f является стационарным, то выход конец цикла вывод f

Из разобранных к настоящему моменту примеров индуктивных функций стационарные значения имеются у функций prod: 0, min: , max: и search: да.

Ещё пара примеров функций на пространстве последовательностей логических значений. Для индуктивной функции and x 1 x n = x 1 x n = i = 1 n x i стационарным значением служит нет, а для функции or x 1 x n = x 1 x n = i = 1 n x i да.

Информатика-54© А. Н. Швец