【问题标题】:Solving "Feed the Golorp" puzzle in Prolog在 Prolog 中解决“Feed the Golorp”难题
【发布时间】:2014-06-06 02:50:45
【问题描述】:

前段时间,我为 Codeforces 2014 年愚人节比赛创建了一个问题 - “Feed the Golorp”:http://codeforces.com/contest/409/problem/I。 请阅读提供的链接上的问题说明。

这个问题本来打算由完全不了解 Prolog 的人来解决。比赛期间只有 3 人成功解决了这个问题 - 使用 Java、Python 和 C++。

主要挑战是了解需要做什么。对于有一些 Prolog 经验的人来说,这应该是显而易见的 像?(_-_/___*__):-___>__. 这样的golorp 名称定义了一个Prolog 谓词,任务是找到满足谓词的变量的最小值。 有一些细节:再次,请阅读问题说明。

其实在了解了目标之后解决问题并不是那么简单。算法上的任务是根据约束对变量进行拓扑排序。 Golorp 的名称最长可达 1024 个字符,因此需要相当高效的算法。

我在 Python 中使用正则表达式编写了我的参考解决方案。但比赛结束后,我开始想知道如何在 Prolog 中解决这个问题。

由于 golorp 名称的长度可能长达 1024 个字符,因此仅使用 Prolog 回溯来蛮力所有可能性看起来都不可行 - 可能需要约束逻辑编程。

如果我可以从不等式中提取所有变量列表和变量对列表,我可以解决它。例如在 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"

但我不确定如何在没有太多代码的情况下从?(__+___+__-___):-___&gt;__. 获取Vars = [__, ___, __, ___]Ineqs = [(__, ___)]term_variables/2 丢失重复变量。 DCG?

或者有完全不同的更好的方法来解决 Prolog 中的难题吗? (不一定在 ECLiPSe CLP 中)。

更新:几个大的例子来测试:

?(_____________________*_________________________*________________________*___________________*_________________*__________________*___________________________*___*__*____________________*_________________________*_______________*____*___________*_____________*______*_____*_______________*____________*__________________*___________________________*___________________________):-_____>__,_______________<___________________,__>___________,________________________>______,_____________>______,____________________<_________________________,_________________<__________________,_____________<___,____<_________________________,______>____________,________________________>_________________________,_____<____________________,__<____________,_____________________>____________,__________________>_______________,_____>___,___________<_______________,_________________________>____,____<___________________,________________________>___________________________,____________>___________________________,_____<_______________.

结果:3898080517870043672800

?(___*__*_____*____*_____*___*___*_____*___*___*___*__*___*_____*___*_____*____*___*____*_____*_____*____*_____*____*____*____*___*___*__*___*____*__*_____*_____*____*____*___*__*____*___*___*____*_____*_____*____*___*__*_____*____*__*_____*___*___*___*_____*____*___*_____*_____*___*___*___*____*__*_____*_____*__*___*__*__*_____*____*_____*___*__*_____*_____*__*____*___*____*_____*_____*___*___*___*_____*__*__*__*__*___*_____*__*___*___*____*_____*___*__*_____*_____*_____*_____*_____*__*__*___*___*_____*____*___*__*___*__*___*_____*__*_____*_____*_____*____*____*___*___*_____*____*____*__*__*_____*___*__*___*_____*_____):-____>_____,___>____.

结果:20010220222020201210010111220210001120122100120010022201200222100002000102000012100222000002002210200000000220120202002011

【问题讨论】:

    标签: prolog metaprogramming constraint-programming


    【解决方案1】:

    上次编辑:由于基于蛮力的答案不合适,因此按照建议,这里是基于库 (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).
    

    在最大的例子上效果很好,产生“3898080517870043672800”...

    继续上一个答案...

    纯序言:

    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]).
    

    简单,鉴于您的广泛解释,但效率不高......

    ?- 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] 
    .
    

    edit我想要一个基于变量排序的反例,因为我觉得我的代码可能不完整/不正确...

    确实,我完全错过了串联部分...

    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).
    

    现在结果更合理了

    ?- time(feed_the_golorp(( ?(___*__*_____*____*_____*___*___*_____*___*___*___*__*___*_____*___*_____*____*___*____*_____*_____*____*_____*____*____*____*___*___*__*___*____*__*_____*_____*____*____*___*__*____*___*___*____*_____*_____*____*___*__*_____*____*__*_____*___*___*___*_____*____*___*_____*_____*___*___*___*____*__*_____*_____*__*___*__*__*_____*____*_____*___*__*_____*_____*__*____*___*____*_____*_____*___*___*___*_____*__*__*__*__*___*_____*__*___*___*____*_____*___*__*_____*_____*_____*_____*_____*__*__*___*___*_____*____*___*__*___*__*___*_____*__*_____*_____*_____*____*____*___*___*_____*____*____*__*__*_____*___*__*___*_____*_____):-____>_____,___>____), F)).
    % 17,806 inferences, 0.009 CPU in 0.010 seconds (94% CPU, 1968536 Lips)
    ___ = 2,
    __ = _____, _____ = 0,
    ____ = 1,
    F = '2001022022202020121001011122021000112012210012001002220120022210000200010200001210022200000200221020000000022012020200000112201100020200' 
    .
    

    或者,更紧凑,产生类似于示例的输出:

    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).
    

    但是,with_output_to/2 它只是一个 SWI-Prolog 实用程序...

    【讨论】:

      【解决方案2】:

      这是一个直接采用 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)
          ).
      

      我使用了 term_variables/2 的保留重复变体,并利用 ic 约束求解器库定义了所有比较谓词 &gt;/2, &lt;/2 等的约束版本这一事实。示例运行:

      ?- golorp((?(_-_/___*__):-___>__), Food).
      ___ = 1
      __ = 0
      Food = "0010"
      Yes (0.00s cpu)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-10
        • 2013-08-19
        • 1970-01-01
        • 1970-01-01
        • 2020-04-03
        相关资源
        最近更新 更多