【问题标题】:How do I find all solutions to a "Generate and Test" task in Prolog?如何在 Prolog 中找到“生成和测试”任务的所有解决方案?
【发布时间】:2014-11-10 06:29:39
【问题描述】:

我想用 Prolog 解决以下问题,以提高/测试我的 Prolog 知识。

求三个数字 X、Y 和 Z,使得 base10 中的 XYZ 等于 base9 中的 ZYX

来源:Coffee Time Challenges

由于我已经用其他一些语言解决了它,我知道解决方案是:

  • (X, Y, Z) = (0, 0, 0)
  • (X, Y, Z) = (4, 4, 5)

我通过生成 X、Y、Z 的所有组合(10^3 = 1000 个组合)并测试条件是否为真,用其他语言解决了这个问题。

用 Prolog 测试

使用 Prolog,我编写了以下测试程序:

is_solution(X, Y, Z) :- 9>=X, X>=0, 9>=Y, Y>=0, 9>=Z, Z>=0,
                        base10 is (100*X + 10*Y + Z), base9 is (9*9*Z+9*Y+X),
                        base10 = base9.

但是好像有什么不对:

?- is_solution(0,0,0).
false.

?- is_solution(4,4,5).
false.

生成所有解决方案

我以为我可以通过swipl -f main.prolog 获得解决方案:

is_solution(X,Y,Z).
ERROR: >=/2: Arguments are not sufficiently instantiated

我该如何解决这个问题

无需用户交互即可打印所有内容

最后,我想要一个可执行脚本,它可以打印所有解决方案而无需用户交互。我见过findall,我想我必须在这里使用它,但到目前为止我从未(成功地)使用过它。你能告诉我如何打印这个问题的所有解决方案吗?

【问题讨论】:

    标签: prolog


    【解决方案1】:

    Prolog 变量以大写字母或下划线开头,因此base9base10 都不是变量。如果您将其更改为 Base9Base10,您的解决方案将适用于具体实例化。

    要使其适用于变量并避免生成所有组合,请考虑使用约束,这通常会显着减少搜索空间,并且是低级算术的更通用替代方案。例如,在 SICStus 和 SWI-Prolog 中:

    :- use_module(library(clpfd)).
    
    is_solution(X, Y, Z) :- 
        [X,Y,Z] ins 0..9,
        9*9*Z+9*Y+X #= 100*X + 10*Y + Z.
    

    查询示例:

    ?- is_solution(X, Y, Z), label([X,Y,Z]).
    X = Y, Y = Z, Z = 0 ;
    X = Y, Y = 4, Z = 5.
    

    或者findall/3:

    ?- findall(triple(X,Y,Z), (is_solution(X, Y, Z), label([X,Y,Z])), Triples).
    Triples = [triple(0, 0, 0), triple(4, 4, 5)].
    

    要打印所有解决方案,请自行打印:

    ?- is_solution(X, Y, Z), label([X,Y,Z]), format("solution: ~w ~w ~w\n", [X,Y,Z]), false.
    solution: 0 0 0
    solution: 4 4 5
    

    【讨论】:

    • 谢谢!我在这里添加了完整的源代码:github.com/schickling/challenges/blob/master/…
    • 在那sn-p中,range/3完全是多余的,三个between/3调用可以更简洁地写成maplist(between(0,9), [X,Y,Z])。正如我所说,考虑使用约束作为这种低级枚举的更通用替代方案。
    • 谢谢,我删除了range/3。是的,maplist(between(0,9), [X,Y,Z]) 更紧凑。但我还是更喜欢这三个between(0,9,X) 调用,因为我真的很了解它们,而且我知道如何针对其他问题调整它们。
    • 还可以考虑调整不连贯的缩进:main/0 以传统的 Prolog 样式缩进,而 is_solution/3 不是。
    • 如果你坚持不使用约束,至少考虑使用(=:=)/2来比较算术表达式。例如,您可以写成:100*X + 10*Y + Z =:= 9*9*Z + 9*Y + X,而不是 Base10 is (100*X + 10*Y + Z), Base9 is (9*9*Z+9*Y+X), Base10 = Base9。多个between/3 调用不是很优雅:要么使用maplist/2,就像我已经推荐的那样,要么使用像digit(D) :- between(0, 9, D). 这样的辅助谓词。如果您不了解其他形式,则可以使用maplist(digit, [X,Y,Z])
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-30
    相关资源
    最近更新 更多