Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекции_Паскал.doc
Скачиваний:
2
Добавлен:
21.09.2019
Размер:
1.21 Mб
Скачать

Алгоритми з поверненням. Розв’язок задачі про вісьмох ферзів

Задача про вісьмох ферзів — добре відомий приклад використання методів проб і помилок і алгоритмів з поверненнями. У 1850 r. цю задачу досліджував К. Ф. Гаусс, однак цілком він її так і не вирішив. Це нікого не повинно дивувати. Для подібних задач характерна відсутність аналітичного рішення. Вони вимагають величезної кількості виснажливої роботи, терпіння й акуратності. Тому такі задачі стали майже винятково прерогативою електронних обчислювальних машин, адже їм ці властивості притаманні в значно більшому ступені, ніж людині, нехай і геніальній.

Задача про вісьмох ферзів формулюється так: вісім ферзів потрібно розставити на шахівниці так, щоб один ферзь не загрожував іншому. Скориставшись тієї ж що і при рішенні задачі «хід коня» схемою як шаблоном, легко одержуємо грубий варіант рішення:

PROCEDURETry(i: INTEGER);

BEGIN

ініціалізація вибору положення i-го ферзя;

REPEAT

вибір чергового положення;

IF безпечне THEN

BEGIN

поставити ферзя

IF i<8 THEN BEGIN

Try(i+l);

IF невдача THEN забрати ферзя

END

END

UNTIL удача OR місць більше немає

ЕND;

Щоб йти далі, потрібно зупинитися на якому-небудь представленні для даних. Оскільки із шахових правил ми знаємо, що ферзь б'є усі фігури, що знаходяться на тій же самій вертикалі, горизонталі чи діагоналі, то заключаємо, що на кожній вертикалі може знаходитися один і тільки один ферзь, тому при пошуку місця для i-ro ферзя можна обмежити себе лише i-й вертикаллю. Таким чином, параметр і стає індексом вертикалі, а процес вибору можливого місця розташування обмежується вісьма припустимими значеннями для індексу горизонталі j.

Залишається вирішити питання: як представляти на дошці ці вісім ферзів? Очевидно, дошку знову можна було б представити у виді квадратної матриці, але після невеликих міркувань ми виявляємо, що це значно ускладнило б перевірку безпеки поля. Звичайно, подібне рішення небажане, оскільки така операція виконується дуже часто. Тому хотілося б зупинитися на такому представленні даних, щоб, наскільки це можливо спростило б перевірку. У цій ситуації найкраще робити безпосередньо доступною саме ту інформацію, що дійсно важлива і найчастіше використовується. У нашому випадку це не поля, зайняті ферзями, а відомості про те, чи знаходиться вже ферзь на даній горизонталі чи діагоналі. (Ми вже знаємо, що на кожній k-й вертикалі (1 ≤ k ≤? і) розташований рівно один ферзь.) Ці розуміння приводять до таких описів змінних:

VAR x: ARRAY [l.. 8] OF INTEGER;

a:ARRAY [1.. 8] OF BOOLEAN;

b: ARRAY [bl.. b2] OFBOOLEAN;

с: ARRAY [cl.. c2] OF BOOLEAN;

де

хi позначає місце розташування ферзя на i-й вертикалі;

aj указує, що на j-й горизонталі ферзя немає;

bk указує, що на k-й /-діагоналі ферзя немає;

ck указує, що на k-й \-діагоналі ферзя немає.

Вибір границь індексів bl, b2, cl, c2 визначається, виходячи зі способу обчислення індексів для b і с, на /-діагоналі у всіх полів постійна сума координат і та j, а на \-діагоналі постійна їхня різниця. Відповідні обчислення приведені в nporpамі. Якщо ми уже визначили дані так , то оператор «Поставити ферзя» перетворюється в такі оператори:

x [і] := j; a [j] := FALSE; b [і + j] := FALSE; c[i-j]:=FALSE

а оператор «Забрати ферзя» у такі:

a [j] := TRUE; b [і + j] := TRUE; з [і - j] := TRUE

Умова «безпечне» виконується, якщо поле з координатами <i,j,> лежить на горизонталі і вертикалі, які ще не зайняті. Отже, йому відповідає логічний вираз:

a[j] and b[i + j] and c[i-J]

На цьому створення алгоритму закінчується; цілком він представлений у програмі.

program ferz;

uses crt;

var a: array[1..8] of boolean;

b: array[2..16] of boolean;

c:array[-7..7] of boolean;

x: array[1..8] of integer;

i:integer;q:boolean;

procedure try(i:integer;var q:boolean);

var j:integer;

begin

j:=0 ;

repeat

j:=j+1;q:=false;

if a[j] and b[i+j] and c[i-j]

then

begin

x[i]:=j;

a[j]:=false;b[i+j]:=false;c[i-j]:=false;

if i<8 then

begin

try(i+1,q);

if not q then

begin

a[j]:=true;b[i+j]:=true;c[i-j]:=true

end;

end

else q:=true;

end;

until q or(j=8);

end;

begin

clrscr;

for i:=1 to 8 do a[i]:=true;

for i:=2 to 16 do b[i]:=true;

for i:=-7 to 7 do c[i]:=true;

try(1,q);

for i:=1 to 8 do write(x[i]:3);

writeln

end.

На мал. приведене отримане рішення x = (l, 5, 8, 6, 3, 7, 2, 4).

Перш ніж закінчити розбір задач, «присвячених» шахівниці, ми скористаємося задачею про вісьмох ферзів і представимо одне важливе узагальнення алгоритму проб і помилок. У загальних словах, мова йде про знаходження не одного, а всіх рішень поставленої задачі.

Т аке узагальнення виходить досить легко. Нагадаємо, що формування можливих кандидатів відбувається регулярним образом, що гарантує, що жоден кандидат не зустрінеться більш ніж один раз. Така властивість алгоритму забезпечується тим, що пошук йде по дереву кандидатів так, що кожна з його вершин проходиться точно один раз. Це дозволяє, якщо знайдено і належним образом зафіксовано одне рішення, просто переходити до наступного кандидату, пропонованому згаданим процесом систематичного перебору. Загальну схему такого процесу можна «вивести» зі схеми.

PROCEDURE Try(i:INTEGER);

VAR k:INTEGER;

BEGIN

FOR k:=l TO m DO

BEGIN

вибір k-го кандидата;