您将获得一个整数 n 和一个理性 p / q ( p 和 q 是整数)
如何比较 sqrt(n)和 p / q ?
解决方案1 :sqrt(n) <= (double) p / q
应该工作,但调用sqrt
比使用乘法/除法慢。{/ p>
解决方案2: (double) n * q * q <= p * p
更好,但我无法思考,因为我们使用浮动,如果 p / q 非常接近 sqrt(n),我们可能得到错误的答案。此外,它需要将整数转换为浮点数,这比使用整数更慢(略微)。
解决方案3 :n*q*q <= p*p
更好的是,如果由于溢出而导致 p 和 q 变大,则会遇到麻烦(通常,如果 p 或 q 使用64位整数时,em>&gt; = 2 ^ 32。
解决方案4 :使用带有bignum库的解决方案3 /使用具有未绑定整数的编程语言。
解决方案5: (q / p) * n <= p / q
成功避免任何溢出问题,但我不确定这在所有情况下都是正确的,因为整数除法......
所以...我很乐意选择解决方案2或4,但我想知道是否有人有巧妙的技巧来解决这个问题,或者可能是解决方案5工作(或不工作)的证明(或反例)。
答案 0 :(得分:3)
正如我所评论的,一个简单而优雅的解决方案是使用bignum,特别是如果内置,或者使用所选语言轻松提供。它对n,p,q没有限制。
我将在此处开发一种基于IEEE浮点的替代解决方案:
我会注意到f
浮点类型,f(x)
将值x转换为f
,可能会舍入到最近的浮点数,与偶数相关联。
fsqrt(x)
将表示精确平方根的浮点近似。
让f x = fsqrt(f(n))
和f y = f(p) / f(q)
。
根据IEEE 754属性,x和y都是最接近精确结果的浮点数,而n=f(n)
,p=f(p)
,q=f(q)
来自我们的初步条件。
因此,如果x < y
问题得到解决sqrt(n) < p/q
。
如果x > y
问题也解决了sqrt(n) > p/q
。
如果x == y
我们无法立即告知......
让我们注意残留f r = fma(x,x,-n)
和f s = fma(y,q,-p)
。
我们完全有r = x*x - n
和s = y*q - p
。因此s/q = y - p/q
(确切的操作,而不是浮点操作)。
现在我们可以比较剩余误差。 (p/q)^2 = y^2-2*y*s/q+ (s/q)^2
。它与n = x^2 - r
相比如何?
n-(p/q)^2 = 2*y*s/q - r - (s/q)^2
。
因此,我们得到了差异d
的近似值,在第一顺序:f d = 2*y*s/f(q) - r
。所以这是一个类似C的原型:
int sqrt_compare(i n,i p,i q)
/* answer -1 if sqrt(n)<p/q, 0 if sqrt(n)==p/q, +1 if sqrt(n)>p/q */
/* n,p,q are presumed representable in f exactly */
{
f x=sqrt((f) n);
f y=(f) p / (f) q;
if(x<y) return -1;
if(x>y) return +1;
f r=fma(x,x,-(f) n);
f s=fma(y,(f) q,-(f) p);
f d=y*s/(f) q - r;
if(d<0) return -1;
if(d>0) return +1;
if(r==0 && s==0) return 0; /* both exact and equal */
return -1; /* due to 2nd order */
}
正如你所看到的,它相对较短,应该是高效的,但很难破译,所以至少从这个POV来看,我不会认为这个解决方案比琐碎的bignum更好。
答案 1 :(得分:1)
您可以考虑使用大小为2x的整数解决方案3,
n * uint2n_t{q} * q <= uint2n_t{p} * p
如果n * q * q
溢出,则会溢出,但在这种情况下,无论如何都会返回false。
uint2n_t nqq;
bool overflow = __builtin_mul_overflow(uint2n_t{n} * q, q, &nqq);
(!overflow) && (uint2n_t{n} * q * q <= uint2n_t{p} * p);