spm_nCr.m 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. function c = spm_nCr(n,r)
  2. % Combinatorics: n choose r, nCr
  3. % FORMAT c = spm_nCr(n,r)
  4. %
  5. % n - Number of objects
  6. % r - Number of objects to choose
  7. % c - n choose r
  8. %__________________________________________________________________________
  9. %
  10. % spm_nCr returns the number of ways of choosing r objects from a pool
  11. % of n objects, without replacement, order unimportant. Equivalently:
  12. % the number of ways of putting r objects into n indistinguishable urns
  13. % with exclusion. These are the Binomial coefficients of Pascal's
  14. % triangle:
  15. % ( n ) n!
  16. % | | = ---------
  17. % ( r ) r! (n-r)!
  18. %
  19. % n & r must be whole numbers, with n>=r. Non-integer or out-of-range
  20. % arguments return zero as nCr. Non-scalar n & r must have the same
  21. % dimensons.
  22. %
  23. % Algorithm:
  24. %--------------------------------------------------------------------------
  25. % For vary small n, nCr can be computed naively as the ratio of
  26. % factorials, using gamma(x+1) to return x!. For moderately sized n, n!
  27. % (& r! &/or (n-r)!) become very large, and naive computation isn't
  28. % possible. Direct computation is still possible upon noting that the
  29. % expression cancels down to the product of r fractions:
  30. %
  31. % n! n n-1 n-(r-1)
  32. % --------- = - x --- x ... x -------
  33. % n! (n-r)! r r-1 1
  34. %
  35. % Unfortunately this cunning computation (given at the end of this
  36. % function) is difficult to vectorise. Therefore we compute the log of
  37. % nCr as (ln(n!)-ln(r!)-ln((n-r)!), using the log-gamma special
  38. % function gammaln:
  39. %
  40. % nCr = exp( gammaln(n+1) - gammaln(r+1) - gammaln(n-r+1) )
  41. %
  42. % The result is rounded to cope with roundoff error for smaller values
  43. % of n & r. See Press et al., Sec6.1 for further details.
  44. %
  45. % References
  46. %--------------------------------------------------------------------------
  47. % Evans M, Hastings N, Peacock B (1993)
  48. % "Statistical Distributions"
  49. % 2nd Ed. Wiley, New York
  50. %
  51. % Abramowitz M, Stegun IA, (1964)
  52. % "Handbook of Mathematical Functions"
  53. % US Government Printing Office
  54. %
  55. % Press WH, Teukolsky SA, Vetterling AT, Flannery BP (1992)
  56. % "Numerical Recipes in C"
  57. % Cambridge
  58. %
  59. %__________________________________________________________________________
  60. % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging
  61. % Andrew Holmes
  62. % $Id: spm_nCr.m 5219 2013-01-29 17:07:07Z spm $
  63. %-Format arguments, note & check sizes
  64. %--------------------------------------------------------------------------
  65. ad = [ndims(n);ndims(r)];
  66. dc = max(ad);
  67. as = [ [size(n),ones(1,dc-ad(1))];...
  68. [size(r),ones(1,dc-ad(2))] ];
  69. sc = max(as);
  70. xa = prod(as,2)>1;
  71. if sum(xa)==2 && any(diff(as)), error('non-scalar args must match in size'), end
  72. %-Computation
  73. %--------------------------------------------------------------------------
  74. %-Initialise result to zeros
  75. c = zeros(sc);
  76. %-Non zero only where n & r are whole and 0<=r<=n
  77. Q = find( n==floor(n) & r==floor(r) & n>=r & r>=0 );
  78. if isempty(Q), return, end
  79. if xa(1), Qn=Q; else Qn=1; end
  80. if xa(2), Qr=Q; else Qr=1; end
  81. %-Compute
  82. c(Q) = round(exp(gammaln(n(Qn)+1) -gammaln(r(Qr)+1) - gammaln(n(Qn)-r(Qr)+1)));
  83. %-Return
  84. %--------------------------------------------------------------------------
  85. return
  86. %==========================================================================
  87. %-Direct computation method: (For interest)
  88. %==========================================================================
  89. % The following cunning direct computation is faster than using log
  90. % gammas, but is rather difficult to vectorise.
  91. %if r<n/2
  92. % %-better for small r (less terms), append 1's for 0!
  93. % c = prod([n:-1:n-r+1,1]./[r:-1:1,1]);
  94. %else
  95. % %-better for large r (less terms), append 1's for 0!
  96. % c = prod([n:-1:r+1,1]./[n-r:-1:1,1]);
  97. %end