скачать doc
2. ИНДУКТИВНЫЕ ФУНКЦИИ НА ПОСЛЕДОВАТЕЛЬНОСТЯХ 0 17 1
2.1. Последовательности и файлы 17 1
2.2. Индуктивные функции 20 4
2.3. Стационарные значения индуктивных функций 22 6
2.4. Индуктивные расширения функций 23 7
2.5. Примеры задач с индуктивными функциями 24 8
.
3. ИНДУКТИВНЫЕ ФУНКЦИИ НА ПОСЛЕДОВАТЕЛЬНОСТЯХ
3.1. Последовательности и файлы
Пусть X – произвольный алфавит (конечное множество). Рассмотрим последовательности символов из этого алфавита: x1 x2 … xn (xi X). Пустую последовательность обозначим . Пусть:
(X) или для краткости просто пространство всех конечных последовательностей над X, т. е.
(X) = {x1 x2 … xn: nN0, xi X i1..n},
при этом ;
k(X) или для краткости просто k пространство конечных последовательностей длины не менее k, т. е.
k(X) = {x1 x2 … xn: n k, nN0, xi X i1..n};
таким образом, = 0 1 2 ... .
Определим операцию postfix: X добавления элемента в конец последовательности, т. е. если = x1 x2 … xn, то postfix(, x) = x1 x2 … xn x , а postfix(, x) = x. Иногда эту операцию удобнее записывать в инфиксной форме, используя для нее специальный знак, например «*», т. е. postfix(, x) = * x. В этих обозначениях * x = x1 x2 … xn x и * x = x. Если k, то * x k + 1.
Тогда конструктивно последовательность можно определить так:
1) есть последовательность;
2) если последовательность, то и * x последовательность;
3) ничто другое (кроме пп.1 и 2) не есть последовательность.
Полезно дополнительно определить следующие операции над последовательностями:
first: 1 X, rest: 1 , prefix: X 1,
и если и = x1 x2 … xn, то
first() = x1, rest() = x2 … xn, prefix(first(), rest()) = .
Последовательный файл (или далее просто файл) из записей типа X (file of X) это реализация математического понятия последовательности с ограниченными способами доступа к элементам. Наглядная модель файла представлена на рис. 3.1. Положение ленты относительно головки чтения-записи определяется разбиением ее (ленты) на две части: левую L(F) и пра-
-
L(F)
R(F)
Начало ленты
Лента
Головка
чтения-записи
Метка
конца
файла
Рис. 3.1. Наглядная модель последовательного файла
вую R(F). Кроме того, файл может находиться в одном из трех режимов: неопределенном (n), чтения (r), записи (w). Таким образом, файл можно определить как упорядоченную тройку F = (L(F), R(F), St(F)), где индикатор режимов St(F) {n, r, w}. Состояние файла R(F) = идентифицируется предописанной функцией (предикатом) Eof(F): если R(F) = , то Eof(F) = true, иначе Eof(F) = false (рис. 3.2).
F: | | | | | | | | | F: | | | | | |
| | | | | | | | | | | | | | |
| Eof(F) = false | | | | | Eof(F) = true |
Рис. 3.2. Действие функции Eof(F)
Определим основные операции над файлами.
1. Установка режима записи: Rewrite(F). Семантика этой операции задается свойством {true} Rewrite(F) {Eof(F) & (L(F) = ) & (St(F) = w)} и иллюстрируется на рис. 3.3.
F: | | | | | | | | | F: | | | | | |
| | | | | | | | | | | | | | |
Рис. 3.3. Установка режима записи Rewrite(F): слева состояние до
операции, справа состояние после операции
2. Запись в файл: Write(F, x). Семантика этой операции (аналога postfix) задается свойством
{(St(F) = w) & Eof(F) & (L(F) = )}
Write(F, x);
{(St(F) = w) & Eof(F) & (L(F)= * x )}
и иллюстрируется на рис. 3. 4.
F: | | | | | | | | | F: | | | | | |
| | | | | | | | | | | | | | |
x: | | | | | | x: | | | ||||||
| | | | | | | Запись |
Рис. 3.4. Запись в файл Write(F, x): слева – состояние до операции,
справа – состояние после операции
3. Установка режима чтения: Reset(F). Семантика операции задается свойством {F = } Reset(F) {(L(F) = ) & (R(F) = ) & (St(F) = r)} и иллюстрируется на рис. 3.5.
F: | | | | | | | | | F: | | | | | |
| | | | | | | | | | | | | | |
Рис. 3.5. Установка режима чтения Reset(F): слева – состояние
до операции, справа – состояние после операции
F: | | | | | | | | | F: | | | | | |
| | | | | | | | | | | | | | |
x: | | | | | | x: | | | ||||||
| | | | | | | Чтение |
Рис. 3.6. Чтение из файла Read(F, x): слева – состояние до операции,
справа – состояние после операции
4. Чтение из файла: Read(F, x). Семантика этой операции задается свойством
{(St(F) = r) & not Eof(F) & (L(F) = ) & (R(F) = )}
Read(F, x)
{(St(F) = r) & (L(F) = *first()) & (R(F) = rest()) & (x = first())}
и иллюстрируется на рис. 3.6.
3.2. Индуктивные функции
Пусть в файле fint записана последовательность целых чисел (Z), требуется найти максимальное из них, т. е. Max(). Программа может быть написана из естественного соображения: читаем последовательность и для каждого очередного элемента x определяем максимальный элемент прочитанной части последовательности. Очевидно, что Max(*x) = max(Max(), x), где max(a, b) обозначает максимальное из двух чисел a и b. Доопределив Max() = , получаем фрагмент программы, вычисляющий Max():
var x, y: Integer; fint: file of Integer; …
Reset(fint); y := 32768; {y := }
while not Eof(fint) do
begin
Read(fint, x);
if x > y then y := x
end {while} {y = Max()}
Инвариантом цикла здесь является соотношение y = Max(L(fint)), а ограничивающей функцией Length(R(fint)).
Функции от последовательностей, подобные рассмотренной, встречаются часто и могут быть названы индуктивными [14], или марковскими [18].
Определение. Функция f: (X) Y называется индуктивной, если f(*x) можно вычислить, зная f() и x, т. е. если существует
G:Y X Y,
такая, что для всех (X), x X выполняется соотношение
f(*x) = G(f(), x).
Рассмотренный пример с Max() записывается в этих обозначениях следующим образом:
f() = Max(), X = Z, f: (Z) Z, G(y, x) = max(y, x).
Еще один пример индуктивной функции:
f = «число положительных элементов последовательности».
Здесь f: (Z) N0. Функция f() индуктивна. Действительно, для всех (Z), x Z имеем f(*x) = G(f(), x), f() = 0, где G: N0Z N0 и
y + 1, если x > 0,
G(y, x) =
y, если x 0.
Индуктивные функции удобно вычислять. Пусть известно значение f0 = f(). Тогда значение fn = f(n), где n = x1 x2 … xn , можно найти так:
1 = 0*x1, f1 = f(1) = G(f0, x1),
2 = 1*x2, f2 = f(2) = G(f1, x2),
…
n = n 1*xn, fn = f(n) = G(fn 1, xn).
Если математическая последовательность представлена в программе файлом fin типа file of X, то эти рекуррентные соотношения можно реализовать в виде следующего фрагмента программы.
Фактически это схема программ, из которой конкретная программа может быть получена интерпретацией абстрактных типов X и Y, переменных x, y и y0, функции G(y, x). Эта схема имеет инвариант цикла y = f(L(fin)) и ограничивающую функцию Length(R(fin)). | Reset(fin); y := y0; {y0 = f()} while not Eof(fin) do begin Read(fin, x); y := G(y, x) end {while} |
Пример неиндуктивной функции:
f = «число максимальных элементов последовательности».
Пусть, например x R, тогда f: (R) N0. Очевидно, можно положить f() = 0 и записать
1, если x > Max(),
f(*x) = f() + 1, если x = Max(),
f(), если x < Max().
Поскольку для вычисления f(*x) необходимо кроме f() и x знать еще Max(), то функция f не является индуктивной.
Можно ввести формальный критерий индуктивности [14].
Утверждение. Функция f индуктивна тогда и только тогда, когда
, : (f() = f()) (f(*x) = f(*x))
для любого x X.
Для предыдущего примера = (0, 1, 0, 1), = (1, 2, 1, 2), f() = 2, f() = 2. При x = 1 получим f(*x) = 3 и f(*x) = 2, т. е. f(*x) f(*x), и, следовательно, f неиндуктивна.
3.3. Стационарные значения индуктивных функций
Пусть f: (X) Y индуктивная функция, т. е. существует G:Y X Y, такая, что для всех (X), x X выполняется соотношение f(*x) = G(f(), x).
Значение ysY называется стационарным, если ys = G(ys, x) для любого x X.
Пример. Пусть f = «среди элементов (Z) нет нулевых». Здесь f: (Z) Boolean и решение имеет вид f() = true, f(*x) = (f() & (x 0)). Ясно, что f() = false – стационарное значение, так как для любого b: Boolean имеем false & b = false. В этом случае программу можно записать с учетом стационарного значения:
Reset(fin); y := true; {y = f()} while not Eof(fin) & y do begin Read(fin, x); y := (x 0) end {while} | Здесь оператор y := y & (x 0) заменен на y := (x 0), поскольку в условие продолжения цикла добавлен конъюнктивный член y. В общем случае схема вычисления индуктивной функции, имеющей стационарное значение, такова: |
Reset(fin);
y := y0; {y0 = f()}
while not Eof(fin) & (y стационарное значение) do
begin
Read(fin, x);
y := G*(y, x)
end {while}
Здесь G* либо совпадает с G, либо является ее сужением с учетом выполнения условия «y стационарное значение» в теле цикла.
3.4. Индуктивные расширения функций
Многие функции не являются индуктивными. Это значит, что информация, заключенная в f(), недостаточна для вычисления f(*x). Однако можно определить, какой именно информации не хватает, и попытаться ее получить. Тогда рассмотрим более сложную функцию, чем исходная, но она уже будет индуктивной.
Пример. Пусть f = «число максимальных элементов последовательности». Эта функция уже рассматривалась в 3.2 как пример неиндуктивной функции. В полученные рекуррентные соотношения
1, если x > Max(),
f(*x) = f() + 1, если x = Max(),
f(), если x < Max()
входит еще функция Max(). Если вычислять и ее значение, то на каждом шаге можно будет определять f(*x).
Пусть x R, тогда f: (R) N0. Введем в рассмотрение функцию Max: (R) R*, где R* = R { }, «» идеальный элемент, используемый как значение Max(). Добавляя к соотношению для f(*x) соотношение Max(*x) = max(Max(), x), можно написать следующую программу:
Reset(fin);
y := 0; {y = f()} z := ; {z = Max()}
while not Eof(fin) do
begin
Read(fint, x);
if x > z then begin z := x; y := 1 end
else {x Max()}
if x = z then y := y + 1 else {ничего}
end {while} {y = f() & z = Max()}
Обобщая этот пример, введем понятие индуктивного расширения.
Определение. Функция F: Y называется индуктивным расширением функции f: Yf, если
1) F – индуктивна,
2) h: Y Yf , такая, что для всех : f() = h(F()).
Условие 2 означает, что, зная F(), можно получить f().
3.5. Примеры задач с индуктивными функциями
Пример 1. Вычисление значения полинома, заданного последовательностью коэффициентов по убывающим степеням.
Пусть = x1 x2 … xn 1 xn и p(; t) = x1 t n 1 + x2 t n 2 + ... + xn 1 t + xn.
Рассмотрим f() = p(; t), тогда
f(*x) = x1 t n + x2 t n 1 + ... + xn 1 t2 + xn t + x =
= f() t + x. (3.1)
Из f(1) = x1 = f() t + x1 следует f() = 0 для всех t.
Эти соотношения лежат в основе следующей программы вычисления значения полинома (t задано). Эту программу естественно назвать схемой Горнера. | Reset(fin); y :=0; {f()=0} while not Eof(fin) do begin Read(fin, x); y := y*t + x end {while} {y = p(; t)} |
Простым и показательным примером практического использования данной вычислительной схемы является чтение натурального числа z, представленного в текстовом файле записью в позиционной системе счисления с основанием q (2 q qmax), т. е. z = <x1 x2 ... xn 1 xn>q , где xi 0..(q 1) для i 1..n и x1 0. Очевидно, что z = x1 q n 1 + x2 q n 2 + ... + xn 1 q + xn и может быть вычислено по схеме Горнера:
const q = 3; {2 q 10} var x: Char; z: Integer; y: Word; fin: Text; begin Assign(fin, 'Number.dat'); Reset(fin); y :=0; while not Eof(fin) do begin Read(fin, x); z := Ord(x) 48; y := y*q + z end {while}; WriteLn('прочитано число = ', y) end. |
Пример 2. Вычисление значения производной полинома, заданного последовательностью коэффициентов по убывающим степеням.
Можно рассмотреть f1() = pt(; t) как новую функцию и попытаться для неё решить задачу заново, а можно поступить иначе продифференцировать по t уже полученное рекуррентное соотношение (3.1):
[f(*x)]t = [f()]t t + f(),
т. е. f1(*x) = f1() t + f(). Функция f1 неиндуктивна, и следует рассмотреть индуктивное расширение F() = (f1() , f()):
F(*x) = ( f1() t + f(), f() t + x).
Из соотношений f1(1) = [x1]t = 0 = f1(0) t + f(0) = f1() t + 0 для всех t R следует f1() = 0. Тогда для вычисления F() имеем программу
Reset(fin);
y := 0; {f() = 0} y1 := 0; {f1() = 0}
while not Eof(fin) do
begin
Read(fin, x);
y1:= y1*t + y;
y := y*t + x
end {while} {y = f() & y1= f1()}
Полученную программу можно назвать схемой Горнера для вычисления значения производной многочлена.
Пример 3. Вычисление максимальной длины равнины (площадки) в заданной последовательности. Пусть задана последовательность = x1 x2 … xn 1 xn . Площадкой длины k называется отрезок (связная подпоследовательность) равных элементов, т. е. xi = xi + 1 = … = xi + k 1 (при этом либо i = 1, либо xi 1 xi , а также либо xi + k 1 xi + k, либо i + k 1 = n). Функция f = «длина самой длинной площадки в » неиндуктивна. Чтобы показать это, воспользуемся критерием индуктивности. Действительно, пусть
= (1, 1), = (2, 2), f() = 2, f() = 2.
При x = 1 получим f(*x) = 3 и f(*x) = 2, т. е. f(*x) f(*x) , и, следовательно, f неиндуктивна.
Рассмотрим индуктивное расширение:
F() = ( f(), last(), конкурент()),
где last() последний элемент последовательности, а конкурент() длина хвостовой площадки (только площадка, заканчивающаяся последним элементом, может конкурировать с текущим рекордом f()).
Запишем рекуррентные (индуктивные) соотношения:
f(*x) = max(f(), конкурент(*x)),
конкурент() + 1, если x = last(),
конкурент(*x) =
1, если x last(),
last(*x) = x.
Все эти функции на пустой последовательности можно доопределить согласованно с индуктивными соотношениями:
f() = 0, конкурент() = 0, last() = 0.
Тогда получаем программу
Reset(fin);
y := 0; {f() = 0}
z := 0; {z конкурент, z() = 0}
last :=0; {last() любой}
while not Eof(fin) do
begin
Read(fin, x);
if x = last then z := z + 1
else z := 1;
if z > y then y := z;
last := x
end {while}
Пример 4. Вычисление максимальной суммы элементов среди всех отрезков последовательности. Пусть задана последовательность = x1 x2 … xn 1 xn . Требуется вычислить
f() = «сумма элементов отрезка с максимальной суммой»,
где отрезок связная подпоследовательность последовательности .
Пусть S(k, m) = ik..m xi, где 1 k m n. Тогда
f() = max(S(k, m): 1 k m n).
Функция f – неиндуктивна. Действительно, по критерию индуктивности: пусть = (5, 1), = (1, 5), f() = 5, f() = 5, но при x = 1 получим f(*x) = 5 и f(*x) = 6, т. е. f(*x) f(*x), и, следовательно, f неиндуктивна.
Рассмотрим индуктивное расширение F() = ( f(), t()), где роль потенциального конкурента текущему рекорду f() при продолжении последовательности играет t() максимальная сумма среди отрезков, прилегающих к правому концу последовательности, т. е. t() = max(S(j, n): 1 j n).
Вычисление f и t можно проводить следующим образом:
f(*x) = max(f(), t(*x)),
t(*x) = max(x, x + t()),
t() = 0, f() = .
В результате получим программу
const MinInt = 32768; {}
var x, { очередной элемент последовательности }
y, { y = f() }
z: Integer; { z = t() }
fin: Text; { input file}
begin ...
Reset(fin); { L(fin) = }
y := MinInt; z := 0; { y = f(), z = t() }
{ inv: y = f(L(fin)) & z = t(L(fin)) }
{ bound: length(R(fin)) }
while not Eof(fin) do
begin
Read( fin, x );
if z > 0 then z := z + x else z := x;
if z > y then { обновить рекорд } y := z;
end { while }…
end.