How to compute the nth root of a very big integer

后端 未结 10 568
暖寄归人
暖寄归人 2020-12-01 06:28

I need a way to compute the nth root of a long integer in Python.

I tried pow(m, 1.0/n), but it doesn\'t work:

OverflowError: lo

相关标签:
10条回答
  • 2020-12-01 07:09

    Try converting the exponent to a floating number, as the default behaviour of / in Python is integer division

    n**(1/float(3))

    0 讨论(0)
  • 2020-12-01 07:11

    If it's a REALLY big number. You could use a binary search.

    def find_invpow(x,n):
        """Finds the integer component of the n'th root of x,
        an integer such that y ** n <= x < (y + 1) ** n.
        """
        high = 1
        while high ** n <= x:
            high *= 2
        low = high/2
        while low < high:
            mid = (low + high) // 2
            if low < mid and mid**n < x:
                low = mid
            elif high > mid and mid**n > x:
                high = mid
            else:
                return mid
        return mid + 1
    

    For example:

    >>> x = 237734537465873465
    >>> n = 5
    >>> y = find_invpow(x,n)
    >>> y
    2986
    >>> y**n <= x <= (y+1)**n
    True
    >>>
    >>> x = 119680039660309643568856114803834088331723464504673392511960931441>
    >>> n = 45
    >>> y = find_invpow(x,n)
    >>> y
    227661383982863143360L
    >>> y**n <= x < (y+1)**n
    True
    >>> find_invpow(y**n,n) == y
    True
    >>>
    
    0 讨论(0)
  • 2020-12-01 07:12

    Gmpy is a C-coded Python extension module that wraps the GMP library to provide to Python code fast multiprecision arithmetic (integer, rational, and float), random number generation, advanced number-theoretical functions, and more.

    Includes a root function:

    x.root(n): returns a 2-element tuple (y,m), such that y is the (possibly truncated) n-th root of x; m, an ordinary Python int, is 1 if the root is exact (x==y**n), else 0. n must be an ordinary Python int, >=0.

    For example, 20th root:

    >>> import gmpy
    >>> i0=11968003966030964356885611480383408833172346450467339251 
    >>> m0=gmpy.mpz(i0)
    >>> m0
    mpz(11968003966030964356885611480383408833172346450467339251L)
    >>> m0.root(20)
    (mpz(567), 0)
    
    0 讨论(0)
  • 2020-12-01 07:15

    You can make it run slightly faster by avoiding the while loops in favor of setting low to 10 ** (len(str(x)) / n) and high to low * 10. Probably better is to replace the len(str(x)) with the bitwise length and using a bit shift. Based on my tests, I estimate a 5% speedup from the first and a 25% speedup from the second. If the ints are big enough, this might matter (and the speedups may vary). Don't trust my code without testing it carefully. I did some basic testing but may have missed an edge case. Also, these speedups vary with the number chosen.

    If the actual data you're using is much bigger than what you posted here, this change may be worthwhile.

    from timeit import Timer
    
    def find_invpow(x,n):
        """Finds the integer component of the n'th root of x,
        an integer such that y ** n <= x < (y + 1) ** n.
        """
        high = 1
        while high ** n < x:
            high *= 2
        low = high/2
        while low < high:
            mid = (low + high) // 2
            if low < mid and mid**n < x:
                low = mid
            elif high > mid and mid**n > x:
                high = mid
            else:
                return mid
        return mid + 1
    
    def find_invpowAlt(x,n):
        """Finds the integer component of the n'th root of x,
        an integer such that y ** n <= x < (y + 1) ** n.
        """
        low = 10 ** (len(str(x)) / n)
        high = low * 10
    
        while low < high:
            mid = (low + high) // 2
            if low < mid and mid**n < x:
                low = mid
            elif high > mid and mid**n > x:
                high = mid
            else:
                return mid
        return mid + 1
    
    x = 237734537465873465
    n = 5
    tests = 10000
    
    print "Norm", Timer('find_invpow(x,n)', 'from __main__ import find_invpow, x,n').timeit(number=tests)
    print "Alt", Timer('find_invpowAlt(x,n)', 'from __main__ import find_invpowAlt, x,n').timeit(number=tests)
    

    Norm 0.626754999161

    Alt 0.566340923309

    0 讨论(0)
提交回复
热议问题