labeling / 2如何从域的中点开始生成解决方案?

时间:2019-05-23 13:01:40

标签: prolog clpfd labeling

具有一个域为1..N的具有自变量的列表,我们如何使用labeling / 2,以便它从中间开始生成解决方案?

我尝试过的标志是[二等分],[枚举],[最大],[最小],[ff],但是无论我选择哪个标志,我都无法使其正常工作。

我的代码是:

:-use_module(library(clpfd)).

combos(EMPLOYEES,POSTS,LIST):-
   LIMIT is POSTS-EMPLOYEES+1,
   length(LIST,EMPLOYEES),
   LIST ins 1..LIMIT,
   sum(LIST,#=,POSTS),
   labeling([bisect],LIST).

设置查询后,例如:

?-combos(2,10,LIST).

我希望它返回:

L = [5,5];
L = [4,6];
L = [6,4] ...

代替:

L = [1,9];
L = [2,8];
L = [3,7] ...

3 个答案:

答案 0 :(得分:0)

您在这里!

combos(2,S,L) :- b2(S,L).
combos(C,S,[A|L]) :-
    C > 2,
    b2(S,[A,B]),
    D is C-1,
    combos(D,B,L).

b2(S,L) :- B is S-1, bisector(B,L).

bisector(Y,[A,B]) :-
    odd(Y),
    M is div(1+Y,2),
    Z is M-1,
    range(D,0,Z),
    bisec1(D,M,A,B).
bisector(Y,[A,B]) :-
    even(Y),
    M is 1+Y,
    Z is Y/2-1,
    range(D,0,Z),
    bisec2(D,M,A,B).

bisec1(0,M,M,M).
bisec1(D,M,A,B) :- D > 0, A is M + D, A > 0, B is M - D, B > 0.
bisec1(D,M,A,B) :- D > 0, A is M - D, A > 0, B is M + D, B > 0.

bisec2(D,M,A,B) :- A is (M+2*D+1)/2, A > 0, B is (M-2*D-1)/2, B > 0.
bisec2(D,M,A,B) :- A is (M-2*D-1)/2, A > 0, B is (M+2*D+1)/2, B > 0.

even(X) :- 0 is mod(X, 2).
odd(X) :- 1 is mod(X, 2).

range(M,M,_).
range(X,M,N) :- P is M + 1, P =< N, range(X,P,N).

答案 1 :(得分:0)

根据经验,每当您尝试扩展clpfd的功能时,都应尝试尽可能多地重用。看来您首先要寻找到中心的距离之和尽可能小的解决方案。

date

答案 2 :(得分:0)

罗伯特·巴伦(Robert Baron)提供的答案正是我所需要的!我对其进行了编辑并添加了注释以了解它。这是评论版。

/* nsector(+N, +Sum, -Tuple)
 * is true when Tuple is an n-tuple of positive
 * integers that add up to Sum. The tuple solutions
 * are recursively searched from the midpoint of
 * the interval [1, S+1]. For example nsector(2,12, T)   
 * => [6,6];[7,5];[5,7];[8,4];[4,8];...;[11,1];[1,11]. */
nsector(2, S, L) :-
    Z is S - 1,
    bisector(Z, L).
nsector(N, S, [A | L]) :-
    N > 2,
    Z is S - 1,
    bisector(Z, [A, B]),
    D is N - 1,
    nsector(D, B, L).

/* bisector(+Sum, -Pair)
 * is true when Pair = [A, B] and S+1 is A+B, A,B > 0.
 * The solutions are found by searching from the midpoint
 * of the interval [1, S+1]. For example: bisector(6, P)
 * => P = [4,3];[3,4];[5,2];[2,5];[6,1];[1,6].*/
bisector(Y, [A, B]) :-
    odd(Y),
    M is div(1 + Y, 2),
    Z is M-1,
    range(D, 0, Z),
    even_interval(D, M, A, B).
bisector(Y, [A, B]) :-
    even(Y),
    M is 1 + Y,
    Z is Y / 2 - 1,
    range(D, 0, Z),
    odd_interval(D, M, A, B).

/* even_interval(+D, +M, -A, -B)
 * is true when A and B are at distance D from M, with A, B > 0.
 * For example: even_interval(2, 6, A, B) => A = 8, B = 4; A= 4, B = 8 */
even_interval(0,M, M, M).
even_interval(D,M,A,B) :-
    D > 0,
    A is M + D,
    A > 0,
    B is M - D, B > 0.
even_interval(D,M,A,B) :-
    D > 0,
    A is M - D,
    A > 0,
    B is M + D,
    B > 0.

/* odd_interval(+D, +M, -A, -B)
 * is true when A and B are at distance D+1/2 from M/2, with A, B > 0.
 * For example: odd_interval(2, 7, A, B) => A = 6, B = 1; A= 1, B = 6 */
odd_interval(D, M, A, B) :-
    A is (M + 2 * D + 1) / 2,
    A > 0,
    B is (M - 2 * D - 1) / 2,
    B > 0.

odd_interval(D, M, A, B) :-
    A is (M - 2 * D - 1) / 2,
    A > 0,
    B is (M + 2 * D + 1) / 2,
    B > 0.

/* even(+Integer)
 * is true when Integer is even. */
even(Integer) :-
    0 is mod(Integer, 2).

/* odd(+Integer)
 * is true when Integer is odd. */
odd(Integer) :-
    1 is mod(Integer, 2).

/* range(-Integer, +LowerBound, +UpperBound)
 * is true when LowerBound <= Integer <= UpperBound. */
range(Integer, Integer, _).
range(Integer, LowerBound, UpperBound) :-
    NewLowerBound is LowerBound + 1,
    NewLowerBound =< UpperBound,
    range(Integer, NewLowerBound, UpperBound).