47

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

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

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

因为next我可以只取范围的长度在(i+m)%n哪里(在示例中)。但是因为我找不到一个优雅的解决方案。nn=10prev

4

7 回答 7

63

只需减去 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; }
于 2010-09-27T11:39:16.157 回答
16

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

试试这个:

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

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

对于prevn首先添加以确保您永远不会取负数的模

于 2010-09-27T11:38:26.547 回答
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
于 2010-09-27T12:15:57.843 回答
2

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

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

正如您正确指定的那样,给定一个 range [x..z],其中 range 是圆形的,您可以找到下一个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() 函数中都需要一个处理机制来处理这个结果。

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

于 2010-09-27T12:38:33.277 回答
1

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

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

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

于 2010-09-27T15:28:32.127 回答
1

我在 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
于 2019-05-24T14:45:05.787 回答
0

假设您想从 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。

于 2021-03-29T20:17:24.877 回答