是否有使用modulo进行向后环绕的表达式("反向溢出")?

aya*_*ane 19 c++ algebra modulo

对于由范围R = [ x,y ] 限制的任何整数输入W,由于缺少更好的项,"溢出"是WR的.如果W超过y,这会导致它回绕.W % (y-x+1) + x

作为这个原则的一个例子,假设我们迭代一个日历的月份:

int this_month = 5;
int next_month = (this_month + 1) % 12;
Run Code Online (Sandbox Code Playgroud)

其中两个整数都在0到11之间(包括0和11).因此,上面的表达式将整数"钳制"到范围R = [0,11].这种使用表达式的方法简单,优雅且有利,因为它省略了分支.

现在,如果我们想要做同样的事情,但倒退呢?以下表达式有效:

int last_month = ((this_month - 1) % 12 + 12) % 12;
Run Code Online (Sandbox Code Playgroud)

但这是深奥的.它怎么能被美化?


tl; dr - 可以((x-1) % k + k) % k进一步简化表达吗?

注意:指定C++标记,因为其他语言以不同方式处理模运算符的负操作数.

Fab*_*ian 21

你的表达应该是((x-1) + k) % k.这将正确地将x = 0包裹到11左右.通常,如果要退回多于1,则需要确保添加足够的值以使模运算的第一个操作数> = 0.

这是Python中的一个实现.您应该能够将其转换为您选择的语言:

int wrapAround(int v, int delta, int minval, int maxval)
{
  const int mod = maxval + 1 - minval;
  if (delta >= 0) {return  (v + delta                - minval) % mod + minval;}
  else            {return ((v + delta) - delta * mod - minval) % mod + minval;}
}
Run Code Online (Sandbox Code Playgroud)

这也允许使用标为0至11或1至12个月,设置min_valmax_val相应.

  • `((x-1)+ k)%k`是解决方案! (7认同)

Enf*_*yve 8

k%k将始终为0.我不是100%确定你要做什么,但似乎你希望上个月被夹在0到11之间(包括0和11).

(this_month + 11) % 12
Run Code Online (Sandbox Code Playgroud)

应该就够了.

  • 实际上,`-1%12 == -1`. (3认同)
  • @ Night5h4d3:在C++ 03中,如果任一操作数为负数,则不指定符号.在C++ 11中,结果的符号与第一个操作数的符号相同([请参阅我的答案](http://stackoverflow.com/a/13100805/485561)). (2认同)

Man*_*rse 5

通用解决方案是编写一个计算所需值的函数:

//Returns floor(a/n) (with the division done exactly).
//Let ÷ be mathematical division, and / be C++ division.
//We know
//    a÷b = a/b + f (f is the remainder, not all 
//                   divisions have exact Integral results)
//and
//    (a/b)*b + a%b == a (from the standard).
//Together, these imply (through algebraic manipulation):
//    sign(f) == sign(a%b)*sign(b)
//We want the remainder (f) to always be >=0 (by definition of flooredDivision),
//so when sign(f) < 0, we subtract 1 from a/n to make f > 0.
template<typename Integral>
Integral flooredDivision(Integral a, Integral n) {
    Integral q(a/n);
    if ((a%n < 0 && n > 0) || (a%n > 0 && n < 0)) --q;
    return q;
}

//flooredModulo: Modulo function for use in the construction
//looping topologies. The result will always be between 0 and the
//denominator, and will loop in a natural fashion (rather than swapping
//the looping direction over the zero point (as in C++11),
//or being unspecified (as in earlier C++)).
//Returns x such that:
//
//Real a = Real(numerator)
//Real n = Real(denominator)
//Real r = a - n*floor(n/d)
//x = Integral(r)
template<typename Integral>
Integral flooredModulo(Integral a, Integral n) {
    return a - n * flooredDivision(a, n);
}
Run Code Online (Sandbox Code Playgroud)