我正在尝试打印 n=4 的 n 分数问题的所有解决方案:
:- lib(ic).
fractions(Digits) :-
Digits = [A,B,C,D,E,F,G,H,I,J,K,L],
Digits #:: 1..9,
ic:alldifferent(Digits),
X #= 10*B+C,
Y #= 10*E+F,
Z #= 10*H+I,
V #= 10*K+L,
A*Y*Z*V + D*X*Z*V + G*X*Y*V + J*X*Y*Z #= X*Y*Z*V,
A*Y #=< D*X,
D*Z #=< G*Y,
G*V #=< J*Z,
search(Digits,0,input_order,indomain,complete,[]).
当我运行查询时:
?- findall(Digits,fractions(Digits),List).
我得到以下异常:
*** Overflow of the local/control stack!
You can use the "-l kBytes" (LOCALSIZE) option to have a larger stack.
Peak sizes were: local stack 105728 kbytes, control stack 25344 kbytes
我在想是否有办法在程序内部循环并每次打印一个解决方案,或者我不能这样做,因为问题有太多的解决方案?
如前所述,您的代码失败是因为alldifferent(Digits)
约束过于严格。 数字必须允许出现 1 到 2 次。 在 eclipse-clp 中,您可以使用诸如 aminimum/3、atmost/3、occurrences/3 或 gcc/2 等约束来表达这一点。
稍微跑题了:当你使用ECLiPSe的ic求解器(可以处理连续域)时,你实际上可以使用更接近原始规范的模型,而无需引入大量的乘法:
:- lib(ic).
:- lib(ic_global).
fractions4(Digits) :-
Digits = [A,B,C,D,E,F,G,H,I,J,K,L],
Digits #:: 1..9,
A/(10*B+C) + D/(10*E+F) + G/(10*H+I) + J/(10*K+L) $= 1,
( for(I,1,9), param(Digits) do
occurrences(I, Digits, NOcc), NOcc #:: 1..2
),
lex_le([A,B,C], [D,E,F]), % lex-ordering to eliminate symmetry
lex_le([D,E,F], [G,H,I]),
lex_le([G,H,I], [J,K,L]),
labeling(Digits).
除了主要的相等约束(使用$=
而不是#=
,因为我们不想在这里要求完整性),我还使用 occurrences/3 作为发生限制,并使用词典排序约束作为消除对称性的更标准方法。结果:
?- findall(Ds, fractions4(Ds), Dss), length(Dss, NSol).
Dss = [[1, 2, 4, 3, 5, 6, 8, 1, 4, 9, 2, 7], [1, 2, 6, 5, 3, 9, 7, 1, 4, 8, 2, 4], [1, 2, 6, 5, 3, 9, 7, 8, 4, 9, 1, 2], [1, 2, 6, 7, 3, 9, 8, 1, 3, 9, 5, 4], [1, 2, 6, 8, 7, 8, 9, 1, 3, 9, 5, 4], [1, 3, 4, 5, 4, 6, 8, 1, 7, 9, 2, 3], [1, 3, 4, 7, 5, 6, 8, 1, 7, 9, 2, 4], [1, 3, 4, 8, 1, 7, 8, 5, 2, 9, 2, ...], [1, 3, 5, 6, 2, 8, 7, 1, 4, 9, ...], [1, 3, 6, 5, 2, 4, 7, 1, 8, ...], [1, 3, 6, 5, 3, 6, 7, 8, ...], [1, 3, 6, 5, 4, 5, 8, ...], [1, 3, 6, 5, 6, 3, ...], [1, 3, 6, 6, 5, ...], [1, 3, 6, 7, ...], [1, 3, 9, ...], [1, 3, ...], [1, ...], [...], ...]
NSol = 1384
Yes (82.66s cpu)
该模型的另一个优点是,它可以很容易地转换为任意 N 的通用模型。
只是你的谓词失败了。如果你删除除alldifferent/1
和search/6
之外的所有约束(只是为了理解问题)并调用?- fractions(Digits).
你会得到false
,因为不可能有一个包含 12 个元素的列表 (Digits = [A,B,C,D,E,F,G,H,I,J,K,L]
) 每个元素的域Digits #:: 1..9
并且约束这些元素都是不同的(ic:alldifferent(Digits)
)。12 个元素的 9 个选项:无法解决。如果将域扩展到 12 (Digits #:: 1..12
),您将获得解决方案:
?- fractions(Digits).
Digits = [2, 3, 4, 9, 7, 10, 12, 8, 5, 11, 1, 6]
Yes (94.00s cpu, solution 1, maybe more)
然后,您可以应用findall/3
并查看其他解决方案...
许多 clpfd 实现都提供了我在此示例中使用的global_cardinality
约束。在下面,我使用SICStus Prolog 4.5.0:
:- use_module(library(clpfd)).
fractions(Digits) :-
Digits = [A,B,C,D,E,F,G,H,I,J,K,L],
domain(Digits, 1, 9),
global_cardinality(Digits, [1-N1,2-N2,3-N3,4-N4,5-N5,6-N6,7-N7,8-N8,9-N9]),
domain([N1,N2,N3,N4,N5,N6,N7,N8,N9], 1, 2),
X #= 10*B+C,
Y #= 10*E+F,
Z #= 10*H+I,
V #= 10*K+L,
Z*V #= ZV,
X*Y #= XY,
A*Y*ZV + D*X*ZV + G*XY*V + J*XY*Z #= XY*ZV,
X #=< Y, X #= Y #=> A #=< D, % break some symmetries
Y #=< Z, Y #= Z #=> D #=< G,
Z #=< V, Z #= V #=> G #=< J.
样品使用:
| ?- n_fractions(4,Zs), labeling([enum],Zs).
Zs = [2,1,2,9,1,8,7,3,5,6,4,5] ? ;
Zs = [2,1,3,7,1,8,9,2,6,5,4,5] ? ;
Zs = [2,1,3,7,1,8,9,2,6,6,5,4] ? ;
...
no
使用prolog-findall收集所有解决方案也可以正常工作:
?- findall(Zs,(n _fractions(4,Zs), labeling([enum],Zs)), Zss),
length(Zss, N_sols).
Zss = [[2,1,2,9,1,8,7,3,5|...],
[2,1,3,7,1,8,9,2,6|...],
[2,1,3,7,1,8,9,2|...],
[2,1,3,8,1,5,7|...],
[2,1,3,8,1,6|...],
[2,1,3,9,1|...],
[2,1,3,9|...],
[2,1,4|...],
[2,1|...],
[...|...]|...],
N_sols = 1384 ? ;
no