当我的数字从 1 开始而不是 0 时,我如何取模?

Posted

技术标签:

【中文标题】当我的数字从 1 开始而不是 0 时,我如何取模?【英文标题】:How can I modulo when my numbers start from 1, not zero? 【发布时间】:2011-04-17 17:29:56 【问题描述】:

我想解决这个问题的方法很简单,但我已经考虑了一段时间,无法想出一个优雅的解决方案。

我有一系列数字,例如1..10 = (1,2,3,4,5,6,7,8,9,10),是循环的,表示最后一个之后的数字又是第一个(next(10)=1)。

对于范围内的给定数字i>0,我想计算下一个m-th 和前一个m-th 数字。例如next(5,1)=6next(10,1)=1next(10,2)=2prev(5,2)=3prev(1,1)=10prev(1,2)=9.

对于next,我可以取(i+m)%n,其中n 是范围的长度(示例中为n=10)。但是对于prev,我找不到优雅的解决方案。

【问题讨论】:

这不是 Perl 特有的。我建议寻找更好的标签。 标签从perl更改为modulo,基于问题的实际内容。 【参考方案1】:

只需减去 1,然后再添加 1。

在大多数编程语言中,在查找“先前”值时需要小心,因为对于负数,模在这种情况下无法按您的意愿工作:它返回一个负数。

这是 C/C++ 版本:

int next(int i, int m, int n)  return (i + m - 1) % n + 1; 
int prev(int i, int m, int n)  return (i - m + n - 1) % n + 1; 

然而,在 Perl 中,取模总是返回一个正值(至少当第二个操作数是一个正整数时)。基本上它会做你想要的。因此,您可以编写以下内容并省略+ $_[2]

sub nxt  ($_[0] + $_[1] - 1) % $_[2] + 1; 
sub prv  ($_[0] - $_[1] - 1) % $_[2] + 1; 

【讨论】:

如果数字是非负数,并且没有数值溢出的危险,我更喜欢加 (base-1) 而不是减一。 从数学角度很好地处理模“运算符”的不同实现:mathforum.org/library/drmath/view/52343.html。实际上,C/C++ 中并没有为负参数定义 % 运算符,但大多数实现都遵循 IEEE 754 标准,这与 Ada 的 REM 运算符相同。 Perl 的 % 实现与 Ada 的 MOD 运算符相同的东西。 @gpvos:注意未定义行为和实现定义行为之间的区别。 % C++03 中的负数是后者。 不错@gpvos。我使用您的 C 示例在 javascript 的搜索结果中循环遍历 hitshitnext 连接到 cycle(1)prevcycle(-1),其中 cyclecycle (direction) this.hit = (direction === -1 ? this.hit + direction + this.hits - 1 : this.hit + direction - 1) % this.hits + 1 【参考方案2】:

您的next = (i + m) % n 无论如何都不正确 - 在某些情况下它会返回零。

试试这个:

next(i, m) = ((i - 1) + m) % n + 1
prev(i, m) = ((i - 1) + n - m) % n + 1

实际上,取下一个,然后找到正确的值,然后重新添加一个。

对于prev,请先添加n,以确保您永远不会取负数的模

【讨论】:

我真的很喜欢这个答案(+1)。而“取下一个,找到正确的值,然后再添加一个”的描述使一个衬垫超级直观,同时又美观简洁。【参考方案3】:

next(i,m)previous(i,-m) 有什么区别?没有!。所以我们去(i - 1 + n + m % n) % n + 1

$ perl -le 'sub gen my $n = shift; return sub my ($i, $m) = @_; return ($i - 1 + $n + $m % $n) % $n + 1;; $"=","; for my $n (2..5)  my $f = gen($n); print "$n: @[map $f->(1,$_) -10 .. 10]"'
2: 1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1
3: 3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2
4: 3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3
5: 1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1,2,3,4,5,1

【讨论】:

有趣:perl 取模不同于 C 取模。 #include void main() for (int i = -10; i 【参考方案4】:

如果你不介意的话,先说几句话。

您在实现“prev”函数时的困惑来自于在正整数和负整数域中思考这个问题。从几何的角度考虑,如果你想象一个有 10 个等距点的圆,那么解决方案看起来像这样:

正如您正确指定的那样,给定一个范围 [x..z],其中范围是循环的,您可以找到下一个 m-th number 作为 (i+m)%k where i belongs to [x..z]k 是范围的长度。

现在,对于“以前的”第 m 个成员。 之前的数字可以通过计算(或者更直观地表达,“到达”)之前的第 m 个数字位置来找到,如下所示(伪代码):

prev(m, i) = (i + len(range) - m) % len(range)

例如,如果你取数字 10 的前一个,那么

prev(1,10) = (10+10-1)%10 = 19%10 = 9

前 3 号为 5 = prev(3,5) = (5+10-3)%10 = 12%10 = 2 。 等等等等。 非常简单,优雅,对吧?

这里唯一需要注意的是 if i == m ,模数为零,因此您需要在 next() 和 prev() 函数中处理此结果。

希望这会有所帮助, 贾斯。

【讨论】:

【参考方案5】:

您可能会查看Tie::Cycle 的源代码,这是我创建的用于循环任意列表的模块。

请记住,数字实际上只是代表某些东西的字形。如果您有这些字形的 Perl 列表,您仍然有一个从零开始的序列,因为您在列表索引而不是字形上进行数学运算。选择正确的列表索引后,您将使用该索引处的元素。

如果你想要非常大的列表或惰性列表,你仍然可以这样做,但你只需要做更多的工作。

【讨论】:

【参考方案6】:

我在 R 中有这个解决方案:

pred <- function(n) n - 1L # cf. Pascal's pred
succ <- function(n) n + 1L # cf. Pascal's succ
`%mod1%` <- function(m, n) succ(pred(m) %% n) # modulo from 1
cat(-11:24 %mod1% 12) # test
# 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11 12

【讨论】:

【参考方案7】:

假设您想从 1 映射到 n 而不是 0 到 n-1 例如 n=5,范围 1 到 x,结果 0 到 4,0mod5=0 1mod5=1, 2mod5=2...xmod5 结果为 0 x=5*k。使用 ((x-1)mod5)+1,x 必须 >0。这将始终映射(计数)在 1 到 5 的范围内,而不是 0 到 4。

【讨论】:

欢迎堆栈溢出。感谢您的贡献。请让您的答案更具可读性,以便每个人都可以享受!

以上是关于当我的数字从 1 开始而不是 0 时,我如何取模?的主要内容,如果未能解决你的问题,请参考以下文章

如何让 Quarkus 监听所有网络接口而不是 localhost?

从 Web API 获取单个数字,而不是字典

YAML 将 5e-6 加载为字符串而不是数字

如何防止写入 0 而不是未输入的数组元素?

我的问题是当我按下清除按钮时,它只是清除文本字段而不是之前按下的数字的数据

如何仅在 ES2015 中生成从 0 到 n 的数字范围?