我正在写一个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)) "
答案 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。您可以通过复制变量并销毁副本来解决此问题。
其他建议:
$[...]
代替$((...))
进行算术评估,因为您可以保存两个字符。`expr ...`
进行算术评估,因为这需要一个fork并使解析复杂化(例如,你必须使用反斜杠 - 转义星号,这是不可取的)。 (当我明显知道算术表达式的$((...))
构造时,我不确定为什么你有时会选择那个成语。)我建议你一直使用$[...]
进行所有算术评估。 / LI>
[[ ... ]]
用于条件,因为它比[ ... ]
更强大,并且由于是一个shell关键字而具有更清晰的语法,而单一 - 括号命令仅仅是内置的。 (如果您不相信我,请尝试运行type [ [[
。)即使您不需要双括号命令的更高级功能,您仍然应该使用它,以确保所有功能的一致性代码,再次,更清晰的语法。-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]";