Prolog ищет возможную комбинацию для вычитания 2 элементов из списка

Это расширенная проблема с этой страницы. Prolog возможное удаление элементов в списке

Например, для списка X = [1,2,3] я могу вычесть следующее:

вычтите 1 из 1-го/2-го элемента, и X станет [1-1, 2-1, 3] = [0, 1, 3] = [1, 3]

вычесть 1 из 1-го / 3-го элемента: X становится [2, 2]

вычесть 1 из 2-го / 3-го элемента: X становится [1, 1, 2]

вычесть 2 из 2-го / 3-го элемента: X становится [1, 1]

Итак, это всегда вычитание 2 элементов и с одним и тем же числом, но всегда действительным числом. У кого-нибудь есть идеи по этому поводу?

Это выглядит лучше:

subt_comb(X, Y).
X = [1,2,3]
Y = [1,3]
Y = [2,2]
Y = [1,1,2]
Y = [1,1]

Посмотрев на решения lurker и gusbro, я создал что-то вроде этого.

remove2([HX|T], S2):-
    between(1, HX, Y),
    remove2__([HX|T], Y, S2).
remove2([HX|T], [HX|TY]):-
    remove2(T, TY).

% remove2__(S1, Y, S2), this procedure is to execute 0 after
% subtraction. Y is generated from remove2(S1, S2).
remove2__([Y|T], Y, TY):- remove2_(Y, T, Y, TY).
remove2__([HX|T], Y, [HY|TY]):-
    HX>Y,
    HY is HX - Y,
    remove2_(HX, T, Y, TY).


% remove2_(HX, L, Y, S2).
% HX is the first element from the origin list. L is the tail of the
% origin list. Y is the number to subtract. S2 is the result.
remove2_(_, [H|T], H, T).
remove2_(_, [H|T], Y, [HY|T]):-   %for list with descending order
    HY is H - Y, HY >0.
remove2_(HX, [H|T], Y, [H|TY]):-   %going for another element.
    remove2_(HX, T, Y, TY).


?- remove2([3,2,1],X).
X = [2, 1, 1] ;
X = [2, 2] ;
X = [1, 1] ;
X = [3, 1] ;
false.

?- remove2([1,2,3],X).
X = [1, 3] ;
X = [2, 2] ;
X = [1, 1, 2] ;
X = [1, 1] ;
false.

person user3390652    schedule 13.11.2015    source источник
comment
Можете ли вы привести пример того, как должен выглядеть ваш запрос? И как вы на самом деле хотите определить, что делает предикат? Это не очень ясно.   -  person lurker    schedule 13.11.2015
comment
Я пытался с помощью permutation([1,2,3], X) получить все возможные комбинации, так что я могу просто получить первые 2 элемента из списка. после вычитания я могу просто получить результат путем сортировки. Однако я обнаружил, что на этом пути есть некоторое дублирование.   -  person user3390652    schedule 13.11.2015
comment
@lurker Итак, я всегда могу выполнить вычитание с любыми двумя элементами в списке. но после вычитания элементы всегда должны быть больше или равны 0. А для вычитания 2 элемента должны быть вычтены с одинаковым числом, то есть E1 - N = R1 >= 0 и E2 - N = R2 >= 0. И так же, как и мой предыдущий вопрос, если R1 или R2 = 0, его нужно удалить из списка. Есть ли смысл?   -  person user3390652    schedule 13.11.2015
comment
@lurker Прошу прощения за беспорядочное описание.. На самом деле, я искал этот вопрос уже неделю... Я вообще понятия не имею..   -  person user3390652    schedule 13.11.2015
comment
Нет, мой плохой. Я просто был дураком. Теперь это кажется довольно ясным.   -  person lurker    schedule 13.11.2015
comment
Я как бы понимаю, что должен делать код, который вы ищете... но что он означает?   -  person repeat    schedule 15.11.2015
comment
@repeat Что ты имеешь в виду?   -  person user3390652    schedule 15.11.2015
comment
@repeat На самом деле это курсовая работа, и она считается игрой для 2 игроков. Игрок проигрывает, когда наступает его очередь со списком = [1]. Имеет ли это смысл?   -  person user3390652    schedule 15.11.2015
comment
@lurker И на самом деле меня смущает вопрос. Требуется написать предикат win(S), который успешен, если S является выигрышной позицией для игрока, чья очередь играть, и терпит неудачу в противном случае. Помимо предоставления правильных ответов, ваш код для этого должен избегать оценки одной и той же позиции более одного раза. Например, из [30,30] можно добраться только до 960 позиций, но оттуда можно сыграть многие миллиарды игр... Я действительно запутался, как [30,30] может достичь 960 позиций.. А собственно что значит выигрышная позиция.. :/   -  person user3390652    schedule 15.11.2015
comment
@lurker Мне интересно, если меня попросят написать предикат win(S), чтобы найти все возможные состояния для достижения цели или что-то в этом роде.. Меня действительно смущает слово выигрышная позиция.. Кто-нибудь знает, что это просит?   -  person user3390652    schedule 15.11.2015
comment
Я думаю, что это нужно перенести в новый вопрос. Оригинал спросили и ответили.   -  person lurker    schedule 15.11.2015
comment
@lurker Я создал новую тему. stackoverflow.com/questions/33722593/prolog-winning-position   -  person user3390652    schedule 15.11.2015


Ответы (2)


Вот альтернативное решение, которое использует более фундаментальные операции. Как и решение @gusbro, оно разбивает проблему на (1) проверку каждого элемента в списке как значение диапазона для сокращения и (2) уменьшение еще одного элемента в остальной части списка на основе текущего значения в определенном диапазоне. в 1).

reduce([H|T], R) :-
    reduce_by([H|T], H, R).   % Reduce the list [H|T] by H yielding R
reduce([H|T], [H|R]) :-
    reduce(T, R).             % Do the same for the rest of the list

% reduce_by(L, X, R) reduces two elements in L by each value in the range 1 to X
reduce_by([X|T], X, R) :-
    reduce_once(T, X, R).     % Drop the element if diff is 0, reduce one more from rest
reduce_by([H|T], X, [Y|R]) :-
    H > X,
    Y is H - X,               % reduce current element by X, reduce one more from rest
    reduce_once(T, X, R).
reduce_by(L, X, R) :-
    X1 is X - 1,              % decrement reduction amount and reduce by that amount
    X1 > 0,
    reduce_by(L, X1, R).

% reduce_once(L, X, R) finds one element in L which is >= X and reduces it, else fails
reduce_once([X|T], X, T).     % same value, it's just removed (difference is 0)
reduce_once([H|T], X, [Y|T]) :-
    H > X,                    % reduce the value by X if we can
    Y is H - X.
reduce_once([H|T], X, [H|R]) :-
    reduce_once(T, X, R).     % skip this value and reduce a different value

Полученные результаты:

| ?- reduce([1,2,3], L).

L = [1,3] ? a

L = [2,2]

L = [1,1]

L = [1,1,2]

no
| ?-

Существует ключевое отличие Пролога от программирования на Java, C# или других процедурных языках. Пролог посредством поиска с возвратом попытается найти все экземпляры аргументов, которые сделают предикат успешным. Дополнительные сведения см. в этом ответе. При написании предикатов (или правил) Пролога вам нужно подумать о том, как сформулировать правила таким образом, чтобы они выполнялись в тех случаях, в которых вы хотите. Prolog выполнит всю работу путем возврата, чтобы перебрать все возможные решения для достижения успеха.

Например, в случае reduce_once у нас есть одно предложение, которое гласит:

reduce_once([X|T], X, T).

Таким образом, это будет успешным, если аргументы могут быть объединены с входными данными. В частности, такой запрос, как reduce_once([1,2,3], 1, T)., будет успешным с T = [2,3]. Но затем, как только это удается, Пролог видит, что для того же предиката существуют другие правила, и также пытается применить их. Таким образом, будет выполнено reduce_once([1,2,3], 1, [1|R]) :- reduce_once([2,3], 1, R). и т.д.

person lurker    schedule 14.11.2015
comment
Интересно. Ваше решение работает со списком в порядке возрастания или убывания. Собственно, я тоже так думал сделать. И я пытался раньше ... но мне не удалось получить элемент из хвоста ... и я изо всех сил пытался унифицировать список после вычитания ... - person user3390652; 14.11.2015
comment
@user3390652 user3390652 у вас есть вопрос о моей реализации? Я был бы рад ответить. Я сделал некоторое объяснение решения, но оно, очевидно, не исчерпывающее (что было бы много материала :)). - person lurker; 14.11.2015
comment
У меня есть обновление по моему вопросу. лол, я кое-что написал, и он делает то же самое, что и ваш код, и код gusbro. Однако боюсь, что мой первый пункт для первой процедуры слишком запутан. Как бы вы его изменили? ржунимагу - person user3390652; 14.11.2015
comment
@user3390652 user3390652 почему вы думаете, что это слишком грязно? Использование between является альтернативой обслуживанию счетчика, которое я делаю в своем решении. Вы можете выбрать тот или иной. - person lurker; 15.11.2015
comment
извините, на самом деле я редактировал свой код 3 раза... и это самая простая версия, которую я могу получить. ржунимагу - person user3390652; 15.11.2015

Я бы использовал 2 процедуры.

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

Вы должны позаботиться о пограничных случаях (если вычисленное число равно элементу, то полностью удалите элемент), поэтому я использую другое предложение для этих случаев.

subt_comb([N|Tail], [NX|NTail]):-
  succ(N1, N),
  between(1, N1, NX),
  subt_comb1(Tail, NX, NTail).
subt_comb([N|Tail], NTail):-
  subt_comb1(Tail, N, NTail).
subt_comb([N|Tail], [N|NTail]):-
  subt_comb(Tail, NTail).

subt_comb1([M|Tail], N, [NM|Tail]):-
  M > N,
  NM is M - N.
subt_comb1([N|Tail], N, Tail).
subt_comb1([M|Tail], N, [M|NTail]):-
  subt_comb1(Tail, N, NTail).

Образец:

?- subt_comb([1,2,3], Y).
Y = [1, 3] ;
Y = [2, 2] ;
Y = [1, 1, 2] ;
Y = [1, 1] ;
false.
person gusbro    schedule 13.11.2015
comment
Это... слишком искусно.. Есть какие-то хитрости, чтобы начать писать первую процедуру? Я могу легко закончить этот вопрос на Java/С++/С# или подобных языках... но для пролога я всегда пытаюсь понять, как начать... - person user3390652; 13.11.2015
comment
@ user3390652: Пролог хорош для программирования алгоритмов с рекурсией. В этом случае вы пытаетесь увидеть рекурсивный алгоритм, решающий проблему. Итак, в этом случае вы знаете, что должны взять один элемент, что-то с ним сделать, а затем взять другой элемент из оставшегося списка. Вот почему я разделил его на 2 процедуры. Первая заботится о выборе одного элемента и применении вашего первого преобразования (вычитание числа), затем вы вызываете вторую процедуру, которая заботится о выборе другого элемента и применении того же преобразования. - person gusbro; 13.11.2015
comment
Обратный поиск в Прологе сделает все остальное за вас (получит все решения вашей проблемы рекурсивно). - person gusbro; 13.11.2015
comment
Только что придумали вопрос. когда я играл с этим решением, я обнаружил, что оно отлично работает со списком в порядке возрастания, но не со списком в порядке убывания. Можно ли без сортировки ввода скомпилировать его в обоих случаях? - person user3390652; 14.11.2015