First, let's get the formula for binomial coefficients stated correctly. The conventional definition of c(M, N)
would have M >= N >= 0
, not N >= M >= 0
as stated. This is important because the formula given:
C(m,0)=C(0,n)=C(n,n)=1 C(m,n)=C(m−1,n−1)+C(m−1,n)
assumes that 0 ≤ n ≤ m
, not what's been stated (0 ≤ m ≤ n
). So it's incorrect (either it was given incorrectly, or you may have written it down incorrectly). Also, C(0, n)
is not a valid coefficient unless n = 0
. So this case is not meaningful and can be omitted. It's already covered by C(m, 0)
.
The incorrect formulation of the problem could be the main reason you are getting "false" in your results.
C(n, 0) = C(n, n) = 1
would be covered by the following base (non-recursive) cases:
bc(N, 0, 1) :- N #>= 0.
bc(N, N, 1) :- N #> 0. % The N = 0 case is already covered in the first base case
Then the more general case would be handled recursively per the given formula:
bc(M, N, R) :-
N #> 0, % The N = 0 case is already covered in the first base case
M #> N, % The M = N case is already covered in the second base case
R #>= M, % This constraint prevents unbounded search in non-solution space
M1 #= M - 1, % The rest of this is just the given formula
N1 #= N - 1,
bc(M1, N1, R1),
bc(M1, N, R2),
R #= R1 + R2.
A few principles when writing Prolog predicates:
Make sure your problem is stated accurately and correctly. This sounds obvious, but was an issue in this case.
Use CLP(FD) for reasoning with integers. It is more relational which is what Prolog is about. Using is/2
is more imperative. For example, X is Y * 2
will generate an instantiation error if Y
is unbound. But X #= Y * 2
will generate potential solutions. If you must use is/2
due to an arbitrary requirement, you can substitute it back in.
Apply constraints in your predicate clauses that make them mutually exclusive. That is, unless it's what you want, don't allow your predicate to succeed more than one way for a given solution. In particular, make your different clauses non-overlapping. For example, if you have a predicate where the first argument is a natural number (non-negative integer), and you have a base case of foo(0, 1).
, then in your recursive case foo(N, R) :-
you'd want a constraint N > 0
or N #> 0
, assuming there are no other side effects or other arguments that might be needed and/or different in the recursive case.
The more you can constrain or bound the variables within the solution space you want, the less your code will wander off trying options that are not valid solutions and, in the worst case, not terminate due to lack of bound on such non-solution options. For example, in this problem, we added the constraint R #>= M
which is true when N > 0
and N < M
. Without this constraint, Prolog will explore cases where R < M
which are boundless and result in non-termination in some cases.
Running the query:
| ?- bc(3,2,R).
R = 3 ? ;
no
| ?- bc(4,2,R).
R = 6 ? ;
no
| ?-
Etc...
By using the CLP(FD) operators, you can also run queries like this:
| ?- bc(4,X,6).
X = 2 ? ;
no
| ?-
And...
| ?- bc(N, K, 6).
N = 6
K = 1 ? ;
N = 4
K = 2 ? ;
N = 6
K = 5 ? ;
no
| ?- ;