我是prolog的初学者。 任务是生成n位数字而不使用1和0。怎么做?你是否生成随机数然后删除1和0(听起来效率低)?
答案 0 :(得分:4)
您所描述的绝对是一种方式。正如您所提到的,它并不是一种特别良好的方式,因为它非常低效。
然而,你提到的方法很常见,它有自己的名字:它通常被称为 generate和test ,因为我们首先生成,然后拒绝或接受解决方案,或进一步修改以满足所有约束条件。
通常更高效方法是首先 约束整个解决方案,以便表达所有要求,然后让系统仅在已经受限的空间内搜索。这在Prolog中特别容易,因为它提供了内置的约束,您可以在开始搜索解决方案之前发布它们,并且它们将自动考虑在内在搜索之前和搜索期间。
例如,您可以使用Prolog系统的 CLP(FD)约束来表达对整数的所需要求,如下所示:
n_digits(N, Ds, Num) :- length(Ds, N), Ds ins 2..9, reverse(Ds, Rs), foldl(pow10, Rs, 0-0, _-Num). pow10(D, Pow0-S0, Pow-S) :- Pow #= Pow0 + 1, S #= D*10^Pow0 + S0.
因此,我们将数字表示为数字的列表,并且使用合适的约束将此列表与相应的整数相关联。关键点在于在实际生成单个解决方案之前发生了所有这些,并且找到的所有解决方案将满足所述约束。此外,这是一个非常通用的关系,可以在所有方向上运行,我们可以使用它来测试,生成和完成< / em> solutions。
以下是一个示例查询,询问这些3位数的数字通常如何 :
?- n_digits(3, Ds, N).
作为回应,我们得到:
Ds = [_11114, _11120, _11126], _11114 in 2..9, _11114*100#=_11182, _11182 in 200..900, _11182+_11236#=N, _11236 in 22..99, _11282+_11126#=_11236, _11282 in 20..90, _11120*10#=_11282, _11120 in 2..9, _11126 in 2..9, N in 222..999.
我们可以使用label/1
来获取具体的解决方案:
?- n_digits(3, Ds, N), label(Ds). Ds = [2, 2, 2], N = 222 ; Ds = [2, 2, 3], N = 223 ; Ds = [2, 2, 4], N = 224 ; Ds = [2, 2, 5], N = 225 ; etc.
在描述涉及整数的任务时,CLP(FD)约束通常非常适合并且允许非常通用的解决方案。
例如,很容易纳入其他要求:
?- n_digits(3, Ds, N), N #> 300, label(Ds). Ds = [3, 2, 2], N = 322 ; Ds = [3, 2, 3], N = 323 ; Ds = [3, 2, 4], N = 324 ; etc.
答案 1 :(得分:2)
clp(fd)解决方案肯定有很多优雅。如果你只想要一个N位数为2..9的随机数,那就有一个更直接的方法
n_digits(N, Ds, Num) :-
length(Ds, N),
maplist(random_between(0'2, 0'9), Ds),
number_codes(Num, Ds).
如果您使用between
代替random_between
,则会生成上述数字。我在http://swish.swi-prolog.org/p/JXELeTrX.swinb进行了比较,我们可以看到
?- time(n_digits(1000, _Ds, N)).
6,010 inferences, 0.003 CPU in 0.003 seconds (100% CPU, 1814820 Lips)
Vs以上。使用clp(fd)解决方案,在42秒后耗尽内存(256Mb)。
?- time((n_digits(1000, _Ds, N), label(_Ds))).
62,143,001 inferences, 42.724 CPU in 42.723 seconds (100% CPU, 1454531 Lips)
Out of global stack
答案 2 :(得分:2)
我想用现有的答案来补充现有的答案,我认为值得研究。
请考虑以下事项:
n_digits(N, Ds, Expr) :- length(Ds, N), Ds ins 2..9, foldl(pow10, Ds, 0, Expr). pow10(D, S, S*10+D).
示例查询:
?- time((n_digits(1000, Ds, Expr), label(Ds))). % 104,063 inferences, 0.013 CPU in 0.017 seconds (75% CPU, 8214635 Lips) Ds = [2, 2, 2, 2, 2, 2, 2, 2, 2|...], Expr = ((((... * ... + 2)*10+2)*10+2)*10+2)*10+2 .
在这里,我们还使用约束以声明方式描述列表,另外构建一个算术CLP(FD)表达式,计算到结果数字。
与其他基于CLP(FD)的版本一样,这让我们轻松在数字本身上发布了额外的约束。
例如:
?- n_digits(1000, Ds, Expr), Expr #> 5*10^999, label(Ds). Ds = [5, 2, 2, 2, 2, 2, 2, 2, 2|...], Expr = ((((... * ... + 2)*10+2)*10+2)*10+2)*10+2 .
当您遇到基于CLP(FD)的版本时,您发现用例太慢,我的建议是寻找提高效率的方法。我不能 - 至少不是直面 - 建议改为低级别的方法。我已经看到太多有才华的Prolog程序员陷入了低级语言结构的困境,并且从未找到实际改进高级方面的方法,以便他们成为两者 general 和足以满足实际相关的所有用例。这是他们最需要的才能!
答案 3 :(得分:2)
我想补充一点,从另一个角度考虑这个任务。
这一次,我想从Jan的回答开始:
n_digits(N, Ds, Num) :- length(Ds, N), maplist(random_between(0'2, 0'9), Ds), number_codes(Num, Ds).
它的主要优点是它非常直接,而且快:
?- time(n_digits(1000, Ds, Num)). % 6,007 inferences, 0.002 CPU in 0.002 seconds (92% CPU, 2736674 Lips)
事实上,它的速度非常快,只能在某些时候 :
?- length(_, N), n_digits(3, Ds, 345). N = 548, Ds = [51, 52, 53] ; N = 1309, Ds = [51, 52, 53] ; N = 1822, Ds = [51, 52, 53] .
然而,到目前为止,我们已经习惯了这样一个众所周知的事实:与其性能相比,解决方案的正确性仅仅是次要关注,所以让我们继续 解决方案是正确的。
我们可以直接将此映射到CLP(FD)约束,方法是修改粗体部分,如下所示:
n_digits(N, Ds, Num) :- length(Ds, N), Ds ins 0'2..0'9, labeling([random_value(0)], Ds), number_codes(Num, Ds).
有没有人理解这个?请让我知道,我会尽我所能向所有感兴趣的人说清楚。如果有人想了解更多信息,最好的方法是简单地提交一个新问题。
为了进行比较,这里再次是上一个查询,它说明了这个谓词的行为与关系的预期相同:
?- length(_, N), n_digits(3, Ds, 345). N = 0, Ds = [51, 52, 53] ; N = 1, Ds = [51, 52, 53] ; N = 2, Ds = [51, 52, 53] .
在这个具体案例中,使用CLP(FD)约束的成本大约是一个数量级的性能,使用 优化性能的约束求解器请注意:
?- time(n_digits(1000, Ds, Num)). % 134,580 inferences, 0.023 CPU in 0.026 seconds (87% CPU, 5935694 Lips)
我不敢提及基于CLP(FD)的版本更可靠,因为这在实践中价值不大。因此,根据您的使用情况,开销可能会过高。让我们假设对于这个具体案例,我们愿意牺牲2%的时间来使用CLP(FD)约束。
在许多可能的示例中,现在让我们考虑以下轻微的变体任务:
让我们描述满足所有约束的数字列表,也是回文。
以下是对回文的可能的声明性描述:
palindrome --> []. palindrome --> [_]. palindrome --> [P], palindrome, [P].
使用基于CLP(FD)的版本,我们可以轻松将这个约束与已经陈述的约束结合起来:
n_digits(N, Ds, Num) :- length(Ds, N), Ds ins 0'2..0'9, phrase(palindrome, Ds), labeling([random_value(0)], Ds), number_codes(Num, Ds).
示例查询:
?- time(n_digits(1000, Ds, Num)). % 12,552,433 inferences, 1.851 CPU in 1.943 seconds (95% CPU, 6780005 Lips)
为了比较,我们如何将这个直接附加约束合并到Jan的版本中?这样做很有诱惑力:
n_digits(N, Ds, Num) :- length(Ds, N), phrase(palindrome, Ds), maplist(random_between(0'2, 0'9), Ds), number_codes(Num, Ds).
非常直截了当,对吧?唯一的问题是它产生:
?- time(n_digits(1000, Ds, Num)). % 4,013 inferences, 0.041 CPU in 0.046 seconds (88% CPU, 97880 Lips) false.
现在好运向任何新手解释这个!在反驳所解释的基于CLP(FD)的版本的复杂性时,请考虑解释这种纯粹的程序现象的更多复杂性,这些现象不可能是仅通过陈述性推理来理解。
此外,通过使用低级功能解决任务任务的良好外观,以便仍然可以轻松添加其他约束。
顺便说一句,有时,查询实际上会成功!
?- length(_, N), time(n_digits(3, Ds, Num)). % 28 inferences, 0.000 CPU in 0.000 seconds (85% CPU, 848485 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 1400000 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (80% CPU, 1166667 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (79% CPU, 1473684 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 1473684 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 1473684 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 1473684 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 1473684 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 1473684 Lips) % 28 inferences, 0.000 CPU in 0.000 seconds (83% CPU, 1473684 Lips) % 26 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 1444444 Lips) N = 10, Ds = [52, 50, 52], Num = 424 .
我只能说:这种方法不能诚实推荐,可以吗?
如果回答对你来说太不现实,请考虑以下任务:
让我们描述没有0,1 和5 的数字列表。
再次,使用CLP(FD),这是直截了当的:
n_digits(N, Ds, Num) :- length(Ds, N), Ds ins 0'2..0'9, maplist(#\=(0'5), Ds), labeling([random_value(0)], Ds), number_codes(Num, Ds).
示例查询:
?- time(n_digits(1000, Ds, Num)). % 203,529 inferences, 0.036 CPU in 0.038 seconds (93% CPU, 5662707 Lips)
为了进行比较,如果没有 CLP(FD)约束,你将如何做到?
现在让我们结合两个要求:
让我们描述不包含0,1 和5 的数字列表,这些数字也是回文。
再次,使用CLP(FD)版本,我们可以简单地陈述要求:
n_digits(N, Ds, Num) :- length(Ds, N), Ds ins 0'2..0'9, maplist(#\=(0'5), Ds), phrase(palindrome, Ds), labeling([random_value(0)], Ds), number_codes(Num, Ds).
示例查询:
?- time(n_digits(1000, Ds, Num)). % 20,117,000 inferences, 2.824 CPU in 2.905 seconds (97% CPU, 7123594 Lips)
这些示例说明了使用约束的一般机制,如果您需要稍微改变先前的解决方案,您实际上某个地方。代码很容易适应,它可以很好地扩展 。
现在,作为最后一句话:我之前发布的基于CLP(FD)的版本为您提供了超越这种直接翻译的方式。对于其他版本,您不仅可以在数字的列表上发布其他约束,还可以在数字本身上发布!对于不使用约束的任何版本,这只是完全无法接触!在搜索解决方案开始之前,也会考虑这些约束。