2
bc(0,N,R,R):- N>0,R is 1,!.
bc(M,0,R,R):- M>0,R is 1,!.
bc(N,N,R,R):- R is 1,!.
bc(M,N,R,R1):- 
    M1 is M - 1, 
    N1 is N - 1, 
    bc(M1,N1,R,R2),
    bc(M1,N,R,R3),
    R1 is R2+R3.

Why does it return false?

Task is: Write recursive procedure C(M,N) (0 ≤ m ≤ n ) to find out binomial coefficient using this formule:

C(m,0)=C(0,n)=C(n,n)=1
C(m,n)=C(m−1,n−1)+C(m−1,n)

P.S.: I use online-compiler SWISH.

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
rottor171
  • 63
  • 4
  • 1
    Why do you need 4 arguments? The 4th seems to be superfluous and is probably causing you problems. Just get rid of it. Your `bc(N, N, ...)` case should have the condition `N > 0` so that it doesn't overlap in truth value with the other two base cases. And `bc(M, N, ...)` should have the condition that `M > N` since that is what that case is for. If you set your conditions correctly, you don't need the cuts. – lurker Nov 10 '17 at 03:00
  • 2
    Can you explain in your own words why you think the above attempt should produce the correct result? What is the query you run? – Willem Van Onsem Nov 10 '17 at 07:11
  • bc(2,4,RES,0), from there: M=2,N=4,Result,Each Result. – rottor171 Nov 10 '17 at 08:48
  • I need to count ONE binomial coef with params of M and N – rottor171 Nov 10 '17 at 08:49

1 Answers1

4

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
| ?- ;
lurker
  • 56,987
  • 9
  • 69
  • 103
  • 1
    You have two typos in your code: the goals `N>0` and `M>N`. With these your example query `?- bc(4,X,6).` leads to an instantiation error. You probably meant `N#>0` and `M#>N`. Furthermore if you add a goal `R #>= M` before the recursive goals, queries like `?- bc(X,2,15).` terminate. If you look at Pascal's triangle the coefficient is either `1` (covered by your facts) or `R`, then the last line it appears in is the one associated with `R` choose `N` at the second and the next-to-last positions. ;-) – tas Nov 11 '17 at 09:05
  • 1
    @tas thank you! I didn't update my post with my latest "fixes", so thanks for catching them. I had run my example on later code than I showed. – lurker Nov 11 '17 at 13:29
  • I feel you, since I'm prone to such mishaps myself :-) – tas Nov 11 '17 at 13:46