Precise nth root

Question:

I’m looking for Python Nth root function/algorithm but before you post: NO INTEGER ROOT, HELL!
Where could I obtain at least a guide how to program Nth root function that produces precise float/Decimal?
Such function that doesn’t return 1 nor 0 for root(125, 1756482845) (1st argument is the number, 2nd is the root depth (or something)).

EDIT: So, you were giving me this solution: n ** (1.0 / exp) which I knew when I asked this question, but it just doesn’t work for, for example, exp = 3. You can’t express 1/3 in terms of rational numbers, so 125 ** (1/3) gives incorrect result 4.999999.... I was asking for some “smart” algorithm, which gives correct result for such nice numbers and at least 4-decimal-points-accurate result for rational exp. If there isn’t such function or algorithm, I will use this (n ** (1/exp)).

Asked By: R.O.S.S

||

Answers:

You mean something like that:

>>> 125**(1/9.0)
1.7099759466766968

Something else that might interest you is bigfloat module (haven’t used personally just know it exists πŸ™‚ – actually had problem installing it in the past-maybe an OS X fault)

Answered By: coder

It’s the function pow of math module.

import math
math.pow(4, 0.5) 

will return the square root of 4, which is 2.0.

For root(125, 1756482845), what you need to do is

math.pow(125, 1.0 / 1756482845)
Answered By: ζ€€ζ˜₯좘

I would try the gmpy2 library.

>>> import gmpy2
>>> gmpy2.root(125,3)
mpfr('5.0')
>>> 

gmpy2 uses the MPFR library to perform correctly rounded floating point operations. The default precision is 53 bits but that can be increased.

>>> gmpy2.root(1234567890123456789**11, 11)
mpfr('1.2345678901234568e+18')  # Last digits are incorrect.
>>> gmpy2.get_context().precision=200
>>> gmpy2.root(1234567890123456789**11, 11)
mpfr('1234567890123456789.0',200)
>>> 

Disclaimer: I maintain gmpy2.

Answered By: casevh

You can do a binary search on the answer. If you want to find the X that is equal to the kth root of N, you can do a binary search on X testing for each step of the binary search whether X^k equals N +- some small constant to avoid precision issues.

Here is the code:

import math

N,K = map(float,raw_input().split()) # We want Kth root of N
lo = 0.0
hi = N
while 1:
    mid = (lo+hi)/2
    if math.fabs(mid**K-N) < 1e-9: # mid^K is really close to N, consider mid^K == N
        print mid
        break
    elif mid**K < N: lo = mid
    else: hi = mid

For (N,K) = (125,3) it prints 5.0, the correct answer. You can make it more precise by changing the 1e-9 constant, but there is a precision limit related to the float variables precision limit in Python

Answered By: Lucas Sampaio

In Squeak Smalltalk, there is a nthRoot: message that answers the exact Integer result if ever the Integer receiver is exact nth power of some whole number. However, if the solution is an algebraic root, then the implementation does not fallback to a naive n**(1/exp); the method rounds to nearest float by appropriate care of residual.

Relevant code (MIT license) is reproduced here. The base algorithm is searching for truncated nth root of an Integer with some Newton-Raphson:

Integer>>nthRootTruncated: aPositiveInteger
    "Answer the integer part of the nth root of the receiver."
    | guess guessToTheNthMinusOne nextGuess |
    self = 0 ifTrue: [^0].
    self negative
        ifTrue:
            [aPositiveInteger even ifTrue: [ ArithmeticError signal: 'Negative numbers don''t have even roots.' ].
            ^(self negated nthRootTruncated: aPositiveInteger) negated].
    guess := 1 bitShift: self highBitOfMagnitude + aPositiveInteger - 1 // aPositiveInteger.
    [
        guessToTheNthMinusOne := guess raisedTo: aPositiveInteger - 1.
        nextGuess := (aPositiveInteger - 1 * guess * guessToTheNthMinusOne + self) // (guessToTheNthMinusOne * aPositiveInteger).
        nextGuess >= guess ] whileFalse:
            [ guess := nextGuess ].
    ( guess raisedTo: aPositiveInteger) > self  ifTrue:
            [ guess := guess - 1 ].
    ^guess

It’s not particularly clever, because convergence can be very slow in case of huge exponent, but well, it works.
Then, the same root rounded away from zero:

Integer>>nthRootRounded: aPositiveInteger
    "Answer the integer nearest the nth root of the receiver."
    | guess |
    self = 0 ifTrue: [^0].
    self negative
        ifTrue:
            [aPositiveInteger even ifTrue: [ ArithmeticError signal: 'Negative numbers don''t have even roots.' ].
            ^(self negated nthRootRounded: aPositiveInteger) negated].
    guess := self nthRootTruncated: aPositiveInteger.
    ^self * 2 > ((guess + 1 raisedTo: aPositiveInteger) + (guess raisedTo: aPositiveInteger))
        ifTrue: [guess + 1]
        ifFalse: [guess]

Then exactness is tested in nthRoot:

Integer>>nthRoot: aPositiveInteger
    "Answer the nth root of the receiver.
    Answer an Integer if root is exactly this Integer, else answer the Float nearest the exact root."

    | guess excess scaled nBits |
    guess := self nthRootRounded: aPositiveInteger.
    excess := (guess raisedTo: aPositiveInteger) - self.
    excess = 0 ifTrue: [ ^ guess ].

    nBits := Float precision - guess highBitOfMagnitude.
    nBits <= 0 ifTrue: [ ^(Fraction numerator: guess * 4 - excess sign denominator: 4) asFloat].

    scaled := self << (nBits * aPositiveInteger).
    guess := scaled nthRootRounded: aPositiveInteger.
    excess := (guess raisedTo: aPositiveInteger) - scaled.
    ^(Fraction numerator: guess * 4 - excess sign denominator: 1 << (nBits + 2)) asFloat

This could be applied to Fraction too, but the nearest float is a bit more complex, and Squeak implementation is currently naive.

It works for large integers like:

  • (10 raisedTo: 600) nthRoot: 300 -> 100 “exact”
  • (10 raisedTo: 600) + 1 nthRoot: 300 -> 100.0 “inexact”

If you don’t have such expectations, the initial guess could use inexact naive n**(1/exp).

The code should be easy to port in Python and leaves a lot of place for optimization.

I didn’t check what was available in Python, but maybe you’ll need correctly rounded LargeInteger -> Float, and Fraction -> Float, like explained here (Smalltalk too, sorry about that, but the language does not really matter).

Answered By: aka.nice

root_i() method with num7 package can compute precise nth roots in python3 as the following 3 calculations on large decimal numbers:

#ROOT ITH
from num7 import Num

def main():
    E = 8; n = '123.456'
    n = Num(n)**E; n = Num(n); ITH = E
    print(n, 'n_len =>', n.len())
    r = n.root_i(ITH)
    print(f'root {E}th =>', r, 'r_len =>', r.len(), 'TEST =>', Num(r)**E==n)
    print('-'*20)
    
    E = 11; n = '-2.51'
    n = Num(n)**E; n = Num(n); ITH = E
    print(n, 'n_len =>', n.len())
    r = n.root_i(ITH)
    print(f'root {E}th =>', r, 'r_len =>', r.len(), 'TEST =>', Num(r)**E==n)
    print('-'*20)
    
    E = 17; ni = 99999377777
    n = ni**E; n = Num(n); ITH = E
    print(n, 'n_len =>', n.len())
    r = n.root_i(ITH)
    print(f'root {E}th =>', r, 'r_len =>', r.len(), 'TEST =>', ni**E==int(n))

if __name__ == '__main__':
    main()
    print('ROOTS ITH OVER')

Answers:

  • 53963189778652575.835351234834193203593216 n_len => (17, 24)
    root 8th => 123.456 r_len => (3, 3) TEST => True
  • -24912.1342886682932269065251 n_len => (5, 22)
    root 11th => -2.51 r_len => (1, 2) TEST => True
  • 9998942273552320705608024716337581191221364529970477148830859837880699665559933625621112988202037402699235718754608281313637006526528759998852623150834378239672382087798737606758743312497.0 n_len => (187, 0)
    root 17th => 99999377777.0 r_len => (11, 0) TEST => True
    ROOTS ITH OVER
Answered By: giocip