Опубликован: 12.02.2014 | Доступ: свободный | Студентов: 920 / 238 | Длительность: 11:22:00
Специальности: Программист
Лекция 13:

Поиск в пространстве состояний

< Лекция 12 || Лекция 13: 123 || Лекция 14 >

Следующая программа посвящена решению задачи о рыцарях и оруженосцах (Алкуин, VIII в.). Три рыцаря, каждый со своим оруженосцем, хотят переправиться через реку, с левого берега на правый. В их распоряжении имеется двухместная лодка. Ни один оруженосец не может оставаться где-либо без своего рыцаря в присутствии других рыцарей. Как им переправиться на другой берег?

Имеются варианты задачи для четырех или пяти рыцарей и трехместной лодки. Двухместной лодки в этом случае недостаточно. Если рыцарей шесть и более, то недостаточно и трехместной лодки. Четырехместной лодки достаточно для переправы любого количества рыцарей.

Для того чтобы можно было использовать предикат subset в другом модуле, его объявление нужно перенести в декларацию класса ex1 (листинг 13.4).

predicates
    subset : (positive, A*, A* [out], A* [out]) nondeterm.
Пример 13.4. Декларация класса ex1
open core, console, list, string, depth, ex1

class facts
    n : positive := 0.
    capacity : positive := 2.      % вместимость лодки

domains
    knightOrSquire = k(integer); s(integer).
    kstate = tuple(loc, knightOrSquire*, knightOrSquire*).
    loc = left; right.

class predicates
    kmove : move{kstate}.
    moveFromTo : (positive, knightOrSquire*, knightOrSquire*,
        knightOrSquire* [out], knightOrSquire* [out], 
        knightOrSquire* [out]) nondeterm.
    unsafe: (knightOrSquire*) determ.
    f: (knightOrSquire*) -> string.
    g: (knightOrSquire) -> string.
clauses
    kmove(tuple(left, L, R), Str) = tuple(right, L1, R1):-
        K = std::downTo(capacity, 2),
        moveFromTo(K, L, R, L1, R1, B),
        Str = format("% from left to right", f(B)).
    kmove(tuple(right, L, R), Str) = tuple(left, L1, R1):-
        K = std::fromTo(1, capacity),
        moveFromTo(K, R, L, R1, L1, B),
        Str = format("% from right to left", f(B)).

    moveFromTo(N, FromBef, ToBefore, FromAfter, ToAfter, Boat):-
        subset(N, FromBef, Boat, FromAfter),
        not(unsafe(Boat)),
        not(unsafe(FromAfter)),
        ToAfter = sort(append(Boat, ToBefore)),
        not(unsafe(ToAfter)).

    unsafe(L):-
        s(N) = getMember_nd(L),
        not(k(N) = getMember_nd(L)),
        k(_) = getMember_nd(L),
        !.

    f([S]) = format("% moves", g(S)):- !.
    f(L) = format("% move", 
        concatWithDelimiter(map(L, {(X) = g(X)}), ", ")).

    g(k(N)) = concat("knight ", toString(N)).
    g(s(N)) = concat("squire ", toString(N)).

    run():-
        L = sort([s(1), k(1), s(2), k(2), s(3), k(3)]),
        N = list::length(L) div 2,
        capacity := if N < 4 then 2 elseif N < 6 then 3 else 4 end if,
        Start = tuple(left, L, []),
        Goal = tuple(right, [], L),
        tuple([S0 | P], Moves) = depthSearch(kmove, Start, Goal, _W),
            V = varM::new(1),
            n := n + 1,
            write("\t", S0), nl,
            forAll(zip(P, Moves), {(tuple(X, S)):-
                writef("%. %\n\t%\n", V:value, S, X), 
                V:value := V:value + 1}), nl,
         fail;
         write("\nвсего - ", n),
        _ = readLine().
Пример 13.5. Задача о рыцарях и оруженосцах. Модуль ex2

Предикат downTo недетерминированно возвращает целые числа в указанных пределах в порядке убывания. Предикат list::length/1 возвращает количество элементов списка.

Для трех рыцарей задача имеет 486 оптимальных решений, которые содержат по 11 переходов. Для четырех рыцарей и трехместной лодки существует 15600 различных оптимальных решений, они содержат по 9 переходов.

Следующая программа посвящена решению задачи о миссионерах и каннибалах (XIX в.). Три миссионера и три каннибала хотят переправиться через реку с левого берега на правый. В их распоряжении имеется двухместная лодка. Если где-либо каннибалов будет больше, чем миссионеров, то они их съедят. Как они могут переправиться на другой берег?

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

open core, console, list, string, depth

class facts
    n : positive := 0.
    capacity : positive := 2.      % вместимость лодки

domains
    mstate = tuple(loc, positive*, positive*).
    loc = left; right.

class predicates
    mcmove : move{mstate}.
    mcmoveFromTo: (positive, positive, positive*, positive*,
        positive* [out], positive* [out]) determ.
    f: (positive, positive) -> string.
    g: (positive) -> string.
    b: (positive, positive) determ.
clauses
    mcmoveFromTo(Mb, Cb, [MFromBefore, CFromBefore], 
        [MToBefore, CToBefore], [MFromAfter, CFromAfter],
            [MToAfter, CToAfter]):-
        Cb <= CFromBefore,
        Mb <= MFromBefore,
        b(Mb, Cb),
        MFromAfter = MFromBefore - Mb,
        CFromAfter = CFromBefore - Cb,
        b(MFromAfter, CFromAfter),
        MToAfter = MToBefore + Mb,
        CToAfter = CToBefore + Cb,
        b(MToAfter, CToAfter).

    mcmove(tuple(left, L, R), Str) = tuple(right, L1, R1):-
        K = std::downTo(capacity, 2),
        Cb = std::downTo(K, 0),
        Mb = K - Cb,
        mcmoveFromTo(Mb, Cb, L, R, L1, R1),
        Str = format("% % from left to right", f(Mb, Cb), g(Mb + Cb)).
    mcmove(tuple(right, L, R), Str) = tuple(left, L1, R1):-
        K = std::fromTo(1, capacity),
        Cb = std::fromTo(0, K),
        Mb = K - Cb,
        mcmoveFromTo(Mb, Cb, R, L, R1, L1),
        Str = format("% % from right to left", f(Mb, Cb), g(Mb + Cb)).

    f(0, 1) = "1 cannibal":- !.
    f(0, C) = format("% cannibals", C):- !.
    f(1, 0) = "1 missionary":- !.
    f(M, 0) = format("% missionaries", M):- !.
    f(M, C) = format("%, %", f(M, 0), f(0, C)).

    g(1) = "moves":- !.
    g(_) = "move".

    b(M, C):-
        if M > 0 then M >= C end if.

    run():-
        Start = tuple(left, [3, 3], [0, 0]),
        Goal = tuple(right, [0, 0], [3, 3]),
        capacity := 2,
        tuple([S0 | P], Moves) = depthSearch(mcmove, Start, Goal, _),
            V = varM::new(1),
            n := n + 1,
            write("\t", S0), nl,
            forAll(zip(P, Moves), {(tuple(X, S)):-
                writef("%. %\n\t%\n", V:value, S, X), 
                V:value := V:value + 1}), nl,
         fail;
         write("\nвсего - ", n),
        _ = readLine().
Пример 13.6. Задача о миссионерах и каннибалах. Модуль ex3

Предикат zip/2 для двух списков одинаковой длины возвращает список, составленный из пар элементов из этих списков с одинаковыми индексами.

Имеется четыре оптимальных решения для задачи о трех миссионерах, 32 оптимальных решения для задачи о четырех миссионерах и 25 оптимальных решений для задачи с пятью миссионерами.

< Лекция 12 || Лекция 13: 123 || Лекция 14 >
Жаныл Айкын
Жаныл Айкын
Rustam Inatov
Rustam Inatov

Доброго времени суток, подскажите пожалуйста, visual prolog examples, pie, vip7.5 - это все, где я могу скачать? (в смысле) может быть на сайте есть какой-то архив? Увы я не нашел его.

Подскажите, пожалуйста.

С уважением, Рустам.