8

当在 bash 中天真地使用 mod 命令时,对于负分子,残差会得到错误的符号(在我看来):

如果我写:

for i in {-5..5}; do echo $(( $i % 3 )) ; done

我得到输出(作为一行)

-2 -1 0 -2 -1 0 1 2 0 1 2

我如何实现“正确”的行为

1 2 0 1 2 0 1 2 0 1 2
4

3 回答 3

6

我知道这是一个老问题,但不要循环直到结果为正或启动 perl 或 python 考虑以下内容:

for i in {-5..5}; do echo $(( (($i % 3) + 3) % 3)) ; done

这将产生 OP 所需的输出。

这是有效的,因为第一个模数会将结果带入 -3 到 3 的范围内,加 3 会导致结果在 0 到 6 的范围内,然后我们可以再次执行模数(加 3 对此没有影响)。

一般来说:mod = ((a % b) + b) % b

于 2018-08-23T19:43:15.227 回答
3

Add 3然后Mod 3到第一组结果:

$ for i in {-5..5}; do printf "%d " $(( (($i % 3) + 3) % 3 )) ; done
1 2 0 1 2 0 1 2 0 1 2

如果您知道最大范围,则可以在第一次模运算之前添加一个足够大的 3 倍数,以使所有数字为正数。

$ for i in {-5..5}; do printf "%d " $(( ($i + 3000000) % 3 )) ; done

但是,第一种方法更清洁、更通用。

最后,为了好玩:

positive_mod() {
  local dividend=$1
  local divisor=$2
  printf "%d" $(( (($dividend % $divisor) + $divisor) % $divisor ))
}

for i in {-5..5}; do
  printf "%d " $(positive_mod $i 3)
done
于 2017-01-20T14:48:17.363 回答
3

根据维基百科,负号是允许的。

[ 的结果a mod n] 如果余数非零,这仍然会留下符号歧义:余数有两种可能的选择,一种是负数,另一种是正数,商数有两种可能的选择。通常,在数论中,总是选择正余数,但编程语言根据语言和 a 或 n 的符号进行选择。

所以由编程语言来定义它。由于 bash 显然采用了“负余数”方式,因此您可能会像这样逃到 perl 中:

for i in {-5..5}; do perl -le "print $i%3"; done

这是以为每个整数单独启动 Perl 解释器为代价的。

的确!由于 OP 似乎关心正确的数学,您可能会考虑切换到类似的东西python并执行循环和其中的所有内容。

于 2017-01-20T14:54:44.097 回答