RSS    

   Математическая логика и теория алгоритмов - (реферат)

Математическая логика и теория алгоритмов - (реферат)

Дата добавления: март 2006г.

    Математическая логика и теория алгоритмов
    Содержание.
    Постановка задачи.
    Построение модели.
    Описание алгоритма
    Доказательство правильности алгоритма
    Блок-схема алгоритма
    Описание переменных и программа
    Расчёт вычислительной сложности
    Тестирование
    Список литературы
    Постановка задачи.

Перечислить все способы расстановки n ферзей на шахматной доске n на n, при которых они не бьют друг друга.

    Построение модели.

Очевидно, на каждой из n горизонталей должно стоять по ферзю. Будем называть k-позицией (для k = 0, 1, ...., n) произвольную расстановку k ферзей на k нижних горизонталях (ферзи могут бить друг друга). Нарисуем "дерево позиций": его корнем будет единственная 0-позиция, а из каждой k-позиции выходит n стрелок вверх в (k+1)-позиции. Эти n позиций отличаются положением ферзя на (k+1)-ой горизонтали. Будем считать, что расположение их на рисунке соответствует положению этого ферзя: левее та позиция, в которой ферзь расположен левее. Дерево позиций для n = 2

Данное дерево представлено только для наглядности и простоты представления для n=2.

Среди позиций этого дерева нам надо отобрать те n-позиции, в которых ферзи не бьют друг друга. Программа будет "обходить дерево" и искать их. Чтобы не делать лишней работы, заметим вот что: если в какой-то k-позиции ферзи бьют друг друга, то ставить дальнейших ферзей смысла нет. Поэтому, обнаружив это, мы будем прекращать построение дерева в этом направлении.

Точнее, назовем k-позицию допустимой, если после удаления верхнего ферзя оставшиеся не бьют друг друга. Наша программа будет рассматривать только допустимые позиции.

    Описание алгоритма.

Разобьем задачу на две части: (1) обход произвольного дерева и (2) реализацию дерева допустимых позиций.

Сформулируем задачу обхода произвольного дерева. Будем считать, что у нас имеется Робот, который в каждый момент находится в одной из вершин дерева. Он умеет выполнять команды:

вверх_налево (идти по самой левой из выходящих вверх стрелок) вправо (перейти в соседнюю справа вершину)

    вниз (спуститься вниз на один уровень)
    вверх_налево
    вправо
    вниз

и проверки, соответствующие возможности выполнить каждую из команд, называемые "есть_сверху", "есть_справа", "есть_снизу" (последняя истинна всюду, кроме корня). Обратите внимание, что команда "вправо" позволяет перейти лишь к "родному брату", но не к "двоюродному".

Будем считать, что у Робота есть команда "обработать" и что его задача обработать все листья (вершины, из которых нет стрелок вверх, то есть где условие "есть_сверху" ложно). Для нашей шахматной задачи команде обработать будет соответствовать проверка и печать позиции ферзей.

Доказательство правильности приводимой далее программы использует такие определения. Пусть фиксировано положение Робота в одной из вершин дерева. Тогда все листья дерева разбиваются на три категории: над Роботом, левее Робота и правее Робота. (Путь из корня в лист может проходить через вершину с Роботом, сворачивать влево, не доходя до нее и сворачивать вправо, не доходя до нее. ) Через (ОЛ) обозначим условие "обработаны все листья левее Робота", а через (ОЛН) - условие "обработаны все листья левее и над Роботом".

    Нам понадобится такая процедура:
    procedure вверх_до_упора_и_обработать
    {дано: (ОЛ), надо: (ОЛН)}
    begin
    {инвариант: ОЛ}
    while есть_сверху do begin
    вверх_налево
    end
    {ОЛ, Робот в листе}
    обработать;
    {ОЛН}
    end;
    Основной алгоритм:
    дано: Робот в корне, листья не обработаны
    надо: Робот в корне, листья обработаны
    {ОЛ}
    вверх_до_упора_и_обработать
    {инвариант: ОЛН}
    while есть_снизу do begin
    if есть_справа then begin {ОЛН, есть справа}
    вправо;
    {ОЛ}
    вверх_до_упора_и_обработать;
    end else begin
    {ОЛН, не есть_справа, есть_снизу}
    вниз;
    end;
    end;
    {ОЛН, Робот в корне => все листья обработаны}

Осталось воспользоваться следующими свойствами команд Робота (сверху записаны условия, в которых выполняется команда, снизу - утверждения о результате ее выполнения):

    {ОЛ, не есть_сверху} обработать {ОЛН}
    {ОЛ} вверх_налево {ОЛ}
    {есть_справа, ОЛН} вправо {ОЛ}
    {не есть_справа, ОЛН} вниз{ОЛН}

Теперь решим задачу об обходе дерева, если мы хотим, чтобы обрабатывались все вершины (не только листья).

Решение. Пусть x - некоторая вершина. Тогда любая вершина y относится к одной из четырех категорий. Рассмотрим путь из корня в y. Он может: а) быть частью пути из корня в x (y ниже x);

    б) свернуть налево с пути в x (y левее x);
    в) пройти через x (y над x);
    г) свернуть направо с пути в x (y правее x);

В частности, сама вершина x относится к категории в). Условия теперь будут такими:

    (ОНЛ) обработаны все вершины ниже и левее;
    (ОНЛН) обработаны все вершины ниже, левее и над.
    Вот как будет выглядеть программа:
    procedure вверх_до_упора_и_обработать
    {дано: (ОНЛ), надо: (ОНЛН)}
    begin
    {инвариант: ОНЛ}
    while есть_сверху do begin
    обработать
    вверх_налево
    end
    {ОНЛ, Робот в листе}
    обработать;
    {ОНЛН}
    end;
    Основной алгоритм:
    дано: Робот в корне, ничего не обработано
    надо: Робот в корне, все вершины обработаны
    {ОНЛ}
    вверх_до_упора_и_обработать
    {инвариант: ОНЛН}
    while есть_снизу do begin
    if есть_справа then begin {ОНЛН, есть справа}
    вправо;
    {ОНЛ}
    вверх_до_упора_и_обработать;
    end else begin
    {ОЛН, не есть_справа, есть_снизу}
    вниз;
    end;
    end;
    {ОНЛН, Робот в корне => все вершины обработаны}

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

Под "обработано ниже и левее" будем понимать "ниже обработано по разу, слева обработано полностью (листья по разу, остальные по два)". Под "обработано ниже, левее и над" будем понимать "ниже обработано по разу, левее и над полностью".

    Программа будет такой:
    procedure вверх_до_упора_и_обработать
    {дано: (ОНЛ), надо: (ОНЛН)}
    begin
    {инвариант: ОНЛ}
    while есть_сверху do begin
    обработать
    вверх_налево
    end
    {ОНЛ, Робот в листе}
    обработать;
    {ОНЛН}
    end;
    Основной алгоритм:
    дано: Робот в корне, ничего не обработано
    надо: Робот в корне, все вершины обработаны
    {ОНЛ}
    вверх_до_упора_и_обработать
    {инвариант: ОНЛН}
    while есть_снизу do begin
    if есть_справа then begin {ОНЛН, есть справа}
    вправо;
    {ОНЛ}
    вверх_до_упора_и_обработать;
    end else begin
    {ОЛН, не есть_справа, есть_снизу}
    вниз;
    обработать;
    end;
    end;
    {ОНЛН, Робот в корне => все вершины обработаны полностью}
    Доказательство правильности алгоритма.

Докажем, что приведенная программа завершает работу (на любом конечном дереве). Доказательство. Процедура вверх_налево завершает работу (высота Робота не может увеличиваться бесконечно). Если программа работает бесконечно, то, поскольку листья не обрабатываются повторно, начиная с некоторого момента ни один лист не обрабатывается. А это возможно, только если Робот все время спускается вниз. Противоречие.

    Блок-схема алгоритма.
    Описание переменных и программа.

Теперь реализуем операции с деревом позиций. Позицию будем представлять с помощью переменной k: 0...n (число ферзей) и массива c: array [1...n] of 1...n (c [i] - координаты ферзя на i-ой горизонтали; при i > k значение c [i] роли не играет). Предполагается, что все позиции допустимы (если убрать верхнего ферзя, остальные не бьют друг друга).

    program queens;
    const n = .... ;
    var k: 0...n;
    c: array [1...n] of 1...n;
    procedure begin_work; {начать работу}
    begin
    k : = 0;
    end;
    function danger: boolean; {верхний ферзь под боем}
    var b: boolean;
    i: integer;
    begin
    if k     danger : = false;
    end else begin
    b : = false; i : = 1;
    {b верхний ферзь под боем ферзей с номерами < i}
    while i <> k do begin
    b : = b or (c[i]=c[k]) {вертикаль}
    or (abs(c[i]-c[k])=abs(i-k)); {диагональ}
    i : = i+ 1;
    end;
    danger : = b;
    end;
    end;
    function is_up: boolean {есть_сверху}
    begin
    is_up : = (k < n) and not danger;
    end;
    function is_right: boolean {есть_справа}
    begin
    is_right : = (k > 0) and (c[k] < n);
    end;
    {возможна ошибка: при k=0 не определено c[k]}
    function is_down: boolean {есть_снизу}
    begin
    is_up : = (k > 0);
    end;
    procedure up; {вверх_налево}
    begin {k < n}
    k : = k + 1;
    c [k] : = 1;
    end;
    procedure right; {вправо}
    begin {k > 0, c[k] < n}
    c [k] : = c [k] + 1;
    end;
    procedure down; {вниз}
    begin {k > 0}
    k : = k - 1;
    end;
    procedure work; {обработать}
    var i: integer;
    begin
    if (k = n) and not danger then begin
    for i : = 1 to n do begin
    write (' ');
    end;
    writeln;
    end;
    end;
    procedure UW; {вверх_до_упора_и_обработать}
    begin
    while is_up do begin
    up;
    end
    work;
    end;
    begin
    begin_work;
    UW;
    while is_down do begin
    if is_right then begin
    right;
    UW;
    end else begin
    down;
    end;
    end;
    end.
    Расчёт вычислительной сложности.
    Емкостная сложность:

В программе используется одномерный массив размерности n, поэтому объём входа и объём выхода совпадают и равны n. Количество пременных равно 3(i, b, k) + 1(const n), т. е. объём промежуточных данных равен 4.

    С(n)=n+4
    Временная сложность:

Если рассматривать обработку каждого листа, без проверки на пути к нему, то временная сложность T(n) = n0+n1+n2+n3+…+nn .

Но в случае, когда каждая вершина проверяется, временная сложность T(n) = o(n0+n1+n2+n3+…+nn). И это тем вернее, чем больше n. Данный вывод получен на основе приведённых ниже статистических данных:

    1
    2
    3
    4
    5
    6
    7
    Общее кол-во листьев
    2
    7
    40
    341
    3906
    55987
    960800
    Кол-во вершин построенного дерева.
    2
    3
    4
    17
    54
    153
    552
    Время построения(сек)
                                8
    9
    10
    11
    12
    13
    Общее кол-во листьев
    Кол-во вершин построенного дерева.
    2057
    8394
    35539
    166926
    856189
    4674890
    Время построения(сек)
        0. 21
    1. 20
    6. 48
    37. 12
    231. 29
    Тестирование.

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

    n=4
    
    

Т. е. количество расстановок равно 2. Ниже приведена таблица зависимости от n количества решений (R).

    n =
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    R=
    1
    0
    0
    2
    10
    4
    40
    92
    352
    724
    2680
    14200
    73712
    Cписок литературы.

Кузнецов О. П. Адельсон-Вельский Г. М. Дискретная математика для инженера. – М. : Энергоатомиздат, 1988. Евстигнеев В. А. Применение теории графов в программировании. – М. :Наука, 1984. Основной алгоритм находился на BBS “Master of Univercity” в файле shen. rar в файловой области “Bardak” (тел. 43-27-03; время работы 21. 00– 7. 00; FTN адрес – 2: 5090/58).


Новости


Быстрый поиск

Группа вКонтакте: новости

Пока нет

Новости в Twitter и Facebook

                   

Новости

© 2010.