首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Prolog CLPFD密码学难题

Prolog CLPFD密码学难题
EN

Stack Overflow用户
提问于 2018-04-15 04:09:21
回答 2查看 782关注 0票数 6

最近,我在Google应用商店里发现了一个名为密码学的小游戏。有几十个类似的应用程序。这个想法是把数字和颜色相匹配,这样所有的方程听起来都是真的。

我能够很快地解决问题1-8和问题10,但是问题9对我来说更加困难。

问题9

经过一段时间的修补和猜测,我放弃了,决定给出一个解决方案。我已经使用Prolog/Datalog作为本科生和一些Euler项目问题的一些小任务。以前我见过在有限域(clpfd)库上使用Prolog的约束逻辑编程的15线数独求解器,我决定自己动手。我在用斯威-普罗格

代码语言:javascript
复制
:- use_module(library(clpfd)).
problem(Colors) :-
    Colors = [Pink, Cyan, Yellow, Green, Purple, Red, Brown, White, Lime],
    Colors ins 0..9,
    all_distinct(Colors),
    % The leading digit of a number can't be 0
    Pink #\= 0,
    Red #\= 0,
    White #\= 0,
    Green #\= 0,
    Lime #\= 0,
    Cyan #\= 0,
    % I originally tried to write a predicate generalizing numbers and a list of digits
    % but got in way over my head with CLPFD.
    Number1_1 #= (Pink * 1000) + (Cyan * 100) + (Pink * 10) + Yellow,
    Number1_2 #= (Green * 10) + Purple,
    Number1_3 #= (Cyan * 100) + (Red * 10) + Purple,
    Number2_1 #= (Red * 1000) + (Brown * 100) + (White * 10) + Red,
    Number2_2 #= (Lime * 10) + Yellow,
    Number2_3 #= (Red * 1000) + (Lime * 100) + (Purple * 10) + Pink,
    Number3_1 #= (White * 1000) + (Purple * 100) + (Cyan * 10) + White,
    Number3_2 #= (Green * 1000) + (Cyan * 100) + (Yellow * 10) + Purple,
    Number3_3 #= (Cyan * 1000) + (Red * 100) + (Yellow * 10) + Red,
    % I'm not 100% sure whether to use floored or truncated division here.
    % I thought the difference would be a float vs integer output,
    % but that doesn't make sense with finite domains.
    Number1_1 // Number1_2 #= Number1_3,
    Number1_1 rem Number1_2 #= 0,
    Number2_3 #= Number2_1 + Number2_2,
    Number3_3 #= Number3_1 - Number3_2,
    Number3_1 #= Number1_1 - Number2_1,
    Number3_2 #= Number1_2 * Number2_2,
    Number3_3 #= Number1_3 + Number2_3.

当我在SWI中运行这个查询时,我的输出让我觉得我误解了CLPFD中的一个大概念:

代码语言:javascript
复制
?- problem([Pink, Cyan, Yellow, Green, Purple, Red, Brown, White, Lime]).
Pink in 3..9,
_7756#=Pink+10*Purple+1000*Red+100*Lime,
_7810#=1010*Pink+100*Cyan+Yellow,
all_distinct([Pink, Cyan, Yellow, Green, Purple, Red, Brown, White|...]),
Cyan in 1..7,
_7946#=1000*Cyan+10*Yellow+101*Red,
_7994#=100*Cyan+10*Yellow+1000*Green+Purple,
_8048#=10*Cyan+100*Purple+1001*White,
_8096#=100*Cyan+Purple+10*Red,
Yellow in 0..9,
_8162#=Yellow+10*Lime,
Green in 1..7,
_8216#=10*Green+Purple,
Purple in 0..9,
Red in 1..7,
_8294#=1001*Red+100*Brown+10*White,
Brown in 0..9,
White in 2..8,
Lime in 1..9,
_7756 in 1103..7568,
_8096+_7756#=_7946,
_8294+_8162#=_7756,
_8096 in 110..779,
_7810//_8216#=_8096,
_7810 in 3334..9799,
_8048+_8294#=_7810,
_7810 rem _8216#=0,
_8048 in 2313..8778,
_7946+_7994#=_8048,
_7946 in 1213..7678,
_7994 in 1100..7565,
_8216*_8162#=_7994,
_8216 in 12..79,
_8162 in 14..99,
_8294 in 1021..7486.

我希望颜色列表中的每一种颜色都绑定到0.9范围内的单个独立整数,但情况并非如此。你能帮我找到这个问题的解决办法吗?

编辑

因此,我选择了一个任意的颜色,并开始在约束应该有效的范围内给它分配数字。我运行这个查询时,Cyan绑定到1。

代码语言:javascript
复制
?- problem([Pink, 1, Yellow, Green, Purple, Red, Brown, White, Lime]).
false.

这没道理。前面的“输出”表示"Cyan in 1..7",我认为这意味着该范围内的任何值都是有效的。但是,如果我为Cyan选择另一个任意值:

代码语言:javascript
复制
?- problem([Pink, 2, Yellow, Green, Purple, Red, Brown, White, Lime]).
Pink = 7,
Yellow = 6,
Green = 3,
Purple = 4,
Red = 1,
Brown = 8,
White = 5,
Lime = 9.

我得到了我想要的答案。虽然密码已经解决,但我仍然不明白为什么Prolog的CLPFD库没有完全独立地找到它。

编辑2

我用你的建议来清理代码。我还重新引入了将数字与数字相关联的谓词。这个代码块工作得很完美。

代码语言:javascript
复制
:- use_module(library(clpfd)).

digit_number(0, [], 1).

digit_number(Number, [Digit|Tail], DigitPlace) :-
    digit_number(NextNumber, Tail, NextDigitPlace),
    DigitPlace #= NextDigitPlace * 10,
    PlaceNumber #= Digit * (NextDigitPlace),
    Number #= PlaceNumber + NextNumber.

digit_number(Number, ColorList) :-
    digit_number(Number, ColorList, _).

problem(Colors) :-
    Colors = [Pink, Cyan, Yellow, Green, Purple, Red, Brown, White, Lime],
    Colors ins 0..9,
    all_distinct(Colors),
    digit_number(Number1_1, [Pink, Cyan, Pink, Yellow]),
    digit_number(Number1_2, [Green, Purple]),
    digit_number(Number1_3, [Cyan, Red, Purple]),
    digit_number(Number2_1, [Red, Brown, White, Red]),
    digit_number(Number2_2, [Lime, Yellow]),
    digit_number(Number2_3, [Red, Lime, Purple, Pink]),
    digit_number(Number3_1, [White, Purple, Cyan, White]),
    digit_number(Number3_2, [Green, Cyan, Yellow, Purple]),
    digit_number(Number3_3, [Cyan, Red, Yellow, Red]),
    Number1_1 // Number1_2 #= Number1_3,
    Number1_1 rem Number1_2 #= 0,
    Number2_1 + Number2_2 #= Number2_3,
    Number3_1 - Number3_2 #= Number3_3,
    Number1_1 - Number2_1 #= Number3_1,
    Number1_2 * Number2_2 #= Number3_2,
    Number1_3 + Number2_3 #= Number3_3,
    label(Colors).
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-04-15 09:50:31

另一个答案显示了一种得到你想要的结果的方法,但是我想回答你的一些问题。

我仍然不明白为什么Prolog的CLPFD库没有完全独立地找到它。

Prolog是一种或多或少的声明性编程语言,但是(尽管出于宣传的原因,我们喜欢假装),您不能只写下任何在逻辑上与您的问题等价的内容,并期望它能够正确高效地执行。特别是,不同目标的执行顺序非常重要,尽管它不应产生任何逻辑上的差异。对于算术来说尤其如此。考虑:

代码语言:javascript
复制
?- between(1, 99999999, N), N > 99999998.
N = 99999999.  % correct but slooooow

?- N > 99999998, between(1, 99999999, N).
ERROR: >/2: Arguments are not sufficiently instantiated

对中电(FD)也做同样的工作,效果要好得多:

代码语言:javascript
复制
?- N in 1..99999999, N #> 99999998.
N = 99999999.  % correct and fast!

?- N #> 99999998, N in 1..99999999.
N = 99999999.  % also correct, also fast!

CLP(FD)允许您编写比其他解决方案更正确、更具声明性和效率更高的程序,除非您手动优化它们。

为了实现这一点,与普通Prolog不同,CLP(FD)将约束集合与实际的解决方案搜索分离开来。当您的程序继续执行并创建约束时,CLP(FD)将进行一些简化,例如在您的示例中,它自行确定Cyan in 1..7,或者在我前面的示例中,它可以立即找到唯一的解决方案。但总的来说,这些简化并不能完全解决问题。

原因之一是,简单地说,性能:搜索可能很慢。如果知道更多的约束,它可能会更快,因为对已经约束的变量的新约束只会使搜索空间变小,但永远不会变大!把它推迟到实际需要具体的答案是有道理的。

因此,要得到具体的结果,您需要调用一个有系统地枚举解决方案的标签谓词。在simple中,简单的是indomain/1label/1,一般的是labeling/2.后者甚至允许您影响搜索空间探索策略,如果您对问题域有一定的了解,这将是有用的。

前面的“输出”表示"Cyan in 1..7",我认为这意味着该范围内的任何值都是有效的。

不完全是:这意味着如果Cyan有一个有效的解决方案,那么它就在1到7之间,它不能保证该范围内的所有值都是解决方案。例如:

代码语言:javascript
复制
?- X in 1..5, Y in 1..5, X #< Y.
X in 1..4,
X#=<Y+ -1,
Y in 2..5.

3在范围1..4中,3在范围2..5中,因此纯粹基于这一点,我们可以期待使用X = 3Y = 3的解决方案。但由于额外的限制,这是不可能的。只有标签才能给出有保证的解决方案的答案,而且只有当你给查询中的所有变量贴上标签的时候。

也请看这里非常好的答案:https://stackoverflow.com/a/27218564/4391743

编辑:

%,我不确定是使用泛型除法还是截断除法。%我认为区别是浮点数和整数输出,%,但这在有限域上是没有意义的。Number1_1 // Number1_2 #= Number1_3,

事实上,分数除法在这里没有意义,但Prolog会告诉您:

代码语言:javascript
复制
?- X in 1..5, Y in 1..5, Z #= X // Y.
X in 1..5,
X//Y#=Z,
Y in 1..5,
Z in 0..5.

?- X in 1..5, Y in 1..5, Z #= X / Y.
ERROR: Domain error: `clpfd_expression' expected, found `_G6388/_G6412'
票数 5
EN

Stack Overflow用户

发布于 2018-04-15 09:05:02

您的代码工作正常,只需添加标签(C)

代码语言:javascript
复制
?- problem(C), label(C).
C = [7, 2, 6, 3, 4, 1, 8, 5, 9] .
票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49838393

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档