1

This is an extended problem from this page. Prolog possible removal of elements in a list

For example, for a list X = [1,2,3], I can subtract like following:

subtract 1 from 1st / 2nd element, and X becomes [1-1, 2-1, 3] = [0, 1, 3] = [1, 3]

subtract 1 from 1st / 3rd element: X becomes [2, 2]

subtract 1 from 2nd / 3rd element: X becomes [1, 1, 2]

subtract 2 from 2nd / 3rd element: X becomes[1, 1]

So, it is always subtracting 2 elements, and with the same number, but always a real number. Have anyone got any ideas on this?

This looks better:

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

After having a look on the solutions of lurker and gusbro, I have created something like this.

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.
Community
  • 1
  • 1
user3390652
  • 132
  • 1
  • 13
  • 1
    Can you give an example of what you want your query to look like? And how you really want to *define* what the predicate does? It's not very clear. – lurker Nov 13 '15 at 16:03
  • I have tried with using `permutation([1,2,3], X)` to get all possible combinations, such that I can simply getting the first 2 elements from the list. after subtraction, I can simply get the result by a sorting. However, I have found that there are some duplication in this way.. – user3390652 Nov 13 '15 at 16:03
  • @lurker So, I can always perform a subtraction with any 2 elements in the list. but after the subtraction, the elements must always be greater than or equals to 0. And for the subtraction, the 2 elements must be subtracted with the same number, i.e. `E1 - N = R1 >= 0` and `E2 - N = R2 >= 0`. And same as my previous question, should R1 or R2 = 0, it has to be deleted from the list. Does it make better sense? – user3390652 Nov 13 '15 at 16:08
  • @lurker I'm sorry for the messy description.. Actually I have been looking on this question for a week... I have no idea at all.. – user3390652 Nov 13 '15 at 16:15
  • No, my bad. I was just being daft. It seems pretty clear now. – lurker Nov 13 '15 at 16:27
  • I kind of get what the code you seek is supposed to do... but what does it *mean*? – repeat Nov 15 '15 at 07:31
  • @repeat What do you mean? – user3390652 Nov 15 '15 at 16:53
  • @repeat actually this is a coursework, and it is regarded as a game, for 2 players. A player is lost when it is his turn with a list = [1]. Does it make sense? – user3390652 Nov 15 '15 at 17:00
  • @lurker And actually I am confused by the question. It is asking to write a predicate win(S), that succeed if S is a winning position for the player whose turn it is to play and fails otherwise. Besides giving the correct answers, your code for this should avoid evaluating the same position more than once. For example, there are only 960 positions that can be reached from [30,30], but many billions of games that could be played starting from there... I am really confused how can [30,30] reach 960 positions.. And actually what is mean by a winning position.. :/ – user3390652 Nov 15 '15 at 17:02
  • @lurker I do wonder if I am asked to write a predicate win(S), to search for all possible state to reach the goal or what.. I am really confused by the word "winning position".. Does anyone have any idea what this is asking for? – user3390652 Nov 15 '15 at 17:04
  • I think this needs to be moved to a new question. The original was asked and answered. – lurker Nov 15 '15 at 17:08
  • @lurker I have created a new thread. http://stackoverflow.com/questions/33722593/prolog-winning-position – user3390652 Nov 15 '15 at 17:24

2 Answers2

1

I would use 2 procedures.

The first one takes an item from the list, computes a number equal or lesser than it and then calls second procedure to subtract that number from another item in the remaining list.

You have to take care of border cases (if computed number is equal to item, then remove the item altogether), thus I use a different clause for those cases.

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).

Sample:

?- subt_comb([1,2,3], Y).
Y = [1, 3] ;
Y = [2, 2] ;
Y = [1, 1, 2] ;
Y = [1, 1] ;
false.
gusbro
  • 22,357
  • 35
  • 46
  • This... is too skilful.. Is there any tricks to start writing the first procedure? I can easily finish this question on Java/C++/C# or similar languages.. but for prolog, I am always struggling on how to start... – user3390652 Nov 13 '15 at 16:24
  • 1
    @user3390652: Prolog is good for programming algorithms with recursion. In this case you try to "see" a recursive algorithm that solves the problem. So in this case you know you have to take one item, do something with it and then take another element from the remaining list. That's why i divided it in 2 procedures. The first one takes care of selecting one element and apply your first transformation (subtract number), then you call the second procedure that takes care of selecting another item and applying the same transformation. – gusbro Nov 13 '15 at 16:31
  • Prolog's backtracking does the rest for you (obtain all the solutions to your problem, recursively). – gusbro Nov 13 '15 at 16:31
  • Have just come up with a question. when I was playing with this solution, I've found that this is working perfectly with a list in ascending order, but not for a list in descending order. Without sorting the input, is it possible to make it compile with both cases? – user3390652 Nov 14 '15 at 17:21
1

Here's an alternative solution which uses more fundamental operations. Like @gusbro's solution, it breaks the problem down into (1) examining each element in the list as a range value for reduction, and (2) reducing one more element in the rest of the list based upon the current value in the range defined in (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

Results:

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

L = [1,3] ? a

L = [2,2]

L = [1,1]

L = [1,1,2]

no
| ?-

There's a key difference in Prolog versus when programming in Java or C# or other procedural languages. Prolog, through backtracking, will attempt to find all instantiations of the arguments that will make a predicate succeed. See this answer for further details. When writing Prolog predicates (or rules), you need to think about how to state the rules such that they succeed for cases that you want them to. Prolog will do all the work through backtracking to iterate through the all of the possible solutions to success.

For example, in the case of reduce_once, we have one clause that reads:

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

So this will succeed as long as the arguments can be unified with the inputs. Specifically, a query such as reduce_once([1,2,3], 1, T). will succeed with T = [2,3]. But then once it succeeds, Prolog sees there are other rules for the same predicate, and will attempt those as well. Thus, reduce_once([1,2,3], 1, [1|R]) :- reduce_once([2,3], 1, R). will be executed, etc.

Community
  • 1
  • 1
lurker
  • 56,987
  • 9
  • 69
  • 103
  • Interesting. Your solution is working for a list with ascending order or descending order. Actually this is also what I was thinking to do. And I tried before.. but I failed with getting an element from the tail.. and struggling on how to unify the list after the subtraction.. – user3390652 Nov 14 '15 at 17:25
  • @user3390652 do you a question about my implementation? I'd be happy to answer. I did some explanation for the solution, but it's obviously not exhaustive (which would be a lot of material :)). – lurker Nov 14 '15 at 17:28
  • I have an update on my question. lol I have written something and it is doing the same thing as your code and gusbro's code. However, I'm afraid that my first clause for the first procedure is too messy. How would you modify it? lol – user3390652 Nov 14 '15 at 19:14
  • @user3390652 why do you think it's too messy? Using `between` is an alternative to the counter maintenance that I do in my solution. You can choose one or the other. – lurker Nov 14 '15 at 21:01
  • sorry, actually I have editied my code 3 times.. and this is the simplest version I can get. lol – user3390652 Nov 14 '15 at 21:11