给定一个非负整数c
,我需要一个有效的算法来找到最大的整数x
,这样
x*(x-1)/2 <= c
同样,我需要一种高效且可靠的精确算法来计算:
x = floor((1 + sqrt(1 + 8*c))/2) (1)
为了定义,我标记了这个问题C ++,所以答案应该是用该语言编写的函数。您可以假设c
是无符号的32位int。
此外,如果您可以证明(1)(或涉及浮点运算的等效表达式)总是给出正确的结果,那也是一个有效的答案,因为现代处理器上的浮点数可能比整数算法更快。
答案 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)那样“精确”。
![]()
floor((1 + sqrt(1 + 8 * c))/ 2)(蓝色)vs int(sqrt(2 * c))(红色)vs Exact(白线) < / p>
![]()
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这些问题都在同一个主题上。