任何简洁的方法来计算n *(n + 1)/ 2并同时处理溢出

时间:2017-09-17 16:32:01

标签: c overflow

我正在尝试实现n * (n + 1) / 2知道nint&lt; = 2 ^ 16 - 1(这可以保证n * (n + 1) / 2 <= 2^31 - 1所以没有溢出) 。

然后我们知道n * (n + 1) / 2保证是非负整数。但是,当在程序中计算此值时,如果我们先进行乘法n *(n + 1),我们可能会遇到整数溢出问题。我的想法是使用笨拙的条件:

int m;
if (n % 2 == 0) {
    m = (n / 2) * (n + 1);
} else {
    m = n * ((n + 1) / 2);
}

还有更简洁的方法吗?

5 个答案:

答案 0 :(得分:4)

使用三元运算符编写测试有一种更简洁的方法:

int m = (n % 2 == 0) ? (n / 2) * (n + 1) : n * ((n + 1) / 2);

但它可能会生成完全相同的代码。

您可以利用额外的精度long long保证提供(至少63个值位):

int m = (long long)n * (n + 1) / 2;

这是否比测试版本效率更高或更低取决于目标CPU和编译器版本和选项。这个版本更易于阅读和理解,这很有价值。添加注释来解释结果将在范围内的原因将非常有用。

根据Amadeus的建议,这里有一个更简洁,但更不易读的替代方案,它不使用64位算术:

int m = (n + (n & 1)) / 2 * (n + 1 - (n & 1));

演示:

  • 如果n是奇数,我们会得到m = (n + 1) / 2 * n;
  • 如果n是偶数,我们会得到:m = n / 2 * (n + 1);

答案 1 :(得分:3)

您如何看待:

m = ((n + (n & 1)) >> 1) * ( n + !(n & 1));

说明:

此解决方案尝试实现两个目标:

  • 不要溢出
  • 避免使用if then else条件,并且管道友好

为了避免溢出,我们首先划分和乘法。一旦除数达到一半(2),它就有一个有趣的属性:如果数字是奇数,则除法是精确的,可以通过简单的右筛选来完成。

因此,为了保证数字为奇数而没有if then else条件,我们使用以下技巧:

如果number为奇数,则表示低位为零(通过1和1捕获),否则为偶数。因此,如果数字是奇数,我们将它除以2,否则,我们首先加1,以确保它是奇数和除数。

换句话说,这个解决方案相当于:

if ( n is odd )
    m = (n >> 1) * (n + 1);
else
    m = ( (n + 1) >> 1) * n;

答案 2 :(得分:3)

最简单的解决方案可能是使用更大的中间类型:

int m = (int)((long long)n * (n + 1) / 2) ;

由于自动类型提升将适用,因此无需投射所有操作数。

答案 3 :(得分:1)

还有一个:

  int m =  (n/2 * n)  + ((n%2) * (n/2)) + (n/2) + (n%2);

答案 4 :(得分:1)

也许

result = (n) * (n / 2) + (n & 1) * (n) + n / 2 ;