用整数运算计算第N个根

时间:2012-01-11 21:21:41

标签: algorithm math integer square-root

有两种方法可以仅使用整数运算来查找整数平方根。例如this one。它有趣的阅读和一个非常有趣的理论,特别是对于我这一代,这些技术不再那么有用。

主要的是它不能使用浮点运算,因此排除了牛顿方法及其推导。我知道找到根的另一种方法是二项式扩展,但这也需要浮点运算。

使用整数算法计算整数第n个根的技术/算法是什么?

编辑:感谢目前为止的所有答案。他们似乎都更加智能的试验和改进。有没有更好的办法?

Edit2:好的,所以看起来似乎没有聪明的方法来做这个没有试用/改进和牛顿方法或二进制搜索。任何人都可以比较理论上的两个 吗?我在两者之间运行了一些基准测试,发现它们非常相似。

6 个答案:

答案 0 :(得分:8)

您可以仅使用整数运算来使用Newton方法,该步骤与浮点运算相同,除非您必须使用具有不同运算符的语言中的相应整数运算符替换浮点运算符。

假设您要查找a > 0的整数第k个根,它应该是r的最大整数r^k <= a。你从任何正整数开始(当然一个好的起点有帮助)。

int_type step(int_type k, int_type a, int_type x) {
    return ((k-1)*x + a/x^(k-1))/k;
}

int_type root(int_type k, int_type a) {
    int_type x = 1, y = step(k,a,x);
    do {
        x = y;
        y = step(k,a,x);
    }while(y < x);
    return x;
}

除了第一步,你有x == r <==> step(k,a,x) >= x

答案 1 :(得分:6)

一种显而易见的方法是将binary searchexponentiation by squaring一起使用。这样,您就可以在nthRoot(x, n)中找到O(log (x + n))[0, x]中的二进制搜索,查找k的最大整数k^n <= x。对于某些k,如果k^n <= x,则将搜索范围缩小为[k + 1, x],否则将其缩减为[0, k]

你需要更聪明或更快的东西吗?

答案 2 :(得分:4)

在我看来,Shifting nth root algorithm完全符合您的要求:

  

移位的第n根算法是用于提取正实数的第n个根的算法,该算法通过移位基数的n个数位来迭代地进行,从最重要的开始,并且在每次迭代时产生一个数字的根,以类似于长期划分的方式。

链接的维基百科页面上有一些工作示例。

答案 3 :(得分:3)

一个简单的解决方案是使用二进制搜索。

假设我们正在寻找x的第n个根。

Function GetRange(x,n):
    y=1
    While y^n < x:
        y*2
    return (y/2,y)

Function BinSearch(a,b,x,):
    if a == b+1:
        if x-a^n < b^n - x:
           return a
        else:
           return b
    c = (a+b)/2
    if n< c^n:
        return BinSearch(a,c,x,n)
    else:
        return BinSearch(c,b,x,n)

a,b = GetRange(x,n)
print BinSearch(a,b,x,n)

===更快的版本===

Function BinSearch(a,b,x,):
    w1 = x-a^n
    w2 = b^n - x
    if a <= b+1:
        if w1 < w2:
           return a
        else:
           return b
    c = (w2*a+w1*b)/(w1+w2)
    if n< c^n:
        return BinSearch(a,c,x,n)
    else:
        return BinSearch(c,b,x,n)

答案 4 :(得分:0)

VBA中的算法更简单。

Public Function RootNth(radicand As Double, degree As Long) As Double
   Dim countDigits As Long, digit As Long, potency As Double
   Dim minDigit As Long, maxDigit As Long, partialRadicand As String
   Dim totalRadicand As String, remainder As Double

  radicand = Int(radicand)
  degree = Abs(degree)
  RootNth = 0
  partialRadicand = ""
  totalRadicand = CStr(radicand)
  countDigits = Len(totalRadicand) Mod degree
  countDigits = IIf(countDigits = 0, degree, countDigits)
  Do While totalRadicand <> ""
     partialRadicand = partialRadicand + Left(totalRadicand, countDigits)
     totalRadicand = Mid(totalRadicand, countDigits + 1)
     countDigits = degree
     minDigit = 0
     maxDigit = 9
     Do While minDigit <= maxDigit
        digit = Int((minDigit + maxDigit) / 2)
        potency = (RootNth * 10 + digit) ^ degree
        If potency = Val(partialRadicand) Then
           maxDigit = digit
           Exit Do
        End If
        If potency < Val(partialRadicand) Then
           minDigit = digit + 1
        Else
           maxDigit = digit - 1
        End If
     Loop
     RootNth = RootNth * 10 + maxDigit
  Loop
   End Function

答案 5 :(得分:0)

我在 Excel VBA 中创建了算法。目前,它仅计算整数的根。同样,实现小数也很容易。

只需将代码复制并粘贴到EXCEL模块中,然后将函数的名称传递到某个单元格中,并传递参数即可。

Public Function RootShift(ByVal radicand As Double, degree As Long, Optional ByRef remainder As Double = 0) As Double

   Dim fullRadicand As String, partialRadicand As String, missingZeroes As Long, digit As Long

   Dim minimalPotency As Double, minimalRemainder As Double, potency As Double

   radicand = Int(radicand)

   degree = Abs(degree)

   fullRadicand = CStr(radicand)

   missingZeroes = degree - Len(fullRadicand) Mod degree

   If missingZeroes < degree Then

      fullRadicand = String(missingZeroes, "0") + fullRadicand

   End If

   remainder = 0

   RootShift = 0

   Do While fullRadicand <> ""

      partialRadicand = Left(fullRadicand, degree)

      fullRadicand = Mid(fullRadicand, degree + 1)

      minimalPotency = (RootShift * 10) ^ degree

      minimalRemainder = remainder * 10 ^ degree + Val(partialRadicand)

      For digit = 9 To 0 Step -1

          potency = (RootShift * 10 + digit) ^ degree - minimalPotency

          If potency <= minimalRemainder Then

             Exit For

          End If

      Next

      RootShift = RootShift * 10 + digit

      remainder = minimalRemainder - potency

   Loop

End Function