I think OP's code and @bgoldst solution can be improved because both will fail when n
and r
are big due to that the key of the algorithm is the use of factorial, and this, as you already should know, produces huge integer numbers which bash
cannot handle. bash
arithmetic is limited to 32-bit signed integers.
For instance, let's say n
is 49 and r
is 2, the @bgoldst's script shows -6 -- the right value is 2352 (49*48). When n
is 32 and r
is 2, the output is 0 -- the right value is 992 (32*31).
@bgoldst improves the OP's code but he adds no intelligence to the algorithm -- Have you forgotten we are programmers?
Mathematical background
The expression n!/(n-r)!
can be re-written this way
n! n·(n-1)·(n-2)···(n-r+1) · (n-r)!
------ = --------------------------------- = n·(n-1)·(n-2)···(n-r+1)
(n-r)! (n-r)!
Pay attention to that the r
value is actually the number of factors in the ultimate multiplication.
For example, when n=49
and r=2
49! 49! 49·48·47!
--------- = ----- = ------------- = 49·48 = 2352
(49-2)! 47! 47!
My solution
My solution uses the bc
tool in order to bypass the constrains of 32-bit fixed-point arithmetic and seq
tool to remove any shell loop.
Provided that the n
and r
values are well formed positive integers:
# Check if n>=r
if [ "$( bc <<<$n'>='$r 2>/dev/null)" != 1 ]
then
echo "r cannot be bigger than n"
exit 1
fi
PERMUTATIONS=$( seq -s '*' $((n-r+1)) "$n" | bc )
echo "Total no. of permutations for n=$n and r=$r is ${PERMUTATIONS:-1}"
notes:
bc <<<$n'>='$r 2>/dev/null
prints either 0 or 1, no matter how big n
and r
are. In the case when n
or r
are not well formed integer values, bc
command will claim, report an error message and print nothing (NULL string). Pay attention to the fact that, if my code has to admit huge integers which cannot be properly handled by bash
, then it cannot contain bash
usual arithmetic comparisons such as [ "$n" -ge "$r" ]
, [[ "$n" >= "$r" ]]
or (( n >= r ))
. That is why bc
is used here too.
seq -s '*'
would produce, for instance, 4*5*6*7
which is piped to the bc
command.
When r
is zero, no matter n
is, the seq
command line produces nothing (empty). Therefore, PERMUTATIONS="${PERMUTATIONS:-1}"
is necessary to provide a default value (1).
The bc
command (as well as dc
) prints the huge numbers in several lines (please read the manual pages); consequently my code will print it as it is.