Can someone helping me to find a way to get the inverse factorial in Prolog...
For example inverse_factorial(6,X)
===> X = 3
.
I have been working on it a lot of time.
I currently have the factorial, but i have to make it reversible. Please help me.
Prolog's predicates are relations, so once you have defined factorial, you have implicitly defined the inverse too. However, regular arithmetics is moded in Prolog, that is, the entire expression in (is)/2
or (>)/2
has to be known at runtime, and if it is not, an error occurs. Constraints overcome this shortcoming:
:- use_module(library(clpfd)). n_factorial(0, 1). n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1, F1).
This definition now works in both directions.
?- n_factorial(N,6). N = 3 ; false. ?- n_factorial(3,F). F = 6 ; false.
Since SICStus 4.3.4 and SWI 7.1.25 also the following terminates:
?- n_factorial(N,N).
N = 1
; N = 2
; false.
See the manual for more.
For reference, here is the best implementation of a declarative factorial
predicate I could come up with.
Two main points are different from @false's answer:
It uses an accumulator argument, and recursive calls increment the factor we multiply the factorial with, instead of a standard recursive implementation where the base case is
0
. This makes the predicate much faster when the factorial is known and the initial number is not.It uses
if_/3
and(=)/3
extensively, from modulereif
, to get rid of unnecessary choice points when possible. It also uses(#>)/3
and the reified(===)/6
which is a variation of(=)/3
for cases where we have two couples that can be used for theif -> then
part ofif_
.
factorial/2
factorial(N, F) :-
factorial(N, 0, 1, F).
factorial(N, I, N0, F) :-
F #> 0,
N #>= 0,
I #>= 0,
I #=< N,
N0 #> 0,
N0 #=< F,
if_(I #> 2,
( F #> N,
if_(===(N, I, N0, F, T1),
if_(T1 = true,
N0 = F,
N = I
),
( J #= I + 1,
N1 #= N0*J,
factorial(N, J, N1, F)
)
)
),
if_(N = I,
N0 = F,
( J #= I + 1,
N1 #= N0*J,
factorial(N, J, N1, F)
)
)
).
(#>)/3
#>(X, Y, T) :-
zcompare(C, X, Y),
greater_true(C, T).
greater_true(>, true).
greater_true(<, false).
greater_true(=, false).
(===)/6
===(X1, Y1, X2, Y2, T1, T) :-
( T1 == true -> =(X1, Y1, T)
; T1 == false -> =(X2, Y2, T)
; X1 == Y1 -> T1 = true, T = true
; X1 \= Y1 -> T1 = true, T = false
; X2 == Y2 -> T1 = false, T = true
; X2 \= Y2 -> T1 = false, T = false
; T1 = true, T = true, X1 = Y1
; T1 = true, T = false, dif(X1, Y1)
).
Some queries
?- factorial(N, N).
N = 1 ;
N = 2 ;
false. % One could probably get rid of the choice point at the cost of readability
?- factorial(N, 1).
N = 0 ;
N = 1 ;
false. % Same
?- factorial(10, N).
N = 3628800. % No choice point
?- time(factorial(N, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000)).
% 79,283 inferences, 0.031 CPU in 0.027 seconds (116% CPU, 2541106 Lips)
N = 100. % No choice point
?- time(factorial(N, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518284253697920827223758251185210916864000000000000000000000000)).
% 78,907 inferences, 0.031 CPU in 0.025 seconds (125% CPU, 2529054 Lips)
false.
?- F #> 10^100, factorial(N, F).
F = 11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000,
N = 70 ;
F = 850478588567862317521167644239926010288584608120796235886430763388588680378079017697280000000000000000,
N = 71 ;
F = 61234458376886086861524070385274672740778091784697328983823014963978384987221689274204160000000000000000,
N = 72 ;
...
a simple 'low tech' way: enumerate integers until
- you find the sought factorial, then 'get back' the number
- the factorial being built is greater than the target. Then you can fail...
Practically, you can just add 2 arguments to your existing factorial implementation, the target and the found inverse.
Just implement factorial(X, XFact) and then swap arguments
factorial(X, XFact) :- f(X, 1, 1, XFact).
f(N, N, F, F) :- !.
f(N, N0, F0, F) :- succ(N0, N1), F1 is F0 * N1, f(N, N1, F1, F).
来源:https://stackoverflow.com/questions/19025664/inverse-factorial-in-prolog