Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/146.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 是否有使用模进行反向环绕(“反向溢出”)的表达式?_C++_Modulo_Algebra - Fatal编程技术网

C++ 是否有使用模进行反向环绕(“反向溢出”)的表达式?

C++ 是否有使用模进行反向环绕(“反向溢出”)的表达式?,c++,modulo,algebra,C++,Modulo,Algebra,对于受范围R=[x,y]限制的任何整数输入W,由于缺少更好的术语,W对R的“溢出”为W%(y-x+1)+x。如果W超过y,则会使其回绕 作为此原则的一个示例,假设我们迭代日历的月份: int this_month = 5; int next_month = (this_month + 1) % 12; 其中两个整数都将介于0和11之间,包括0和11。因此,上面的表达式将整数“钳制”到范围R=[0,11]。这种使用表达式的方法简单、优雅且有利,因为它省略了分支 现在,如果我们想做同样的事情,但是

对于受范围R=[x,y]限制的任何整数输入W,由于缺少更好的术语,W对R的“溢出”为
W%(y-x+1)+x
。如果W超过y,则会使其回绕

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

int this_month = 5;
int next_month = (this_month + 1) % 12;
其中两个整数都将介于0和11之间,包括0和11。因此,上面的表达式将整数“钳制”到范围R=[0,11]。这种使用表达式的方法简单、优雅且有利,因为它省略了分支

现在,如果我们想做同样的事情,但是向后做呢?以下表达方式有效:

int last_month = ((this_month - 1) % 12 + 12) % 12;
但这很深奥。如何美化


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

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

K%K总是0。我不是100%确定你想做什么,但似乎你想把上个月限制在0到11之间

(this_month + 11) % 12

应该足够了。

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

//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);
}
//返回楼层(a/n)(完全完成除法)。
/让我做数学除法和/或C++除法。
//我们知道
//a÷b=a/b+f(f是余数,不是全部
//除法有精确的积分结果)
//及
//(a/b)*b+a%b==a(来自标准)。
//总之,这意味着(通过代数运算):
//符号(f)==符号(a%b)*符号(b)
//我们希望余数(f)始终大于等于0(根据FloordDivision的定义),
//所以当符号(f)<0时,我们从a/n中减去1,使f>0。
模板
积分地板分割(积分a,积分n){
积分q(a/n);
如果((a%n<0&&n>0)|(a%n>0&&n<0))--q;
返回q;
}
//FloorredModulo:用于构造的模函数
//循环拓扑。结果将始终介于0和0之间
//分母,并将以自然方式循环(而不是交换
//零点上的循环方向(如C++11),
//或者未指定(如早期的C++)。
//返回x,以便:
//
//实a=实(分子)
//实n=实(分母)
//实际r=a-n*楼层(n/d)
//x=积分(r)
模板
积分落地模(积分a,积分n){
返回a-n*floordDivision(a,n);
}

不确定您是否遇到了与我相同的问题,但我的问题本质上是我想将所有数字限制在某个范围内。假设范围是0-6,那么使用%7意味着任何大于6的数字都将返回到0或更高。实际问题是,小于零的数字并没有回到6。我有一个解决方案(其中X是数字范围的上限,0是最小值):


if(inputNumber您的表达式应该是
((x-1)+k)%k
。这将正确地将x=0包装到11。通常,如果您想后退超过1,您需要确保添加足够的值,以便模运算的第一个操作数>=0

下面是C++中的一个实现:

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;}
}
这也允许使用标记为0到11或1到12的月份,相应地设置
min_val
max_val

由于这个答案非常受欢迎,这里有一个没有分支的改进版本,它还处理初始值
v
小于
minval
的情况。我保留另一个示例,因为它更容易理解:

int wrapAround(int v, int delta, int minval, int maxval)
{
  const int mod = maxval + 1 - minval;
  v += delta - minval;
  v += (1 - v / mod) * mod;
  return v % mod + minval;
}

剩下的唯一问题是,如果
minval
大于
maxval
。如果需要,请随时添加断言。

简单,不要使用第一个模块运算符,这是多余的:

 int last_month = (this_month - 1 + 12) % 12;
这是一般情况


在本例中,您可以编写
11
,但我仍然会执行
-1+11
,因为它更清楚地说明了您想要实现的目标。

请注意,普通mod会导致模式
0…11
12…23
24…35
等处重复。但不会在
-11…-1
上进行包装。换句话说,它有两个se一组行为。一组来自
-infinity…-1
,另一组来自
0…infinity

表达式
((x-1)%k+k)%k
修复了
-11…-1
,但与普通mod的
-23…-12
有相同的问题。也就是说,虽然它修复了12个额外的数字,但它不会无限环绕。它仍然有一组来自
-infinity…-12
的行为,以及一个不同于
-11…+infinity
的行为

这意味着,如果使用函数进行偏移,可能会导致错误代码

如果你想要一个真正的环绕模式,它应该以完全相同的方式处理整个范围,
-infinity…infinity

可能有更好的方法来实现这一点,但这里有一个易于理解的实现:

// n must be greater than 0
func wrapAroundMod(a: Int, n: Int) -> Int {
    var offsetTimes: Int = 0

    if a < 0 {
        offsetTimes = (-a / n) + 1
    }

    return (a + n * offsetTimes) % n
}
//n必须大于0
func wraproundmod(a:Int,n:Int)->Int{
变量偏移时间:Int=0
如果a<0{
抵销时间=(-a/n)+1
}
返回(a+n*偏移时间)%n
}

事实上,
-1%12==-1
-1%12=11
--在
c++
中是这样吗?@mankarse2做了吗?我只是在计算器中试过,还没有机会在编译器中试过。@Night5h4d3:在c++03中,如果任何一个操作数为负数,则符号未指定。在c++11中,结果的符号与的符号相同第一个操作数()%k是解决方案!数字
-1
不能低于
-(k-1)// n must be greater than 0
func wrapAroundMod(a: Int, n: Int) -> Int {
    var offsetTimes: Int = 0

    if a < 0 {
        offsetTimes = (-a / n) + 1
    }

    return (a + n * offsetTimes) % n
}