bash脚本查找permeations的总数

时间:2016-08-26 06:02:27

标签: linux bash shell permutation

我正在写一个bash脚本来查找总数。排列(用于数学) 其公式为 n!/(n-r)!,但我使用的脚本给出的输出值与预期值不同。有谁弄清楚我的错误?我是bash脚本中的新手。

echo "Enter no. to find factorial"
read num 
fact=1
while [ $num -gt 0 ]
do
fact=`expr $num \* $fact`
num=`expr $num - 1`
done

echo "Enter value for r"
read num1
num2=$((num-num1))

fact1=1
while [ $num2 -gt 0 ]
 do
fact1=`expr $num2 \* $fact1`
num2=`expr $num2 - 1`
done

echo "Total no. of permutations $((fact/fact1)) "

3 个答案:

答案 0 :(得分:2)

没有必要调用另一个程序(expr),因为Bash完全能够计算整数算术(请参阅man bash中的ARITHMETIC EVALUATION)。以下代码段可能会对您有所帮助:

fact=1     
for ((i=4; i>1; i--)) do
  ((fact*=i))
done
echo $fact

#echo $((4+4))  # Arithmetic Expansion might also be useful

<强>输出

24

答案 1 :(得分:1)

我认为OP&#39代码和@bgoldst解决方案可以改进,因为当 n r 很大时,这两种方法都会失败因为算法的关键是使用阶乘,正如你已经知道的那样,这会产生bash无法处理的巨大整数。 bash算术仅限于32位有符号整数。

例如,让我们说 n 是49而 r 是2,@ bgoldst的脚本显示-6 - 正确的值是2352(49 * 48)。当 n 为32且 r 为2时,输出为0 - 正确的值为992(32 * 31)。

@bgoldst改进了OP的代码,但他没有为算法添加任何智能 - 你忘了我们是程序员吗?

数学背景

表达式 n!/(n-r)! 可以通过这种方式重写

  n!      n·(n-1)·(n-2)···(n-r+1) · (n-r)!
------ = --------------------------------- = n·(n-1)·(n-2)···(n-r+1)
(n-r)!              (n-r)!

注意 r 值实际上是最终乘法中的因子数。

例如,当 n=49 r=2

   49!       49!      49·48·47!
--------- = ----- = ------------- = 49·48 = 2352
 (49-2)!     47!         47!

我的解决方案

我的解决方案使用 bc 工具绕过32位定点运算的约束,并使用 seq 工具删除任何shell循环。

如果 n r 值形成良好的正整数:

# 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}"

注释:

  • bc <<<$n'>='$r 2>/dev/null打印0或1,无论 n r 有多大。如果 n r 不是格式良好的整数值,bc命令将声明,报告错误消息,什么都不打印(空字符串)。请注意,如果我的代码必须允许bash无法正确处理的大整数,那么它就不能包含bash通常的算术比较,例如[ "$n" -ge "$r" ],{{1 }或[[ "$n" >= "$r" ]]。这就是为什么(( n >= r ))也在这里使用的原因。

  • 例如,
  • bc会产生seq -s '*',它通过4*5*6*7命令传送。

  • bc 为零时,无论 r 是什么,n命令行都不会产生任何结果(空)。因此,seq是提供默认值(1)所必需的。

  • PERMUTATIONS="${PERMUTATIONS:-1}"命令(以及bc)在几行中打印大量数字(请阅读手册页);因此,我的代码将打印,因为它

答案 2 :(得分:0)

你的主要问题是你在第一个阶乘循环中破坏了$num的值,但是你试着在循环后再次使用它。在所述循环之后,$num的值始终为零,因此$((num-num1))将最终为负数,并且条件$num2 -gt 0在第一次评估时始终为false。您可以通过复制变量并销毁副本来解决此问题。

其他建议:

  • 当您不需要插值时,请使用单引号字符串而不是双引号,因为单引号字符串完全不允许插值。这样更安全。人们一直在偶然的双引号内插中被烧毁。这是一个很好的例子:Assignment issue with Rscript -e calls
  • 最好使用$[...]代替$((...))进行算术评估,因为您可以保存两个字符。
  • 永远不要使用`expr ...`进行算术评估,因为这需要一个fork并使解析复杂化(例如,你必须使用反斜杠 - 转义星号,这是不可取的)。 (当我明显知道算术表达式的$((...))构造时,我不确定为什么你有时会选择那个成语。)我建议你一直使用$[...]进行所有算术评估。 / LI>
  • 最好将[[ ... ]]用于条件,因为它比[ ... ]更强大,并且由于是一个shell关键字而具有更清晰的语法,而单一 - 括号命令仅仅是内置的。 (如果您不相信我,请尝试运行type [ [[。)即使您不需要双括号命令的更高级功能,您仍然应该使用它,以确保所有功能的一致性代码,再次,更清晰的语法。
  • 由于乘以1无效,您可以将-gt 0测试更改为-gt 1
#!/bin/bash

## get n
echo 'Enter no. to find factorial';
read n;

## compute n!
nFact=1;
temp1=$n;
while [[ $temp1 -gt 1 ]]; do
    nFact=$[temp1*nFact];
    temp1=$[temp1-1];
done;

## get r
echo 'Enter value for r';
read r;

## compute (n-r)!
nrFact=1;
temp1=$[n-r];
while [[ $temp1 -gt 1 ]]; do
    nrFact=$[temp1*nrFact];
    temp1=$[temp1-1];
done;

echo "Total no. of permutations $[nFact/nrFact]";