- •Історична довідка
- •Характеристика й особливості мови
- •Алфавіт мови
- •Службові (зарезервовані) слова.
- •Структура програми мови Turbo Pascal
- •Процедури введення-виведення. Деякі вбудовані функції Турбо-Паскаля.
- •Функції числових параметрів.
- •Базові управляючі конструкції Турбо-Паскаля Оператори умовного переходу.
- •Оператори циклів у Паскалі
- •Концепція типів даних. Типи даних в мові Pascal
- •Дійсні типи
- •Бульовий (логічний) тип
- •Символьні і рядкові змінні
- •1. Символьний тип
- •2. Рядковий тип
- •Перерахований та обмежений типи
- •1. Перерахований тип
- •2. Обмежений тип
- •1. Поняття масиву. Одномірні масиви
- •2. Багатомірні масиви
- •3. Сортування і пошук
- •Множинний тип
- •Тип запис
- •Процедури і функції
- •Формальні і фактичні параметри. Механізм параметрів
- •Параметри - значення
- •Параметри-змінні
- •Безтипові параметри
- •Приведення типів.
- •Процедурні типи
- •Рекурсія Рекурсивні визначення
- •Рекурсивні підпрограми
- •Алгоритми з поверненням. Розв’язок задачі про рух коня
- •Алгоритми з поверненням. Розв’язок задачі про вісьмох ферзів
- •Модулі в Турбо Паскалі
- •Модуль crt
- •1. Керування екраном
- •2. Робота з клавіатурою
- •3. Інші можливості
- •Графіка в Турбо Паскалі
- •1. Включення і вимикання графічного режиму.
- •2. Побудова елементарних зображень
- •3. Виведення текстової інформації.
- •Файли в мові програмування Pascal
- •Установчі і завершальні операції
- •Операції введення-виведення
- •Обробка помилок введення-виведення
- •Переміщення по файлу
- •Спеціальні операції
- •Текстові файли
- •Двійкові файли
- •Статичні і динамічні змінні
- •Покажчики
- •Стан покажчика
- •Установка розмірів динамічної пам'яті
- •Сумісність і перетворення посилкових типів
- •Динамічні структури даних
- •Динамічні змінні: інші види списків, стек і черга.
- •1. Інші види списків
- •2. Стек і черга
- •Дерева і пошук у деревах
- •1. Визначення й описи структур даних
- •2. Алгоритми
- •Змішані таблиці
- •Об’єктно-орієнтоване програмування. Що таке об’єктно-орієнтоване програмування
- •Інкапсуляція
- •Спадкування
- •Віртуальні методи і поліморфізм
- •Конструктори, динамічні об'єкти і деструктори
- •Поля і методи: сховані і загальнодоступні
- •Системно- залежні розширення
- •Налагодження змінних
- •Оверлеї
- •Переривання і системні виклики
- •Доступ до пам'яті і портів
- •Перевизначення переривань
Алгоритми з поверненням. Розв’язок задачі про рух коня
Особливо інтригує область програмування — задачі так називаного «штучного інтелекту». Тут ми маємо справу з алгоритмами, що шукають рішення не за заданими правилами обчислень, а шляхом проб і помилок. Звичайно процес проб і помилок розділяється на окремі задачі. Часто ці задачі найбільше природно виражаються в термінах рекурсії і вимагають дослідження кінцевого числа підзадач. У загальному вигляді весь процес можна мислити як процес пошуку, що будує (і обрізає) дерево підзадач. У багатьох проблемах таке дерево пошуку росте дуже швидко, ріст залежить від параметрів задачі і часто буває експонентним. Відповідно збільшується і вартість пошуку. Іноді, використовуючи деякі евристики, дерево пошуку вдається скоротити і тим самим звести витрати на обчислення до розумних меж.
Почнемо з демонстрації основних методів на добре відомому прикладі— задачі про хід коня.
Дано дошку розміром n×n, тобто таку, що містить n2 полів. Спочатку на поле з координатами х0, у0 ставлять коня — фігуру, що переміщується по звичайних шахових правилах. Задача полягає в пошуку послідовності ходів (якщо вона існує), при якій кінь точно один раз побуває на всіх полях дошки (обійде дошку), тобто потрібно обчислити n2 — 1 ходів.
Очевидний прийом спростити задачу обходу n2 полів — рішити більш просту: або виконати черговий хід, або довести, що ніякий хід не можливий. Тому почнемо з визначення алгоритму виконання чергового ходу. Перший його варіант має такий вигляд
PROCEDURE TryNextMovet
BEGIN
ініціалізація вибору ходу;
REPEAT
вибір чергового кандидата зі списку ходів;
IF підходить THEN
BEGIN
запис ходу
IF дошка не заповнена Тhеn
BEGIN
TryNextMove;
IF невдача Then знищення попереднього ходу
END
END
UNTIL (був удалий хід) OR(кандидатів більше немає)
ENDTtyNextMove
Якщо ми хочемо описати цей алгоритм більш детально, то потрібно вибрати деяке представлення для даних. Дошку, найпростіше, можна представляти як матрицю, назвемо її h. Уведемо, крім того, тип для значень, індексів:
CONST razmer=100
VAR h: ARRAY [1..razmer, 1..razmer] OF INTEGER
Через те що ми хочемо знати історію просування по дошці, поля її будемо представляти цілими числами, а не бульовими значеннями, що дозволяють лише визначати зайнятість поля. Очевидно, можна зупинитися на таких угодах:
h[x, у] = 0: поле (x, y) ще не відвідувалося
h[x,y] = i поле (x,y) відвідувалося на i-му ході.
Тепер потрібно вибрати відповідні параметри. Вони повинні визначати початкові умови наступного ходу і результат (якщо хід зроблений). У першому випадку досить задавати координати :поля :(x,y), звідки роблять хід, і число яке вказує номер ходу (для фіксації). Для результату ж потрібно булевий параметр; якщо він істиний, то хід був можливий.
Які оператори можна уточнити на основі прийнятих рішень? Очевидно, умову «дошка не заповнена» можна переписати як i < n2. Крім того, якщо ввести дві локальні змінні u і v для можливого ходу, обумовленого відповідно до правил «стрибка» коня, то предикат «допустимо» можна представити як логічну кон’юнкцію умов, що нове поле знаходиться в межах дошки (l<=u<=n і 1<=v<=n) і ще не відвідувалося (huv=0).
Фіксація припустимого ходу виконується за допомогою присвоювання huv := i, а скасування — за допомогою huv=0. Якщо ввести локальну змінну ql і використовувати її як параметр-результат, при рекурсивних звертаннях до цього алгоритму то ql можна підставити замість «є хід». Так ми приходимо до варіанта
PROCEDURE TRY(I,X,Y:INTEGER; VAR Q:BOOLEAN);
VAR U,V:INTEGER; Q1:BOOLEAN;
BEGIN
ініціалізація вибору ходу;
REPEAT
<U,V> — координати наступного ходу;
IF (U>=1) AND (U<=N) AND (V>=1) AND (V<=N) AND (H[U,V]=0)
THEN
BEGIN
H[U,V]:=I
IF I<N*N THEN
BEGIN
IF NOT Q1
THEN H[U,V]:=0
ELSE Q1:= TRUE
END
END;
UNTIL Q1 OR нема інших кандидатів;
Q:=Q1
END.
Ще один крок деталізації — і одержимо вже програму, цілком написану в термінах нашої основної мови програмування. Помітимо, що до цього моменту програма створювалася зовсім незалежно від правил, що керують рухом коня. Ми цілком навмисне відкладали розгляд приватних особливостей задачі. Тепер самий час звернути на них увага.
Якщо задана початкова пара координат x, у, то для наступного ходу u, v існує вісім можливих кандидатів. На малюнку вони пронумеровані від 1 до 8. Одержувати u, v з x, у дуже просто, досить до останнього додавати різниці між координатами, що зберігаються або в масиві різниць, або в двох масивах, що зберігають окремі різниці. Позначимо ці масиви через dx і dy і будемо вважати, що вони відповідним чином ініційовані. Для нумерації чергового ходу-кандидата можна використовувати індекс k. Подробиці показані в nроrрамі. Перший раз до рекурсивної процедури звертаються з параметрами x і y — координатами поля, з якого починається обхід. Цьому полю повинне бути присвоєне значення 1, інші поля маркіруються як вільні:
h [x0, y0] := 1; try (2, x0, y0, q)
Не можна упускати ще одну деталь. Змінна H[u,v] існує лише в тому випадку коли і u i v лежать в діапазоні індексів 1..n. Тому в умові важливо щоб складова H[u,v] =0 була останньою.
program kings_tour;
uses crt,dos;
const razmer=100;
var i,j,n,nsqr:integer;
q:boolean;
dx,dy:array[1..8] of integer;
h:array[1..razmer,1..razmer] of integer;
procedure try(i,x,y:integer; var q:boolean);
var k,u,v:integer;
q1:boolean;
begin
k:=0;
repeat
k:=k+1;q1:=false;
u:=x+dx[k];v:=y+dy[k];
if (u>=1)and (u<=n) and(v>=1) and(v<=n)and(h[u,v]=0) then
begin
h[u,v]:=i;
if i<nsqr then
begin
try(i+1,u,v,q1);
if not q1 then h[u,v]:=0;
end
else q1:=true
end;
until q1 or (k=8);
q:=q1;
end;
begin
clrscr;
write('Razmer->');readln(n);
dx[1]:=2;dx[2]:=1; dx[3]:=-1;dx[4]:=-2;
dx[5]:=-2;dx[6]:=-1;dx[7]:=1;dx[8]:=2;
dy[1]:=1;dy[2]:=2;dy[3]:=2;dy[4]:=1;
dy[5]:=-1;dy[6]:=-2;dy[7]:=-2;dy[8]:=-1;
for i:=1 to n do
for j:=1 to n do
h[i,j]:=0;
write('i->');readln(i);
write('j->');readln(j);
nsqr:=n*n;h[i,j]:=1;
try(2,i,j,q);
if q then begin
for i:=1 to n do
begin
for j:=1 to n do
write(h[i,j]:3);
writeln;
end;
end
else write('goo!');
end.
Характерною особливістю таких алгоритмів є те, що в них виконуються кроки в напрямку загального розв’язку, і всі ці кроки фіксуються таким чином, щоб пізнше можна було повернутись “всліпу” відкидаючи ті кроки, що не ведуть до загального розв’язку. Такий процес називають відкатом або поверненням (backtraking).