使用模数运算符保持在容器的索引内

Using modulus operator to keep within indices of container

本文关键字:索引 运算符      更新时间:2023-10-16

假设我有一个包含m个元素的向量v,以及一个随机访问索引I。

当我增加索引时,如果它超出边界,我想索引第一个(第零)元素。类似地,当我递减索引时,如果索引是<0,我要索引到最后一个元素。目前,我每次只移动一个元素,所以想到了这个函数:

unsigned int GetIndexModM(int index,unsigned int m) {return (index + m) % m;}

调用站点可能看起来像这样:

std::vector<Whatever> v = ... // initialise with 5 elements
unsigned int i = 0;
unsigned int j = GetIndexModM(static_cast<int>(i) - 1,v.size()); // get preceeding index

如果从索引中减去值> m,该函数将失败:

unsigned int j = GetIndexModM(static_cast<int>(i) - 17,v.size()); // oops: returns -2

我的问题:什么是最优雅的实现一个函数,接受任何整数,并返回它的位置作为索引?

处理MOD的技巧是这样的,它适用于正数和负数:

  val = ((val % mod_val) + mod_val) % mod_val; 

例如,假设我们希望保持值在0到359之间。我们可以这样写:

  val = ((val % 360) + 360) % 360; 

下面是c++中的一个简单示例:

int getmod(int val, int mod) {
  return ((val % mod) + mod) % mod; 
}
int main() {
  printf("%dn", getmod(50,360));   // prints 50
  printf("%dn", getmod(-400,360)); // prints 320
  printf("%dn", getmod(350,360));  // prints 350
  printf("%dn", getmod(375,360));  // prints 15
  printf("%dn", getmod(-725,360));  // prints 355

  return 0;
}

遗憾的是,c++没有实现对负整数仍然正确工作的适当模数。

我认为最干净的解决方案确实是使用if来妥善处理所有情况。这至少使代码明显(因为每个case都是显式),并且更容易找到错误:

unsigned GetIndexModM(int index, unsigned m) {
    if (index < 0)
        return GetIndexModM(index + m, m);
    if (index >= m)
        return index % m;
    return index;
}

确保index在[0,n)中,但只有一个模数运算且没有分支:

index = index % n + (index < 0)*n

其中第一项(包含模数运算符)使值为(-n, n),第二项确保值为[0,n)。

请注意,当n是无符号类型时,以及在旧版本(11之前)的c++中,%操作符依赖于负参数的实现时,这是不可靠的。