Как создать отличные решения в Прологе для решения числовой игры «8 из 10 кошек ведет обратный отсчет»? - PullRequest
4 голосов
/ 22 января 2020

Я написал программу Prolog, чтобы найти все решения для любой ' 8 из 10 кошек, которые ведут обратный отсчет ' числовой последовательности. Я доволен результатом. Тем не менее, решения не являются уникальными. Я пробовал distincts() и reduced() из библиотеки "последовательности решений" . Они не дали уникальных решений.

Проблема проста. у вас есть заданный список из шести чисел [n1, n2, n3, n4, n5, n6] и целевого числа (R). Вычислить R из любой произвольной комбинации из n1 в n6, используя только +, -, *, /. Вам не нужно использовать все номера, но вы можете использовать каждый номер только один раз. Если два решения идентичны, должно быть сгенерировано только одно, а другое отброшено.

Иногда есть эквивалентные результаты с другим расположением. Например:

(100+3)*6*75/50+25
(100+3)*75*6/50+25  

У кого-нибудь есть предложения по устранению такой избыточности?

Каждое решение - это вложенные операторы и целые числа. Например +(2,*(4,-(10,5))). Это решение представляет собой несбалансированное двоичное дерево с оператором Arithmeti c для root, а также с одноуровневыми узлами и числами для конечных узлов. Чтобы иметь уникальные решения, никакие два дерева не должны быть эквивалентными.

Код:

:- use_module(library(lists)).
:- use_module(library(solution_sequences)).

solve(L,R,OP) :-
    findnsols(10,OP,solve_(L,R,OP),S),
    print_solutions(S).

solve_(L,R,OP) :-
    distinct(find_op(L,OP)),
    R =:= OP.

find_op(L,OP) :-
    select(N1,L,Ln),
    select(N2,Ln,[]),
    N1 > N2,
    member(OP,[+(N1,N2), -(N1,N2), *(N1,N2), /(N1,N2), N1, N2]).
find_op(L,OP) :-
    select(N,L,Ln),
    find_op(Ln,OP_),
    OP_ > N,
    member(OP,[+(OP_,N), -(OP_,N), *(OP_,N), /(OP_,N), OP_]).

print_solutions([]).
print_solutions([A|B]) :-
  format('~w~n',A),
  print_solutions(B).

Тест:

solve([25,50,75,100,6,3],952,X)

Результат

(100+3)*6*75/50+25 <- s1
((100+6)*3*75-50)/25 <- s2
(100+3)*75*6/50+25 <- s1
((100+6)*75*3-50)/25 <- s2
(100+3)*75/50*6+25 <- s1
true.

ОБНОВЛЕНИЕ: создание решений с использованием DCG

Ниже приведена попытка создания решений с использованием DCG. Мне удалось создать более исчерпывающий набор решений, чем в предыдущем опубликованном коде. В некотором смысле, использование DCG привело к более правильному и элегантному коду. Однако гораздо труднее «угадать», что делает код.

Проблема избыточных решений все еще сохраняется.

:- use_module(library(lists)).
:- use_module(library(solution_sequences)).

s(L) --> [L].

s(+(L,Ls)) --> [L],s(Ls).
s(*(L,Ls)) --> [L],s(Ls), {L =\= 1, Ls =\= 1, Ls =\= 0}.

s(-(L,Ls)) --> [L],s(Ls), {L =\= Ls, Ls =\= 0}.
s(/(L,Ls)) --> [L],s(Ls), {Ls =\= 1, Ls =\= 0}.

s(-(Ls,L)) --> [L],s(Ls), {L =\= Ls}.
s(/(Ls,L)) --> [L],s(Ls), {L =\= 1, Ls =\=0}.

solution_list([N,H|[]],S) :-
    phrase(s(S),[N,H]).

solution_list([N,H|T],S) :-
    phrase(s(S),[N,H|T]);
    solution_list([H|T],S).

solve(L,R,S) :-
    permutation(L,X),
    solution_list(X,S),
    R =:= S.

Ответы [ 2 ]

2 голосов
/ 22 января 2020

У кого-нибудь есть предложения по устранению такой избыточности?

Я предлагаю определить вес сортировки для каждого узла (внутреннего или конечного). Можно использовать число, полученное в результате сокращения дочернего узла, хотя связи появятся. Их можно сломать, дополнительно взглянув на самые верхние операции, например, отсортировав * перед +. На самом деле хотелось бы иметь операцию сортировки, для которой «t ie» означает «точно такое же поддерево арифметических c операций».

1 голос
/ 25 января 2020

Поскольку ОП ищет только подсказки, которые помогут решить проблему.

  1. Используйте DCG в качестве генератора. ( SWI-Prolog ) ( Prolog DCG Primer )
    a. Для более тонкой версии использования DCG в качестве генератора ищите примеры, которые используют length / 2 . Когда вы понимаете, почему вы можете увидеть луч света, падающий на вас на несколько мгновений (Луч света - это видеоигра).
  2. Использование решателя ограничений ( SWI-Prolog ) ( CLP (FD) и CLP (ℤ): арифметическое число Пролога c) ( Основные сведения о коде пролога CLP (FD) проблемы N-ферзей )
  3. Поскольку ваши решения ограничены 6 числами, а операторы всегда являются бинарными операторами (+, -, *, /), можно перечислить уникальные двоичные деревья. Если вы знаете о OEIS , вы можете найти соответствующие ссылки, которые могут помочь вам решить эту проблему, но вам нужно дать OEIS последовательность. Чтобы получить последовательность для использования с OEIS, нарисуйте деревья для N от 2 до 5, а затем введите эту последовательность в OEIS и посмотрите, что вы получите. например,
N is the number of leaf (*) nodes.

N=2  ( 1 way to draw the tree )
   -  
  / \  
 *   *  

N=3  ( 2 ways to draw the tree )
     -       -  
    / \     / \  
   -   *   *   -  
  / \         / \  
 *   *       *   *  

Таким образом, последовательность начинается с 1,2 ...

Подсказка - на этой странице показаны изображения деревьев, чтобы увидеть, если вы сделали это правильно. В описании я использую N для подсчета количества листьев (*), но на этой странице они используют N для подсчета количества внутренних узлов (-). Если мы назовем мой N N1 и страницу N N2, то отношение будет N2 = N1 - 1

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

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

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

...