質問

I'm working on a (SWI-)Prolog program that uses CLP(FD) constraints to find a solution for a particular problem. To do so, I happen to need what I call an "unpositioned" overlapping of two lists. That is:

  • List La has length A
  • List Lb has length B
  • A > B
  • The unpositioned overlapping list is La+Lb, where elements are added in a one-by-one fashion.

However, I need Lb to have a variable offset (i.e. each lists' first element are not in the same position for the La+Lb addition. However, list Lb is always within the width of La. For instance:

Be La = [0,1,1,1,1,0,1,1,1] and Lb = [1,2,2].

Possible case 1

(Lb) 1 2 2 . . . . . . ---> offset = 0
(La) 0 1 1 1 1 0 1 1 1
( +) 1 3 3 1 1 0 1 1 1   

Possible case 2

(Lb) . . . 1 2 2 . . . ---> offset = 3
(La) 0 1 1 1 1 0 1 1 1
( +) 0 1 1 2 3 2 1 1 1   

Possible case 3

(Lb) . . . . . 1 2 2 . ---> offset = 5
(La) 0 1 1 1 1 0 1 1 1
( +) 0 1 1 1 1 1 3 3 1   

What I want is to define the offset as a clpfd variable with a particular domain associated to it. In order to compute La+Lb I've written the predicate overlap/6, which is the following:

overlap([],[],_,_,_,[]) :- !.
overlap([],_, _,_,_,[]) :- !.
overlap(A, [],_,_,_, A) :- !.
overlap(A, _,Os,_,_, A) :- length(A,L), L =< Os, !.
overlap([A|As],[B|Bs],0,Os,S,[A|Ls]) :-  % Os is the actual Offset 
    A #= B #<== S #= Os,                 % S is a clpfd variable
    overlap(As,Bs,0,Os,S,Ls),!.
overlap([A|As],Bs,Acc,Os,S,[A|Ls]) :-
    Acc > 0,
    Acc0 is Acc-1,
    overlap(As,Bs,Acc0,Os,S,Ls),!.

The idea is to find La+Lb by calling overlap/6 then, with indomain(S), make the numbers converge to a particular solution of the addition. My problem is that when Prolog reaches the line A #= B #<==> S #= Os, S is assigned to Os (a case offset value), rather to constrain A with a reified condition.

Am I crazy and this makes no sense? Is there any proper way to do what I'm trying? Thanks in advance!

Edit: The idea is to call overlap/6 for every point within S's domain, and use this constraining list to label a proper S afterwards.

Example of unification:

?- S in 0..2,
   L0 = [0,0,0,0],
   overlap(L0, [1,2], 0, S, L1),
   overlap(L1, [1,2], 1, S, L2),
   overlap(L2, [1,2], 2, S, L).
L = [_G1, _G2, _G3, _G4]

_G1 in 0\/1
_G2 in 0\/1\/2
_G3 in 0\/1\/2
_G4 in 0\/2

_G1 #= 1 #<== S #= 0
_G1 #= 0 #<== S #> 0

_G2 #= 2 #<== S #= 0
_G2 #= 1 #<== S #= 1
_G2 #= 0 #<== S #> 2

_G3 #= 0 #<== S #= 0
_G3 #= 2 #<== S #= 1
_G3 #= 1 #<== S #< 2

_G1 #= 0 #<== S #= 0
_G4 #= 0 #<== S #= 1
_G4 #= 2 #<== S #= 2

Or:

?- S in 0..2,
   L0 = [0,0,0,0],
   overlap(L0, [1,2], 0, S, L1),
   overlap(L1, [1,2], 1, S, L2),
   overlap(L2, [1,2], 2, S, L),
   indomain(S).
S = 0
L = [1, 2, 0, 0]
役に立ちましたか?

解決

If there is an overlap with starting position S, we expect a conjunction of constraints so that all overlapping positions are covered. For example:

:- use_module(library(clpfd)).

overlap_at(As, Bs, S, ABs) :-
        length(As, L),
        L1 #= L - 1,
        S in 0..L1,
        overlap_at_(As, Bs, S, 0, ABs).

overlap_at_([], _, _, _, []).
overlap_at_([A|As], Bs, S, N0, [AB|ABs]) :-
        overlap_here(Bs, [A|As], [AB|ABs], Conj),
        S #= N0 #==> Conj,
        S #> N0 #==> AB #= A,
        N1 #= N0 + 1,
        overlap_at_(As, Bs, S, N1, ABs).

overlap_here(_, [], _, 1) :- !.
overlap_here([], _, _, 1).
overlap_here([B|Bs], [A|As], [AB|ABs], (AB #= A + B #/\ Rest)) :-
        overlap_here(Bs, As, ABs, Rest).

Notice how I describe a conjunction in overlap_here/4.

Sample query:

?- overlap_at([0,1,1,1,1,0,1,1,1], [1,2,2], 3, ABs).
ABs = [0, 1, 1, 2, 3, 2, _G909, _G912, _G915],
_G909 in inf..sup,
_G912 in inf..sup,
_G915 in inf..sup.

This gives you a good chunk of the solution: All elements up to and including the overlap are instantiated as desired. The third argument can of course also be a variable: Try for example

?- overlap_at([0,1,1,1,1,0,1,1,1], [1,2,2], S, ABs), 
   indomain(S), writeln(ABs), false.

Which yields something like:

[1,3,3,_,_,_,_,_,_]
[0,2,3,3,_,_,_,_,_]
[0,1,2,3,3,_,_,_,_]
[0,1,1,2,3,2,_,_,_]
[0,1,1,1,2,2,3,_,_]
[0,1,1,1,1,1,3,3,_]
[0,1,1,1,1,0,2,3,3]
[0,1,1,1,1,0,1,2,3]
[0,1,1,1,1,0,1,1,2]

I leave the rest as an exercise: Trailing positions that are not affected by the overlap need to be made equal to elements of A. Also, you may want to further restrict the possible positions of the overlap, which I have kept rather general.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top