x(x-1)/ 2 = c的快整数解

时间:2014-10-01 20:10:56

标签: c++ algorithm integer-arithmetic

给定一个非负整数c,我需要一个有效的算法来找到最大的整数x,这样

x*(x-1)/2 <= c

同样,我需要一种高效且可靠的精确算法来计算:

x = floor((1 + sqrt(1 + 8*c))/2)        (1)

为了定义,我标记了这个问题C ++,所以答案应该是用该语言编写的函数。您可以假设c是无符号的32位int。

此外,如果您可以证明(1)(或涉及浮点运算的等效表达式)总是给出正确的结果,那也是一个有效的答案,因为现代处理器上的浮点数可能比整数算法更快。

3 个答案:

答案 0 :(得分:5)

如果您愿意假设IEEE为所有操作(包括平方根)进行正确的舍入,那么您编写的表达式(加上强制转换为double)会在所有输入上给出正确答案。

这是一个非正式的证明。由于c是一个32位无符号整数,转换为具有53位有效位数的浮点类型,因此1 + 8*(double)c是精确的,sqrt(1 + 8*(double)c)是正确舍入的。 1 + sqrt(1 + 8*(double)c)精确到一个ulp,因为最后一个术语小于2**((32 + 3)/2) = 2**17.5意味着后一个术语最后一个单位的单位小于1,因此{{1}由于(1 + sqrt(1 + 8*(double)c))/2的除法是准确的,因此精确到一个ulp内。

最后一项业务是发言。这里的问题是将2四舍五入为整数。当且仅当(1 + sqrt(1 + 8*(double)c))/2向上舍入到奇数时才会发生这种情况。由于sqrt(...)的参数是一个整数,对于正奇数整数sqrt,最坏的情况看起来像sqrt(z**2 - 1),我们绑定

z
通过泰勒扩张来实现。由于z - sqrt(z**2 - 1) = z * (1 - sqrt(1 - 1/z**2)) >= 1/(2*z) 小于z,因此在幅度小于2**17.5的情况下,与最接近的整数之间的差距至少为1/2**18.5,这意味着此错误不会来自正确舍入2**17.5

采用Yakk的简化,我们可以写

sqrt

没有进一步检查。

答案 1 :(得分:1)

如果我们从二次公式开始,我们会快速达到sqrt(1/4 + 2c),向上舍入为1/2或更高。

现在,如果以浮点进行计算,可能会出现不准确之处。

有两种方法可以解决这些不准确之处。首先要仔细确定它们的大小,确定计算值是否足够接近一半才能使它们变得重要。如果它们不重要,只需返回值即可。如果它们是,我们仍然可以将答案限定为两个值之一。在整数数学中测试这两个值,然后返回。

但是,我们可以取消这一小心点,并注意sqrt(1/4 + 2c)如果值为32位,则误差小于0.5,我们使用double秒。 (我们无法使用float来保证这一点,因为2^31 float无法在不舍入的情况下处理+0.5

在本质上,我们使用二次公式将其减少为两种可能性,然后测试这两种可能性。

uint64_t eval(uint64_t x) {
  return x*(x-1)/2;
}
unsigned solve(unsigned c) {
  double test = sqrt( 0.25 + 2.*c );
  if ( eval(test+1.) <= c )
    return test+1.
  ASSERT( eval(test) <= c );
  return test;
}

请注意,将正double转换为整数类型会向0舍入。如果需要,可以插入floor

答案 2 :(得分:1)

这可能与您的问题有点相关。但是,引起我注意的是具体的公式。您正在尝试找到T n - 1 的三角形根(其中T n n 三角形数字)。

I.e。:

  

T n = n *(n + 1)/ 2

     

     

T n - n = T n - 1 = n *(n - 1)/ 2

从描述here的漂亮技巧中,对于T n ,我们有:

  

n = int(sqrt(2 * c))

在这种情况下寻找n使得T n - 1 ≤c不会改变n的定义,原因与原始问题中的原因相同。

计算上,这节省了一些操作,因此理论上 比精确解决方案(1)更快。实际上,它可能大致相同。

这个解决方案或大卫提出的解决方案都不如你的(1)那样“精确”。

  

Exact vs int(sqrt(2 * c))

     

floor((1 + sqrt(1 + 8 * c))/ 2)(蓝色)vs int(sqrt(2 * c))(红色)vs Exact(白线) < / p>


  

Exact vs int(sqrt(0.25 + 2 * c) + 0.5)

     

floor((1 + sqrt(1 + 8 * c))/ 2)(蓝色)vs int(sqrt(0.25 + 2 * c)+ 0.5(红色)vs Exact(白线)

我的真实观点是,三角形数字是一组有趣的数字,与正方形,pascal三角形,Fibonacci数等相关联。人。

因此,它们周围有loads of identities,可用于以不需要平方根的方式重新排列问题。

特别感兴趣的是T n + T n - 1 = n 2

我假设你知道你正在使用三角形数字,但是如果你没有意识到这一点,那么搜索三角形根会产生一些问题,例如this one这些问题都在同一个主题上。