Модераторы: Poseidon
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> "Пятнашки" (в общем случае игра «N^2-1».). Авторешитель на Прологе. Срочно! 
:(
    Опции темы
mayaer
Дата 27.1.2007, 06:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 27.1.2007

Репутация: нет
Всего: нет



-> Задачу желательно решить как можно быстрее на turbo prolog 2.0, лучше в виде dll на visual prolog 5.2.

Это обобщение знаменитой игры «15». Имеется поле размером NxN клеток, на котором расположено N^2-1 пронумерованных фишек. Требуется выстроить фишки в определенном порядке.

Как понятно полный перебор не идет! Для классических пятнашек у нас просто не хватит памяти.

Важный метод сокращения перебора - использование оценочных "эвристических" функций, позволяющих грубо оценить, насколько "хорошим" (или "плохим") является текущее состояние. Пример, шахмты, Го.

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

Далеее приводятся примерный ход решения на swi-prolog.

1) С использованием "эвристической" функции:

Код

solve(State, Answer) :-
   starting(S),
   h(S,Val),
   search([d(Val,S,[])], [S], d(_,State,Path)),
   reverse(Path,Answer).

search([d(Val,State,Path)|_], _, d(Val,State,Path)) :-
    solution(State).
search([d(_,State,Path)|FR], Seen, Answer) :-
    findall(d(_,NewState,[Op|Path]),
            (arc(State,Op,NewState), \+ member(NewState, Seen)),
            NBs),
    checklist(hvalue, NBs),
    mark(NBs, Seen, NewSeen),
    join(NBs, FR, NewFR),
    search(NewFR, NewSeen, Answer).

mark([], Seen, Seen).
mark([d(_,State,_)|Sts], Seen, NewSeen) :-
    mark(Sts,[State|Seen],NewSeen).

Встроенный предикат checklist(Pred,List) применяет предикат ко всем элементам списка. Обычное его назначение - проверить, что все элементы удовлетворяют некоторому условию. Но в нашем случае мы не проверяем, а устанавливаем этот факт, присваивая значения оценки с помощью предиката hvalue.

Код

hvalue(d(Val,State,_)) :- h(State,Val).

Предикат join определяет последовательность обработки вершин. Определив его соответствующим образом, получим эвристический поиск в глубину, 
Код

join(Ns,FR,FR1):- sort(Ns,Ns1), append(Ns1,FR,FR1).

или в ширину
Код

join(Ns,FR,FR1):- sort(Ns,Ns1), merge(Ns1,FR,FR1).

Встроенная процедура merge выполняет слияние упорядоченных списков и является составной частью сортировки слиянием. Таким образом, это просто более эффективный способ выполнить
Код

join(Ns,FR,FR1):- append(Ns,FR,NF), sort(NF,FR1).

Эти два метода известны под названиями "восхождение на гору" (hill climbing), и "сначала-лучший" (best-first search).

Данное решение достаточно быстрое, но решение получается далекое от оптимального.

2) Алгоритм A* 

Чтобы получить наилучшее решение, мы должны не просто выбирать самое многообещающее продолжение, но учитывать и уже пройденный путь. Удобнее это делать, если есть некоторая простая связь между оценочной функцией и длиной пути. Но наша функция как раз имеет такую связь. Она дает оценку длины пути от данного состояния к целевому. Причем эта оценка всегда занижена. А это означает, что мы можем изменить критерий упорядочивания вершин и выбирать ту из них, для которой сумма длины уже пройденного пути и оценки пути, который предстоит пройти, минимальна. Все, что нам осталось сделать - изменить процедуру оценки.

Код

hvalue(d(Val,State,Path)) :-
    h(State,Vs),
    length(Path,Vp),
    Val is Vs + Vp.


Новая версия программы находит действительно оптимальное решение за приемлемое время (за несколько минут). В литературе по искусственному интеллекту этот метод носит имя "Алгоритм A*". Иногда используют "взвешенный A*", когда какой либо составляющей придают больший вес.

Код

hvalue(d(Val,State,Path)) :-
    h(State,Vs),
    length(Path,Vp),
    weight(Wh,Wp),
    Val is Wh*Vs + Wp*Vp.


В крайнем случае, когда Wh = 0 мы получим более громоздкую реализацию поиска в ширину. В другом крайнем случае, когда Wp = 0, получаем поиск "сначала лучший". Идея состоит в том, чтобы пожертвовать оптимальностью решения ради скорости.

3) Наличие оценочной функции позволяет организовать более интеллектуальное отсечение при поиске с ограничением на глубину. Мы можем отсекать ветви несколько раньше, как только оценка длины пути вместе с длиной уже пройденного пути превзойдет заданное ограничение. Поскольку эвристическая функция не переоценивает длину предстоящего пути, мы можем быть уверены, что отсекаемые ветви будут заведомо длиннее. Комбинируя эту идею с методом итерационного углубления, получим популярный "алгоритм IDA*" (Iterative Deepening A*). В отличие от DFID, ограничение на глубину при переходе к следующей итерации можно увеличивать большими шагами. Новое ограничение можно смело установить равным минимальной оценке всех отброшенных на предыдущей итерации вершин. Эту оценку мы будем передавать вместо флага.

 Вот примерноый ход решение на swi-prolog с использованием Алгоритм IDA* и оценочной "эвристической" функции:

Код

solve(State, Answer) :-
    starting(Start),
    h(Start,Val),
    search([d(Val,Start,[])],Val,0,[Start],d(_,State,Path)),
    reverse(Path,Answer).
search([d(Val,State,Path)|_], _, _,  _, d(Val,State,Path)) :-
    solution(State).
search([d(Val,_,_)|FR], Lim,B, Seen, Answer) :-
    Val > Lim,!,
    adjust_bound(Val,B,B1),
    search(FR, Lim,B1, Seen, Answer).
search([d(_,State,Path)|FR], Lim,B, Seen, Answer) :-
    findall(d(_,NewState,[Op|Path]),
           (arc(State, Op, NewState), \+ member(NewState, Seen)),
            NBs),
    checklist(hvalue, NBs),
    mark(NBs, Seen, NewSeen),
    append(NBs, FR, NewFR),!,
    search(NewFR, Lim,B, NewSeen, Answer).
search([], _, B, _, Answer) :-
   B>0,
   starting(Start),
   h(Start,Val),
   search([d(Val,Start,[])],B,0,[Start],Answer).
adjust_bound(V,B,B) :- B>0,B =< V,!.
adjust_bound(V,B,V).


где h, например, "эвристическая" функция - "манхэттенское" расстояние (|XX0|+|YY0|) от каждой фишки до ее законного места:

Код

h(B, HV) :-  sumdist(9,B, 0, HV).
sumdist(0, _, S, S) :- !.
sumdist(N, B, A, S) :-
    arg(N, B, P),
    mdist(N, P, D),
    A1 is A + D,
    M is N-1,
    sumdist(M, B, A1, S).
mdist(A, B, D) :-
  Ax is (A-1) mod 3, Ay is (A-1) // 3,
  Bx is (B-1) mod 3, By is (B-1) // 3,
  D is abs(Ax-Bx) +  abs(Ay-By) .


***3) Третье решение самое предпочтительное.***


M
Guedda
Не забываем выделять код!


Это сообщение отредактировал(а) Guedda - 1.2.2007, 10:39
PM MAIL   Вверх
mayaer
Дата 30.1.2007, 06:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 27.1.2007

Репутация: нет
Всего: нет



Кстати последня эвристическая функция дана для случая n=3.

Мда, видно придется решать самому, как обычно, за пару дней. Только вот бы еще их выделить smile
PM MAIL   Вверх
mayaer
Дата 9.2.2007, 08:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 27.1.2007

Репутация: нет
Всего: нет



Я, конечно, все понимаю - все заняты. Но может хоть кто-то знает ссылки, у кого-то есть исходники (можно игры в 8, лучше конечно 15). Просто очень критичная ситуация.
Я представляю логику, но нет не времени, не сил разбираться в Prolog, особенно VP (а в других средах и языках написать техническое задание не позволяет, я бы конечно с радостью лучше написал в c++ или на java).

Буду всем плагодарен, кто хоть чем-то поделится.

Хорошие поступки остаются в истории )

В книге братко есть описание процедур для головоломки "игра в восемь", предназначенные для использования программой поиска с предпочтением (которая приведена ниже) (Вот это бы все это переделать для пятнашек и на VP 5.2 или 6.3 в виде DLL):

Код


/*  Процедуры, отражающие специфику головоломки
"игра в восемь".
Текущая ситуация представлена списком положений фишек;
первый элемент списка соответствует пустой клетке.
Пример:
  
1  1   2   3  
2  8        4
3  7   6   5 

    1   2   3

         Эта позиция представляется так:
[2/2, 1/3, 2/3, 3/3, 3/2, 3/1, 2/1, 1/1, 1/2]
 

"Пусто" можно перемещать в любую соседнюю клетку,
т.е. "Пусто" меняется местами со своим соседом.
*/

        после( [Пусто | Спис], [Фшк | Спис1], 1) :-
                                    % Стоимости всех дуг равны 1
                перест( Пусто, Фшк, Спис, Спис1).
                                    % Переставив Пусто и Фшк, получаем СПИС1

        перест( П, Ф, [Ф | С], [П | С] ) :-
                расст( П, Ф, 1).

        перест( П, Ф, [Ф1 | С], [Ф1 | С1] ) :-
                перест( П, Ф, С, С1).

        расст( X/Y, X1/Y1, Р) :-
                        % Манхеттеновское расстояние между клетками
                расст1( X, X1, Рх),
                расст1( Y, Y1, Ру),
                Р is Рх + Py.

        расст1( А, В, Р) :-
                Р is А-В,    Р >= 0,  ! ;
                Р is B-A.

% Эвристическая оценка  h  равна сумме расстояний фишек
% от их "целевых" клеток плюс "степень упорядоченности",
% умноженная на 3

        h( [ Пусто | Спис], H) :-
                цель( [Пусто1 | Цспис] ),
                сумрасст( Спис, ЦСпис, Р),
                упоряд( Спис, Уп),
                Н is Р + 3*Уп.

        сумрасст( [ ], [ ], 0).

        сумрасст( [Ф | С], [Ф1 | С1], Р) :-
                расст( Ф, Ф1, Р1),
                сумрасст( С, Cl, P2),
                Р is P1 + Р2.

        упоряд( [Первый | С], Уп) :-
                упоряд( [Первый | С], Первый, Уп).

        упоряд( [Ф1, Ф2 | С], Первый, Уп) :-
                очки( Ф1, Ф2, Уп1),
                упоряд( [Ф2 | С], Первый, Уп2),
                Уп is Уп1 + Уп2.

        упоряд( [Последний], Первый, Уп) :-
                очки( Последний, Первый, Уп).

        очки( 2/2, _, 1) :-  !.                         % Фишка в центре - 1 очко

        очки( 1/3, 2/3, 0) :-  !.
                                % Правильная последовательность - 0 очков
        очки( 2/3, 3/3, 0) :-  !.

        очки( 3/3, 3/2, 0) :-  !.

        очки( 3/2, 3/1, 0) :-  !.

        очки( 3/1, 2/1, 0) :-  !.

        очки( 2/1, 1/1, 0) :-  !.

        очки( 1/1, 1/2, 0) :-  !.

        очки( 1/2, 1/3, 0) :-  !.

        очки( _, _, 2).                 % Неправильная последовательность

        цель( [2/2, 1/3, 2/3, 3/3, 3/2, 3/1, 2/1, 1/1, 1/2] ).

% Стартовые позиции для трех головоломок

        старт1( [2/2, 1/3, 3/2, 2/3, 3/3, 3/1, 2/1, 1/1, 1/2] ).
                                    % Требуется для решения 4 шага

        старт2( [2/1, 1/2, 1/3, 3/3, 3/2, 3/1, 2/2, 1/1, 2/3] ).
                                    % 5 шагов

        старт3( [2/2, 2/3, 1/3, 3/1, 1/2, 2/1, 3/3, 1/1, 3/2] ).
                                    % 18 шагов

% Отображение решающего пути в виде списка позиций на доске

        показреш( [ ]).

        показреш( [ Поз | Спис] :-
                показреш( Спис),
                nl, write( '---'),
                показпоз( Поз).

% Отображение позиции на доске

        показпоз( [S0, S1, S2, S3, S4, S5, S6, S7, S8] ) :-
                принадлежит Y, [3, 2, 1] ),                       % Порядок Y-координат
                nl, принадлежит X, [1, 2, 3] ),                 % Порядок Х-координат
                принадлежит( Фшк-X/Y,
                [' '-S0, 1-S1, 2-S2, 3-S3, 4-S4, 5-S5, 6-S6, 7-S7, 8-S8]),
                write( Фшк),
                fail.                     %Возврат с переходом к следующей клетке

                показпоз( _ ).



Программа поиска с предпочтением:

Код

% Поиск с предпочтением

        эврпоиск( Старт, Решение):-
                макс_f( Fмакс).                     % Fмакс  >  любой  f-оценки
                расширить( [ ], л( Старт, 0/0), Fмакс, _, да, Решение).

        расширить( П, л( В, _ ), _, _, да, [В | П] ) :-
                цель( В).

        расширить( П, л( В, F/G), Предел, Дер1, ЕстьРеш, Реш) :-
            F <= Предел,
            ( bagof( B1/C, ( после( В, В1, С), not принадлежит( В1, П)),
                            Преемники),   !,
                преемспис( G, Преемники, ДД),
                опт_f( ДД, F1),
                расширить( П, д( В, F1/G, ДД), Предел, Дер1,
                                                                    ЕстьРеш, Реш);
            ЕстьРеш = никогда).                 % Нет преемников - тупик

        расширить( П, д( В, F/G, [Д | ДД]), Предел, Дер1,
                                                                  ЕстьРеш, Реш):-
                F <= Предел,
                опт_f( ДД, OF), мин( Предел, OF, Предел1),
                расширить( [В | П], Д, Предел1, Д1, ЕстьРеш1, Реш),
                продолжить( П, д( В, F/G, [Д1, ДД]), Предел, Дер1,
                                                            ЕстьРеш1, ЕстьРеш, Реш).

        расширить( _, д( _, _, [ ]), _, _, никогда, _ ) :-  !.
                                   % Тупиковое дерево - нет решений

        расширить( _, Дер, Предел, Дер, нет, _ ) :-
                f( Дер, F), F > Предел.           % Рост остановлен
        продолжить( _, _, _, _, да, да, Реш).

        продолжить( П, д( В, F/G, [Д1, ДД]), Предел, Дер1,
                                                           ЕстьРеш1, ЕстьРеш, Реш):-
                ( ЕстьРеш1 = нет, встав( Д1, ДД, НДД);
                  ЕстьРеш1 = никогда, НДД = ДД),
                опт_f( НДД, F1),
                расширить( П, д( В, F1/G, НДД), Предел, Дер1,
                                                                                ЕстьРеш, Реш).

        преемспис( _, [ ], [ ]).

        преемспис( G0, [В/С | ВВ], ДД) :-
                G is G0 + С,
                h( В, Н),                                   % Эвристика h(B)
                F is G + Н,
                преемспис( G0, ВВ, ДД1),
                встав( л( В, F/G), ДД1, ДД).

% Вставление дерева Д в список деревьев ДД с сохранением
% упорядоченности по f-оценкам

        встав( Д, ДД, [Д | ДД] ) :-
                f( Д, F), опт_f( ДД, F1),
                F =< F1,  !.

        встав( Д, [Д1 | ДД], [Д1 | ДД1] ) ) :-
                встав( Д, ДД, ДД1).

% Получение f-оценки

        f( л( _, F/_ ), F).                                             % f-оценка листа

        f( д( _, F/_, _ ) F).                                          % f-оценка дерева

        опт_f( [Д | _ ], F) :-                                       % Наилучшая f-оценка для
             f( Д, F).                                                     % списка деревьев

        опт_f( [ ], Fмакс) :-                                      % Нет деревьев:
              мaкс_f( Fмакс).                                      % плохая f-оценка

        мин( X, Y, X) :-
             Х =< Y,   !.

        мин( X, Y, Y).



Кто хоть чем-то поделится, буду очень благодарен.
PM MAIL   Вверх
mayaer
Дата 9.2.2007, 08:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 27.1.2007

Репутация: нет
Всего: нет



Если у кого что-есть можете скидывать на:

[email protected]

Добавлено @ 08:46 
Взаранее всем спасибо!
PM MAIL   Вверх
Винитарх
Дата 10.2.2007, 12:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 9
Регистрация: 21.1.2007

Репутация: нет
Всего: нет



Пятнашки были решены на Проложьем форуме года три назад. Зайдите на progz и поищите.
PM MAIL   Вверх
mayaer
Дата 11.2.2007, 14:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 5
Регистрация: 27.1.2007

Репутация: нет
Всего: нет



На да, там что-то есть. Но что-то не очень понятно на чем писалось, да и реализация далека от совершенства smile
Мда...
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Центр помощи"

ВНИМАНИЕ! Прежде чем создавать темы, или писать сообщения в данный раздел, ознакомьтесь, пожалуйста, с Правилами форума и конкретно этого раздела.
Несоблюдение правил может повлечь за собой самые строгие меры от закрытия/удаления темы до бана пользователя!


  • Название темы должно отражать её суть! (Не следует добавлять туда слова "помогите", "срочно" и т.п.)
  • При создании темы, первым делом в квадратных скобках укажите область, из которой исходит вопрос (язык, дисциплина, диплом). Пример: [C++].
  • В названии темы не нужно указывать происхождение задачи (например "школьная задача", "задача из учебника" и т.п.), не нужно указывать ее сложность ("простая задача", "легкий вопрос" и т.п.). Все это можно писать в тексте самой задачи.
  • Если Вы ошиблись при вводе названия темы, отправьте письмо любому из модераторов раздела (через личные сообщения или report).
  • Для подсветки кода пользуйтесь тегами [code][/code] (выделяйте код и нажимаете на кнопку "Код"). Не забывайте выбирать при этом соответствующий язык.
  • Помните: один топик - один вопрос!
  • В данном разделе запрещено поднимать темы, т.е. при отсутствии ответов на Ваш вопрос добавлять новые ответы к теме, тем самым поднимая тему на верх списка.
  • Если вы хотите, чтобы вашу проблему решили при помощи определенного алгоритма, то не забудьте описать его!
  • Если вопрос решён, то воспользуйтесь ссылкой "Пометить как решённый", которая находится под кнопками создания темы или специальным флажком при ответе.

Более подробно с правилами данного раздела Вы можете ознакомится в этой теме.

Если Вам помогли и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, Poseidon, Rodman

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Центр помощи | Следующая тема »


 




[ Время генерации скрипта: 0.0560 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.