不使用浮点算法,如何确定最接近n的k次方根的整数?

10
假设我想计算 k√n 并将其四舍五入到最接近的整数,其中 n 和 k 是非负整数。使用二分查找,可以找到一个整数 a 使得:

ak ≤ n < (a+1)k.

这意味着 a 或 a+1 中的一个是与 k√n 最接近的整数。但是,我不确定如何确定它是哪一个而不进行涉及浮点运算的一些计算。
给定 a、n 和 k 的值,是否有一种方法可以确定 k√n 并将其四舍五入到最接近的整数,而不进行任何浮点计算呢?
谢谢!

1
一个天真的方法是计算a^k和(a+1)^k,然后计算哪个更接近n,所有的数学运算都是整数。 - Oliver Charlesworth
3
那种方法并不一定有效。尝试使用这个方法计算16的立方根,结果约为2.51。如果你计算2的三次方,得到8,如果计算3的三次方,得到27。虽然27比16更远离目标,但正确答案是3。 - templatetypedef
啊,我明白了,我误解了你的意思 ;) - Oliver Charlesworth
好的例子。让我们来解决它。如果且仅当5^3 - 16*2^3为正数时,2.5^3 - 16才是正数。在我的整数计算器上测试一下——它显示为负数——这意味着2.5比根更小,即根更接近3。 - Colonel Panic
7个回答

7

2kak < 2kn < (2a+1)k → (除以 2k) ak < n < (a+0.5)k → (取 k 次方根) a < k√n < a+0.5,因此 n 的 k 次方根更接近于 a 而不是 a+1。请注意,极端情况不会发生;整数的 k 次方根不能是一个整数加上 0.5(a+0.5),因为不是 k 次方的 n 的 k 次方根是无理数,如果 n 是完全 k 次方,那么 k 次方根就是一个整数。


1
也许我漏掉了什么,但这如何帮助确定答案是a还是a + 1? - templatetypedef
1
那个第二个不等式中的n前面应该有一个2^k项吗? - templatetypedef
@templatetypedef 是的;已经修复了。 - Ramchandra Apte
@unutbu 唉。已经修复了。 - Ramchandra Apte
1
@RamchandraApte:2^k (2a+1)^k 应该只是 (2a+1)^k,对吗?(顺便说一句,回答得很好)。 - unutbu
显示剩余5条评论

3

Ramchandra ApteLazarus的回答都包含了正确答案的精髓,但对我来说都有点难以理解。让我尝试更清晰地解释一下他们似乎在暗示的技巧:

基本思路是,为了找出aa+1哪个更接近kn,我们需要测试kn<a+½。

为了去掉 ½,我们可以简单地将不等式两边乘以 2,得到 2·kn < 2a+1,然后将两边都提高到第 k 次幂(并假设它们都是正数),我们得到等价的不等式 2k·n < (2a+1)k。因此,只要 2k·n = nk 不溢出,我们就可以将其与 (2a+1)k 进行比较以获得答案。
事实上,我们可以简单地计算b = ⌊ k√(2k·n) ⌋。如果b是偶数,则最接近kn的整数为b/2;如果b是奇数,则为(b+1)/2。实际上,我们可以将这两种情况结合起来,得出最接近kn的整数为⌊ (b+1) / 2 ⌋,或者在伪C语言中表示为:
int round_root( int k, int n ) {
    int b = floor_root( k, n << k );
    return (b + 1) / 2;
}

附注:另一种方法是直接使用二项式定理计算一个近似值(a+½)k,如下所示: (a+½)k = ∑i=0..k (k 选择 i) aki / 2iak + k·ak−1 / 2 + ... 然后将其与n直接比较。但是,至少在朴素情况下,对二项式展开的所有项求和仍需要保留额外的k位精度(或至少k−1位;我认为可以安全地忽略最后一项),因此它可能并不比上述建议的方法更有优势。


1
我猜想你想在FPGA / CPLD或资源有限的处理器上使用此算法,因为你的方法让我想起了CORDIC。因此,我会考虑这种情况来给出一个解决方案。
当你达到a^k ≤ n < (a+1)^k时,它意味着x = root(n,k)的floor是'a'。换句话说,x = a + f,其中0≤f<0.5。因此,将等式乘以2,你会得到2x = 2a + 2f。这基本上意味着floor(2x) = 2a(因为2f<1)。现在,x = √n(第k个根),因此2x = k√((2^k)*n)(第k个根)。因此,只需将n向左移动k位,然后使用你的算法计算其第k个根。如果其下界恰好是n的第2倍k个根,则n的第k个根为a,否则为a+1。
假设您有一个函数可以给出n的k次方根的下限(rootk(n)),使用二进制运算符和C符号,最终结果为:
closestint = a + ((rootk(n) << 1) == rootk(n>>k));

0

计算 (a + 0.5)*10 的立方(或者10a+5 - 不使用浮点运算),然后将其除以1000

然后检查该数字在哪一侧。

乘以10的想法是将小数点向右移动一位。然后我们除以1000,因为我们进行了3次乘法(立方操作)。

例如:

Input: 16

a = 2
a+1 = 3

a^3 = 8
(a+1)^3 = 27

10a + 5 = 25

25^3 = 15625

floor(15625 / 1000) = 15

16 > 15, thus 3 is closer.

正如Oli所指出的那样,计算(a + 0.5)*2(或2a + 1)的立方,然后将其除以8也可以行得通。

例如:

2a + 1 = 5

5^3 = 125

floor(125 / 8) = 15

16 > 15, thus 3 is closer.

@OliCharlesworth 将小数点向右移动一位。 - Bernhard Barker
1
我想我的意思是,10在这里看起来像一个魔法数字;十进制并不固有于任何正在进行的数学中。 - Oliver Charlesworth
它需要是10,否则(a + 0.5)*whatever不一定是整数。 - Bernhard Barker

0

你可以使用牛顿法来找到a;它在整数上运行得非常好,并且比二分查找更快。然后使用平方-乘幂算法计算ak和(a+1)k。这里是一些代码,使用Scheme编写,因为我碰巧有这个方便

(define (iroot k n) ; largest integer x such that x ^ k <= n
  (let ((k-1 (- k 1)))
    (let loop ((u n) (s (+ n 1)))
      (if (<= s u) s
        (loop (quotient (+ (* k-1 u) (quotient n (expt u k-1))) k) u)))))

(define (ipow b e) ; b^e
  (if (= e 0) 1
    (let loop ((s b) (i e) (a 1)) ; a * s^i = b^e
      (let ((a (if (odd? i) (* a s) a)) (i (quotient i 2)))
        (if (zero? i) a (loop (* s s) i a))))))

为了确定哪个更接近根号的值,ak和(a+1)k,您可以使用幂算法计算(a + 1/2)k — 这是一个精确的计算,平方乘操作可以执行 — 然后将结果与n进行比较,并确定哪一侧更接近。


我认为在所有情况下选择a^n和(a+1)^n中的哪一个会给出正确答案是不可行的。请参考我的评论中的反例。 - templatetypedef
我误解了问题。抱歉。请看我刚刚添加到答案中的最后一段。 - user448810

0

编辑:

很抱歉误解了问题。这里是原问题的一个可能解决方案:

使用牛顿逼近定理:-

here = means (approximately = )

f(b+a) = f(b) + a*f'(b)

 a -> 0

f(x) = x^k

f'(x) = k*x^(k-1)

hence using above equation

f(a+0.5) = a^k + 1/2*k*a^(k-1);



need to check    n < f(a+0.5)  

                 n < a^k + 1/2*k*a^(k-1)

rearranging      (n-a^k)*2 < k*a^(k-1)

注意:您可以使用二项式定理来获得更高的精度。


我觉得你可能误读了我的问题。我知道可以使用二分查找来找到接近的值,但如果根在两个整数之间,我不知道如何四舍五入到最近的整数。你的答案是否解决了这个问题? - templatetypedef
@templatetypedef 没问题,抱歉我理解错了,你需要什么样的精度? - Vikram Bhat

0

思考。理想情况下,您可以进行一次二分查找的步骤,以查看根在a+½的哪一侧。也就是说,测试不等式

(a+0.5)k < n

但左边很难精确计算(浮点问题)。因此,请写下一个等价的不等式,其中所有术语都是整数:

(2a+1)k < 2k n

完成。


与RamchandraApte相同,但我的问题是对于更大的n值,它不会导致溢出,例如我想要n = 2 ^ 40的40次根,那么2 ^ k * n = 2 ^ 80会导致溢出,而答案远低于long的大小,即2。 - Vikram Bhat
显而易见的问题是,我们能否在不发生溢出和浮点计算的情况下获得良好的精度? - Vikram Bhat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接