Ser*_*nko 7 metaprogramming prolog constraint-programming
前段时间我为2014年Codeforces愚人节大赛创造了一个问题 - "Feed the Golorp":http://codeforces.com/contest/409/problem/I .请阅读提供的链接上的问题陈述.
这个问题原本是由那些根本不了解Prolog的人解决的.在比赛期间,只有3人设法解决了这个问题 - 使用Java,Python和C++.
主要挑战是了解需要做什么.对于具有一些Prolog经验的人来说,很明显golorp的名字就像?(_-_/___*__):-___>__.
定义Prolog谓词一样,并且任务是找到变量的最小值,使谓词满足.有一些细节:再次,请阅读问题陈述.
实际上在理解目标之后解决问题并非如此微不足道.在算法上,任务是根据约束对变量进行拓扑排序.Golorp的名称最长可达1024个字符,因此需要相当高效的算法.
我用Python用正则表达式编写了我的参考解决方案.但在比赛结束后,我开始想知道如何解决Prolog中的问题.
由于只使用Prolog回溯到暴力强迫,golorp的名称可能长达1024个字符,所有可能性看起来都不可行 - 可能需要约束逻辑编程.
如果我可以从不等式中提取所有变量的列表和变量对的列表,我可以解决它.例如在ECLiPSe CLP中:
:- lib(ic).
solve(Vars, Ineqs, Result) :-
Vars :: 0..9,
( foreach((A, B), Ineqs) do
A #< B ),
labeling(Vars),
concat_string(Vars, Result).
[eclipse]: Vars = [__, ___, __, ___], Ineqs = [(__, ___)], solve(Vars, Ineqs, Result).
Vars = [0, 1, 0, 1]
__ = 0
___ = 1
Ineqs = [(0, 1)]
Result = "0101"
Run Code Online (Sandbox Code Playgroud)
但是我不知道如何在没有太多代码的情况下获得Vars = [__, ___, __, ___]
和Ineqs = [(__, ___)]
离开?(__+___+__-___):-___>__.
.
term_variables/2
丢失重复的变量.DCG?
或者是否有完全不同的,更好的方法来解决Prolog中的难题?(不一定在ECLiPSe CLP中).
更新:测试的几个大例子:
?(_____________________*_________________________*________________________*___________________*_________________*__________________*___________________________*___*__*____________________*_________________________*_______________*____*___________*_____________*______*_____*_______________*____________*__________________*___________________________*___________________________):-_____>__,_______________<___________________,__>___________,________________________>______,_____________>______,____________________<_________________________,_________________<__________________,_____________<___,____<_________________________,______>____________,________________________>_________________________,_____<____________________,__<____________,_____________________>____________,__________________>_______________,_____>___,___________<_______________,_________________________>____,____<___________________,________________________>___________________________,____________>___________________________,_____<_______________.
Run Code Online (Sandbox Code Playgroud)
结果:3898080517870043672800
?(___*__*_____*____*_____*___*___*_____*___*___*___*__*___*_____*___*_____*____*___*____*_____*_____*____*_____*____*____*____*___*___*__*___*____*__*_____*_____*____*____*___*__*____*___*___*____*_____*_____*____*___*__*_____*____*__*_____*___*___*___*_____*____*___*_____*_____*___*___*___*____*__*_____*_____*__*___*__*__*_____*____*_____*___*__*_____*_____*__*____*___*____*_____*_____*___*___*___*_____*__*__*__*__*___*_____*__*___*___*____*_____*___*__*_____*_____*_____*_____*_____*__*__*___*___*_____*____*___*__*___*__*___*_____*__*_____*_____*_____*____*____*___*___*_____*____*____*__*__*_____*___*__*___*_____*_____):-____>_____,___>____.
Run Code Online (Sandbox Code Playgroud)
结果:2001022022202020121001011122021000112012210012001002220120022210000200010200001210022200000200221020000000022012020200000112201100020200
最后编辑:由于基于暴力的答案是不合适的,按照建议,这里是基于库(clpfd)的解决方案:
:- [library(clpfd)].
feed_the_golorp_clp(G, Food) :-
G = (?(F) :- C),
prepare(C, P),
term_variables(G, T),
T ins 0..9,
call(P),
label(T),
with_output_to(string(Food), yields(F)).
yields(E) :- E =.. [_,A,B] -> yields(A), yields(B) ; write(E).
prepare(C, P) :-
compound(C),
C =.. [O, A, B],
member((O, Op), [(<, #<), (>, #>), ((,), (,))]),
prepare(A, Pa),
prepare(B, Pb),
P =.. [Op, Pa, Pb].
prepare(C, C).
Run Code Online (Sandbox Code Playgroud)
在最大的例子上效果很好,产生"3898080517870043672800"......
恢复以前的答案......
纯粹的Prolog:
feed_the_golorp(G, F) :-
G = (_ :- B),
term_variables(G, F),
maplist(food, F),
call(B).
food(X) :- member(X, [0,1,2,3,4,5,6,7,8,9]).
Run Code Online (Sandbox Code Playgroud)
很容易,鉴于你的广泛解释,但效率不高......
?- time(feed_the_golorp(( ?(______________________/____+_______*__-_____*______-___):-__<___,___<____,____<_____,_____<______,______<_______ ), F)).
% 976,115 inferences, 0.874 CPU in 0.876 seconds (100% CPU, 1116785 Lips)
______________________ = __, __ = 0,
____ = 2,
_______ = 5,
_____ = 3,
______ = 4,
___ = 1,
F = [0, 2, 5, 0, 3, 4, 1]
.
Run Code Online (Sandbox Code Playgroud)
编辑我想要一个基于变量排序的反例,因为我觉得我的代码可能不完整/不正确...
的确,我完全错过了连接部分......
feed_the_golorp(G, Food) :-
G = (?(F) :- C),
term_variables(G, T),
maplist(food, T),
call(C),
yields(F, S),
atomic_list_concat(S, Food).
food(X) :- member(X, [0,1,2,3,4,5,6,7,8,9]).
yields(C, [C]) :- number(C).
yields(E, S) :- E =.. [_,A,B], yields(A,Ca), yields(B,Cb), append(Ca,Cb,S).
Run Code Online (Sandbox Code Playgroud)
现在结果更合理
?- time(feed_the_golorp(( ?(___*__*_____*____*_____*___*___*_____*___*___*___*__*___*_____*___*_____*____*___*____*_____*_____*____*_____*____*____*____*___*___*__*___*____*__*_____*_____*____*____*___*__*____*___*___*____*_____*_____*____*___*__*_____*____*__*_____*___*___*___*_____*____*___*_____*_____*___*___*___*____*__*_____*_____*__*___*__*__*_____*____*_____*___*__*_____*_____*__*____*___*____*_____*_____*___*___*___*_____*__*__*__*__*___*_____*__*___*___*____*_____*___*__*_____*_____*_____*_____*_____*__*__*___*___*_____*____*___*__*___*__*___*_____*__*_____*_____*_____*____*____*___*___*_____*____*____*__*__*_____*___*__*___*_____*_____):-____>_____,___>____), F)).
% 17,806 inferences, 0.009 CPU in 0.010 seconds (94% CPU, 1968536 Lips)
___ = 2,
__ = _____, _____ = 0,
____ = 1,
F = '2001022022202020121001011122021000112012210012001002220120022210000200010200001210022200000200221020000000022012020200000112201100020200'
.
Run Code Online (Sandbox Code Playgroud)
或者,更紧凑和屈服的输出类似于示例:
feed_the_golorp(G, Food) :-
G = (?(F) :- C),
term_variables(G, T),
maplist(food, T),
call(C),
with_output_to(string(Food), yields(F)).
food(X) :- member(X, [0,1,2,3,4,5,6,7,8,9]).
yields(E) :- E =.. [_,A,B] -> yields(A), yields(B) ; write(E).
Run Code Online (Sandbox Code Playgroud)
但是,with_output_to/2它只是一个SWI-Prolog实用工具......
这是直接采用 Golorp 描述的ECLiPSe解决方案:
:- lib(ic).
golorp((?(Jaws):-Stomach), Food) :-
term_vars(Jaws, Xs, []),
Xs :: 0..9,
call(Stomach)@ic,
once labeling(Xs),
concat_string(Xs, Food).
term_vars(X, [X|Vs], Vs) :- var(X).
term_vars(Xs, Vs, Vs0) :- nonvar(Xs),
( foreacharg(X,Xs), fromto(Vs,Vs2,Vs1,Vs0) do
term_vars(X, Vs2, Vs1)
).
Run Code Online (Sandbox Code Playgroud)
我使用了 的重复保留变体,并利用了ic约束求解器库定义所有比较谓词等的约束版本term_variables/2
的事实。示例运行:>/2, </2
?- golorp((?(_-_/___*__):-___>__), Food).
___ = 1
__ = 0
Food = "0010"
Yes (0.00s cpu)
Run Code Online (Sandbox Code Playgroud)