在prolog中生成n位数字w / o 1s和0s

时间:2017-04-11 16:56:04

标签: random prolog clpfd

我是prolog的初学者。 任务是生成n位数字而不使用1和0。怎么做?你是否生成随机数然后删除1和0(听起来效率低)?

4 个答案:

答案 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)的版本为您提供了超越这种直接翻译的方式。对于其他版本,您不仅可以在数字的列表上发布其他约束,还可以在数字本身上发布!对于使用约束的任何版本,这只是完全无法接触!在搜索解决方案开始之前,会考虑这些约束。