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.