Prolog:使用 clp(FD) 计算 OEIS A031877 ("nontrivial reversal numbers")
Prolog: calculating OEIS A031877 ("nontrivial reversal numbers") using clp(FD)
浏览 太棒了 On-Line Encyclopedia of Integer Sequences (cf. en.wikipedia.org),我偶然发现了以下整数序列:
A031877: Nontrivial reversal numbers (numbers which are integer multiples of their reversals), excluding palindromic numbers and multiples of 10.
通过重新使用我为回答相关问题“Faster implementation of verbal arithmetic in Prolog”而编写的一些代码,我可以
毫不费力地写下解决方案——感谢 clpfd!
:- use_module(library(clpfd)).
我们定义核心关系 a031877_ndigits_/3
基于
digits_number/2
(之前定义):
a031877_ndigits_(Z_big,N_digits,[K,Z_small,Z_big]) :-
K #> 1,
length(D_big,N_digits),
reverse(D_small,D_big),
digits_number(D_big,Z_big),
digits_number(D_small,Z_small),
Z_big #= Z_small * K.
核心关系是确定性的并且普遍终止
N_digit
是一个具体的整数。亲自查看 N_digit
!
的前 100 个值
?- time((N in 0..99,indomain(N),a031877_ndigits_(Z,N,Zs),false)).
% 3,888,222 inferences, 0.563 CPU in 0.563 seconds (100% CPU, 6903708 Lips)
false
让我们运行一些查询!
?- a031877_ndigits_(87912000000087912,17,_).
true % succeeds, as expected
; false.
?- a031877_ndigits_(87912000000987912,17,_).
false. % fails, as expected
接下来,让我们找到一些非平凡的反转数,其中包含 正好四个 小数位:
?- a031877_ndigits_(Z,4,Zs), labeling([],Zs).
Z = 8712, Zs = [4,2178,8712]
; Z = 9801, Zs = [9,1089,9801]
; false.
好的!让我们测量一下 运行证明上述查询普遍终止所需的时间!
?- time((a031877_ndigits_(Z,4,Zs),labeling([],Zs),false)).
% 11,611,502 inferences, 3.642 CPU in 3.641 seconds (100% CPU, 3188193 Lips)
false. % terminates universally
现在,这样 太长了!
我可以做些什么来加快速度?使用不同的 and/or 其他约束?甚至是多余的?或者可能识别并消除削减搜索 space 大小的对称性?不同的 clp(*) 域(b、q、r、set)呢?还是不同的 consistency/propagation 技术?或者更确切地说是 Prolog 风格的协程?
有想法吗?我想要他们!提前致谢。
到目前为止...没有答案:(
我想出了以下...
如何为 labeling/2
使用不同的变量?
a031877_ndigitsNEW_(Z_big,N_digits,/* [K,Z_small,Z_big] */
[K|D_big]) :-
K #> 1,
length(D_big,N_digits),
reverse(D_small,D_big),
digits_number(D_big,Z_big),
digits_number(D_small,Z_small),
Z_big #= Z_small * K.
让我们测量一些运行时间!
?- time((a031877_ndigits_(Z,4,Zs),labeling([ff],Zs),false)).
% 14,849,250 inferences, 4.545 CPU in 4.543 seconds (100% CPU, 3267070 Lips)
false.
?- time((a031877_ndigitsNEW_(Z,4,Zs),labeling([ff],Zs),false)).
% 464,917 inferences, 0.052 CPU in 0.052 seconds (100% CPU, 8962485 Lips)
false.
更好!但是我们可以更进一步吗?
?- time((a031877_ndigitsNEW_(Z,5,Zs),labeling([ff],Zs),false)).
% 1,455,670 inferences, 0.174 CPU in 0.174 seconds (100% CPU, 8347374 Lips)
false.
?- time((a031877_ndigitsNEW_(Z,6,Zs),labeling([ff],Zs),false)).
% 5,020,125 inferences, 0.614 CPU in 0.613 seconds (100% CPU, 8181572 Lips)
false.
?- time((a031877_ndigitsNEW_(Z,7,Zs),labeling([ff],Zs),false)).
% 15,169,630 inferences, 1.752 CPU in 1.751 seconds (100% CPU, 8657015 Lips)
false.
当然还有很大的改进空间!一定有...
我们可以通过将数论属性转化为约束语言来做得更好!
All terms are of the form 87...12 = 4*21...78 or 98...01 = 9*10...89.
我们在a031877_ndigitsNEW_/3
的基础上实现a031877_ndigitsNEWER_/3
,直接在属性上面加上两个有限域约束:
a031877_ndigitsNEWER_(Z_big,N_digits,[K|D_big]) :-
K in {4}\/{9}, % (new)
length(D_big,N_digits),
D_big ins (0..2)\/(7..9), % (new)
reverse(D_small,D_big),
digits_number(D_big,Z_big),
digits_number(D_small,Z_small),
Z_big #= Z_small * K.
让我们重新[=28=]我们之前使用的基准!
?- time((a031877_ndigitsNEWER_(Z,5,Zs),labeling([ff],Zs),false)).
% 73,011 inferences, 0.006 CPU in 0.006 seconds (100% CPU, 11602554 Lips)
false.
?- time((a031877_ndigitsNEWER_(Z,6,Zs),labeling([ff],Zs),false)).
% 179,424 inferences, 0.028 CPU in 0.028 seconds (100% CPU, 6399871 Lips)
false.
?- time((a031877_ndigitsNEWER_(Z,7,Zs),labeling([ff],Zs),false)).
% 348,525 inferences, 0.037 CPU in 0.037 seconds (100% CPU, 9490920 Lips)
false.
总结:对于这三个查询,我们始终观察到所需的搜索显着减少。只需考虑推理计数减少了多少:1.45M -> 73k,5M -> 179k,15.1M -> 348k。
我们能否做得更好(同时保留代码的声明性)?我不知道,我想是的...
浏览 太棒了 On-Line Encyclopedia of Integer Sequences (cf. en.wikipedia.org),我偶然发现了以下整数序列:
A031877: Nontrivial reversal numbers (numbers which are integer multiples of their reversals), excluding palindromic numbers and multiples of 10.
通过重新使用我为回答相关问题“Faster implementation of verbal arithmetic in Prolog”而编写的一些代码,我可以 毫不费力地写下解决方案——感谢 clpfd!
:- use_module(library(clpfd)).
我们定义核心关系 a031877_ndigits_/3
基于
digits_number/2
(之前定义):
a031877_ndigits_(Z_big,N_digits,[K,Z_small,Z_big]) :-
K #> 1,
length(D_big,N_digits),
reverse(D_small,D_big),
digits_number(D_big,Z_big),
digits_number(D_small,Z_small),
Z_big #= Z_small * K.
核心关系是确定性的并且普遍终止
N_digit
是一个具体的整数。亲自查看 N_digit
!
?- time((N in 0..99,indomain(N),a031877_ndigits_(Z,N,Zs),false)).
% 3,888,222 inferences, 0.563 CPU in 0.563 seconds (100% CPU, 6903708 Lips)
false
让我们运行一些查询!
?- a031877_ndigits_(87912000000087912,17,_). true % succeeds, as expected ; false. ?- a031877_ndigits_(87912000000987912,17,_). false. % fails, as expected
接下来,让我们找到一些非平凡的反转数,其中包含 正好四个 小数位:
?- a031877_ndigits_(Z,4,Zs), labeling([],Zs).
Z = 8712, Zs = [4,2178,8712]
; Z = 9801, Zs = [9,1089,9801]
; false.
好的!让我们测量一下 运行证明上述查询普遍终止所需的时间!
?- time((a031877_ndigits_(Z,4,Zs),labeling([],Zs),false)).
% 11,611,502 inferences, 3.642 CPU in 3.641 seconds (100% CPU, 3188193 Lips)
false. % terminates universally
现在,这样 太长了!
我可以做些什么来加快速度?使用不同的 and/or 其他约束?甚至是多余的?或者可能识别并消除削减搜索 space 大小的对称性?不同的 clp(*) 域(b、q、r、set)呢?还是不同的 consistency/propagation 技术?或者更确切地说是 Prolog 风格的协程?
有想法吗?我想要他们!提前致谢。
到目前为止...没有答案:(
我想出了以下...
如何为 labeling/2
使用不同的变量?
a031877_ndigitsNEW_(Z_big,N_digits,/*[K,Z_small,Z_big]*/ [K|D_big]) :- K #> 1, length(D_big,N_digits), reverse(D_small,D_big), digits_number(D_big,Z_big), digits_number(D_small,Z_small), Z_big #= Z_small * K.
让我们测量一些运行时间!
?- time((a031877_ndigits_(Z,4,Zs),labeling([ff],Zs),false)).
% 14,849,250 inferences, 4.545 CPU in 4.543 seconds (100% CPU, 3267070 Lips)
false.
?- time((a031877_ndigitsNEW_(Z,4,Zs),labeling([ff],Zs),false)).
% 464,917 inferences, 0.052 CPU in 0.052 seconds (100% CPU, 8962485 Lips)
false.
更好!但是我们可以更进一步吗?
?- time((a031877_ndigitsNEW_(Z,5,Zs),labeling([ff],Zs),false)).
% 1,455,670 inferences, 0.174 CPU in 0.174 seconds (100% CPU, 8347374 Lips)
false.
?- time((a031877_ndigitsNEW_(Z,6,Zs),labeling([ff],Zs),false)).
% 5,020,125 inferences, 0.614 CPU in 0.613 seconds (100% CPU, 8181572 Lips)
false.
?- time((a031877_ndigitsNEW_(Z,7,Zs),labeling([ff],Zs),false)).
% 15,169,630 inferences, 1.752 CPU in 1.751 seconds (100% CPU, 8657015 Lips)
false.
当然还有很大的改进空间!一定有...
我们可以通过将数论属性转化为约束语言来做得更好!
All terms are of the form 87...12 = 4*21...78 or 98...01 = 9*10...89.
我们在a031877_ndigitsNEW_/3
的基础上实现a031877_ndigitsNEWER_/3
,直接在属性上面加上两个有限域约束:
a031877_ndigitsNEWER_(Z_big,N_digits,[K|D_big]) :- K in {4}\/{9}, % (new) length(D_big,N_digits), D_big ins (0..2)\/(7..9), % (new) reverse(D_small,D_big), digits_number(D_big,Z_big), digits_number(D_small,Z_small), Z_big #= Z_small * K.
让我们重新[=28=]我们之前使用的基准!
?- time((a031877_ndigitsNEWER_(Z,5,Zs),labeling([ff],Zs),false)).
% 73,011 inferences, 0.006 CPU in 0.006 seconds (100% CPU, 11602554 Lips)
false.
?- time((a031877_ndigitsNEWER_(Z,6,Zs),labeling([ff],Zs),false)).
% 179,424 inferences, 0.028 CPU in 0.028 seconds (100% CPU, 6399871 Lips)
false.
?- time((a031877_ndigitsNEWER_(Z,7,Zs),labeling([ff],Zs),false)).
% 348,525 inferences, 0.037 CPU in 0.037 seconds (100% CPU, 9490920 Lips)
false.
总结:对于这三个查询,我们始终观察到所需的搜索显着减少。只需考虑推理计数减少了多少:1.45M -> 73k,5M -> 179k,15.1M -> 348k。
我们能否做得更好(同时保留代码的声明性)?我不知道,我想是的...