この回答では、前の回答と同じようにclpfdを使用します。
:- use_module(library(clpfd)).
(後で)簡単に直接比較できるように、ここに示す述語を呼び出しますn_fac/2
。
n_fac(N_expr,F_expr) :-
N #= N_expr, % eval arith expr
F #= F_expr, % eval arith expr
n_facAux(N,F).
この前の答えのようn_fac/2
に、算術式の使用を認めます。
n_facAux(0,1). % 0! = 1
n_facAux(1,1). % 1! = 1
n_facAux(2,2). % 2! = 2
n_facAux(N,F) :-
N #> 2,
F #> N, % redundant constraint
% to help `n_fac(N,N)` terminate
n0_n_fac0_fac(3,N,6,F). % general case starts with "3! = 6"
ヘルパー述語n_facAux/2
は、「実際の」作業を次のように委任しますn0_n_fac0_fac/4
。
n0_n_fac0_fac(N ,N,F ,F).
n0_n_fac0_fac(N0,N,F0,F) :-
N0 #< N,
N1 #= N0+1, % count "up", not "down"
F1 #= F0*N1, % calc `1*2*...*N`, not `N*(N-1)*...*2*1`
F1 #=< F, % enforce redundant constraint
n0_n_fac0_fac(N1,N,F1,F).
n_fac/2
比較してみましょうn_factorial/2
!
?- n_factorial(47,F).
F = 258623241511168180642964355153611979969197632389120000000000
; false.
?- n_fac(47,F).
F = 258623241511168180642964355153611979969197632389120000000000
; false.
?- n_factorial(N,1).
N = 0
; N = 1
; false.
?- n_fac(N,1).
N = 0
; N = 1
; false.
?- member(F,[3,1_000_000]), ( n_factorial(N,F) ; n_fac(N,F) ).
false. % both predicates agree
わかった!これまでのところ同じです...少しブルートフォーステストをしてみませんか?
?-time((F1#\ = F2、n_factorial(N、F1)、n_fac(N、F2)))。
%57,739,784推論、7.112秒で6.415 CPU(90%CPU、9001245リップ)
%実行が中止されました
?-time((F1#\ = F2、n_fac(N、F2)、n_factorial(N、F1)))。
%52,815,182推論、6.631秒で5.942 CPU(90%CPU、8888423リップ)
%実行が中止されました
?-time((N1#> 1、N2#> 1、N1#\ = N2、n_fac(N1、F)、n_factorial(N2、F)))。
%99,463,654推論、16.575秒で15.767 CPU(95%CPU、6308401リップ)
%実行が中止されました
?-time((N1#> 1、N2#> 1、N1#\ = N2、n_factorial(N2、F)、n_fac(N1、F)))。
%187,621,733推論、18.232秒で17.192 CPU(94%CPU、10913552リップ)
%実行が中止されました
最初の数百の値に違いはありませんN in 2..sup
...良いです!
次に進む:次はどうですか(この回答へのコメントで提案されています)?
?- n_factorial(N,N), false.
false.
?- n_fac(N,N), false.
false.
元気にやっています!同一の終了動作...もっと?
?- N #< 5, n_factorial(N,_), false.
false.
?- N #< 5, n_fac(N,_), false.
false.
?- F in 10..100, n_factorial(_,F), false.
false.
?- F in 10..100, n_fac(_,F), false.
false.
大丈夫!まだ同じ終了プロパティ!もう少し深く掘り下げましょう!次はどうですか?
?-inf..10のF、n_factorial(_、F)、false。
...%Execution Aborted%はユニバーサルに終了しません
?-inf..10のF、n_fac(_、F)、false。
false。%はユニバーサルに終了します
D'oh!最初のクエリは終了せず、2番目のクエリは終了します。なんてスピードアップ!:)
経験的な実行時測定を行いましょう!
?-member(Exp、[6,7,8,9])、F#= 10 ^ Exp、time(n_factorial(N、F)); 本当。
%328,700推論、0.043秒で0.043 CPU(100%CPU、7660054リップ)
%1,027,296推論、0.153秒で0.153 CPU(100%CPU、6735634リップ)
%5,759,864推論、1.967秒で1.967 CPU(100%CPU、2927658リップ)
%22,795,694推論、23.908秒で23.911 CPU(100%CPU、953351リップ)
本当。
?-member(Exp、[6,7,8,9])、F#= 10 ^ Exp、time(n_fac(N、F)); 本当。
%1,340推論、0.000秒で0.000 CPU(99%CPU、3793262リップ)
%1,479推論、0.000秒で0.000 CPU(100%CPU、6253673リップ)
%1,618推論、0.000秒で0.000 CPU(100%CPU、5129994リップ)
%1,757推論、0.000秒で0.000 CPU(100%CPU、5044792リップ)
本当。
わお!もう少し?
?-member(U、[10,100,1000])、time((N in 1..U、n_factorial(N、_)、false)); 本当。
%34,511推論、0.004秒で0.004 CPU(100%CPU、9591041リップ)
%3,091,271推論、0.322秒で0.322 CPU(100%CPU、9589264リップ)
%305,413,871推論、90.721秒で90.732 CPU(100%CPU、3366116リップ)
本当。
?-member(U、[10,100,1000])、time((N in 1..U、n_fac(N、_)、false)); 本当。
%3,729推論、0.001秒で0.001 CPU(100%CPU、2973653リップ)
%36,369推論、0.004秒で0.004 CPU(100%CPU、10309784リップ)
%362,471推論、0.036秒で0.036 CPU(100%CPU、9997610リップ)
本当。
結論は?
- この回答で提示されているコードは、必要なだけ低レベルです:忘れてください
is/2
!
- 冗長な制約は報われる可能性があり、実際に報われます。
- 算術演算の順序(「上」と「下」を数える)もかなりの違いを生む可能性があります。
- 「大きい」の階乗を計算する場合は、別のアプローチ
N
を使用することを検討してください。
- clpfdを使用してください!