# HG changeset patch # User Francesco Potortì # Date 1229133593 -3600 # Node ID 1235e389bbd99059b370ddbfbc4e1c6e24268f34 # Parent 87cca636a6c61e0cc8c3ab88a2652782203aff26 nchoosek checks, warning, corner cases and tests diff -r 87cca636a6c6 -r 1235e389bbd9 scripts/ChangeLog --- a/scripts/ChangeLog Fri Dec 12 23:25:54 2008 +0100 +++ b/scripts/ChangeLog Sat Dec 13 02:59:53 2008 +0100 @@ -1,3 +1,8 @@ 2008-12-11 Jaroslav Hajek + + * specfun/nchoosek.m: Check for input arguments, signal loss of + precision, correctly handle k==0 and k==n cases, add proper tests. + 2008-12-11 Jaroslav Hajek * optimization/fsolve.m: Optionally allow pivoted qr factorization. diff -r 87cca636a6c6 -r 1235e389bbd9 scripts/specfun/nchoosek.m --- a/scripts/specfun/nchoosek.m Fri Dec 12 23:25:54 2008 +0100 +++ b/scripts/specfun/nchoosek.m Sat Dec 13 02:59:53 2008 +0100 @@ -49,31 +49,44 @@ ## of @var{n}, taken @var{k} at a time, one row per combination. The ## resulting @var{c} has size @code{[nchoosek (length (@var{n}), ## @var{k}), @var{k}]}. +## +## @code{nchoosek} works only for nonnegative integer arguments; use +## @code{bincoeff} for non-integer scalar arguments and for using vector +## arguments to compute many coefficients at once. ## ## @seealso{bincoeff} ## @end deftypefn ## Author: Rolf Fabian ## Author: Paul Kienzle - -## FIXME -- This function is identical to bincoeff for scalar -## values, and so should probably be combined with bincoeff. +## Author: Jaroslav Hajek function A = nchoosek (v, k) - if (nargin != 2) + if (nargin != 2 + || !isnumeric(k) || !isnumeric(v) + || !isscalar(k) || (!isscalar(v) && !isvector(v))) print_usage (); + endif + if ((isscalar(v) && v < k) || k < 0 + || k != round(k) || any (v < 0 || v != round(v))) + error ("nchoosek: args are nonnegative integers with V not less than K"); endif n = length (v); if (n == 1) - if (k > v/2) - k = v - k; + k = min (k, v-k); # improve precision at next step + A = round (prod ((v-k+1:v)./(1:k))); + if (A*2*k*eps >= 0.5) + warning ("nchoosek", "nchoosek: possible loss of precision"); endif - A = round (prod ((v-k+1:v)./(1:k))); + elseif (k == 0) + A = []; elseif (k == 1) A = v(:); + elseif (k == n) + A = v(:).'; elseif (k > n) A = zeros (0, k, class (v)); else @@ -110,6 +123,8 @@ function A = nchoosek (v, k) endif endfunction -# nchoosek seems to be slightly more accurate (but only allowing scalar inputs) -%!assert (nchoosek(100,45), bincoeff(100,45), -1e2*eps) +%!warning (nchoosek(100,45)); +%!error (nchoosek(100,45.5)); +%!error (nchoosek(100,145)); +%!assert (nchoosek(80,10), bincoeff(80,10)) %!assert (nchoosek(1:5,3),[1:3;1,2,4;1,2,5;1,3,4;1,3,5;1,4,5;2:4;2,3,5;2,4,5;3:5])