【问题标题】:Magic Hexagon can't constrain itselfMagic Hexagon 无法自我约束
【发布时间】:2015-11-23 19:31:39
【问题描述】:

我正在尝试解决 Prolog 中的Magic Hexagon 问题,在维度 5 中,现在(?)。我首先使用 2D 列表创建布局。然后我尝试约束该列表的每个元素(实际上是一个列表)。 但是,我无法让它工作,这是我的代码,在所有更新之后

:- use_module(library(clpfd)).

solve(Dim) :-
    length(L, 5), % define 5 diagonals
    Offset is Dim - 2,
    Flag is 0,
    fill(L, Offset, Dim, Flag),
    writeln(L),
    constraint_sum(L, 38),
    writeln(L).
    
constraint_sum([], _).
constraint_sum([H|T], Sum) :-
    label(H),
    sum_list(H, Sum),
    constraint_sum(T, Sum).
    
fill([], _, _, _).
fill([H|T], Len, Dim, Flag) :-
    Flag == 0,
    Len < Dim,
    length(H, Len),
    H ins 1..19,
    all_different(H),
    NewLen is Len + 1,
    fill(T, NewLen, Dim, Flag).
fill([H|T], Len, Dim, _) :-
    length(H, Len),
    H ins 1..19,
    all_different(H),
    NewLen is Len - 1,
    Flag is 1,
    fill(T, NewLen, Dim, Flag).

我得到了:

1 ?- 求解(5)。

[[_G2537,_G2581,_G2617],[_G2857,_G2893,_G2929,_G2965],

[_G3263,_G3299,_G3335,_G3371,_G3407],[_G3757,_G3793,_G3829,_G3865],

[_G4157,_G4193,_G4229]]

[[1,18,19],[1,2,16,19],[1,2,3,13,19],[1,2,16,19],[1,18,19 ]]

真的。

..如您所见,问题在于元素不是唯一的,因为我已将 all_different() 分别用于每个列表而不是整个列表,但我不知道该怎么做!

【问题讨论】:

  • sum_list 内部使用is/2。因此,sum_list 的第一个参数中必须有一些未实例化的列表元素。你的constraint_sum([H|T], Sum) 看起来也很奇怪。递归调用不应该是constraint_sum(T, Sum) 而不是sum_list(T, Sum)?而且您使用了两次Sum,这意味着它可能会失败,因为sum_list(H, Sum) 将实例化Sum,并且它可能与获取Sum 的递归调用不匹配。
  • 但是没有元素被初始化,如果这就是你所说的实例化@lurker的意思。到目前为止,我看到的问题是所有元素都应该不同,并且通过我的实现,我使每个列表的所有元素都与同一列表中的元素不同,而不是与其他所有元素不同,因为它应该。
  • 查看我的更新评论。使用clpfd 时,您必须先调用labeling(Vars),然后才能执行sum_list 之类的操作来实例化这些值。所以labeling(H), sum_list(H, Sum)...
  • @lurker 谢谢,我更正了,但仍然是同样的错误。那么 Sum 有一个固定值为 38 的值,不是吗?从在solve() 中调用constraint_sum() 时开始。
  • 请看完我关于sum_list的cmets。 :)

标签: list prolog logic constraints clpfd


【解决方案1】:

我敢打赌 - 但我认为存在错误,因为问题页面指出只有解决方案。

:- module(magic_exagon, [magic_exagon/0]).
:- use_module(library(clpfd)).

magic_exagon :-
    magic_exagon(3, 38).

magic_exagon(N, Sum) :-
    R is N*2-1,
    findall(L, (between(1,R,C), c_cells(C,N,R,L)), Rows),
    flatten(Rows, Cells),
    length(Cells, Max),
    Cells ins 1..Max,
    all_different(Cells),

    get_diags(Rows, N,R,1, LeftDiags),

    reverse(Rows, Rev),
    maplist(reverse, Rev, RevRows),
    get_diags(RevRows, N,R,1, RightDiags),

    maplist(sum_diags(Sum), Rows),
    maplist(sum_diags(Sum), LeftDiags),
    maplist(sum_diags(Sum), RightDiags),

    label(Cells),
    show(rows, Rows).

c_cells(C,N,R,L) :-
    ( C > N ->   M is N+R-C ; M is N+C-1 ),
    length(L,M).

sum_diags(Sum, Diag) :-
    sum(Diag, #=, Sum).

get_diags([], _,_,_, []).
get_diags(Rows, N,R,C, [Diag|Diags]) :-
    c_cells(C, N, R, Diag),
    capture(Diag, Rows, RestWithEmpty),
    drop_empties(RestWithEmpty, Rest),
    C1 is C+1,
    get_diags(Rest, N,R,C1, Diags).

capture([], Rest, Rest).
capture([Cell|Diag], [[Cell|Cs]|Rows], [Cs|Rest]) :-
    capture(Diag, Rows, Rest).

drop_empties([[]|RestT], Rest) :- !, drop_empties(RestT, Rest).
drop_empties(Rest, Rest).

show(K,Ds) :- writeln(K), maplist(writeln, Ds).

get_diags/5 很难处理索引。我设计了一种算法来从操场上捕获诊断信息。在变量被赋予属性后,我们不能使用 findall/3,因此是递归循环。

编辑

显示对角线,一种简单的方法

...
label(Cells),
show(rows, Rows),
show(left, LeftDiags),
show(right, RightDiags).

我们得到

?- magic_exagon.
rows
[3,16,19]
[17,6,7,8]
[18,4,1,5,10]
[12,2,11,13]
[9,14,15]
left
[3,17,18]
[16,6,4,12]
[19,7,1,2,9]
[8,5,11,14]
[10,13,15]
right
[15,13,10]
[14,11,5,8]
[9,2,1,7,19]
[12,4,6,16]
[18,17,3]

【讨论】:

  • +1 给了我label() 谓词。你的答案似乎......逻辑(:)),但我想扩展我的代码!
  • @gsamaras:谢谢。我没能理解你在哪里尝试了困难的部分:得到对角线。所以,为了更好地理解这个问题,我制定了我的解决方案。 HTH
  • 谢谢..仔细阅读您的回答后,我认为它回答了我的新问题...嗯..
  • drop_emtpies(Ls0, Ls) 是:exclude(=([]), Ls0, Ls)。我还建议使用append/2 而不是flatten/2。此外,C1 #= C + 1R #= N*2 - 1 看起来会更好!
  • @mat:感谢您的建议。带有 exclude/3 的 drop_emtpies/2 确实看起来好多了。我故意使用 is/2,因为 - 我认为 - 可以帮助 gsamaras 区分 where 约束的使用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-02
  • 1970-01-01
  • 1970-01-01
  • 2016-12-27
  • 2012-12-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多