How to compute -1 / (-343)^(1/3) as 1/7 in Python?

873 views Asked by At

I like to incorporate Python in my exploration of functions, however I have encountered a behavior that I didn't expect or want for these assessments.

>>> def h(x):
...     return -1 / x**(1/3)
...
>>> h(-343)
(-0.07142857142857145 + 0.12371791482634838j)

I would like a true inverse to the following function:

>>> def f(x):
...     return x**3
...
>>> f(-7)
-343

Such that:

>>> def h(x):
...     return -1/inverse_f(x)
...
>>> h(-343)
0.14285714285714285

Is there a Pythonic way to get this behavior?

2

There are 2 answers

0
Nayuki On BEST ANSWER

You are getting problems because a negative number raised to a fractional power can be a complex number.


The solution is to apply a math identity. We know that if x is negative, then x1/3 is equal to −((−x)1/3). In other words, we turn x into a positive number, take the cube root, and negate it again. Here is the Python code to do so:

def h(x):
    if x >= 0:
        return -1.0 / x**(1.0/3.0)
    else:  # x < 0
        return -h(-x)

To explain why you are getting the problem in the first place, it helps to look at the implementation of x**y (the power operator). The key mathematical identity is that xy = exp(log(x) · y). This identity makes it easier to handle powers, because the exponent is treated as a regular number and doesn't need to be analyzed (is it an integer? is it a fraction? is it negative? etc.).

When x is a positive number, log(x) is a real number. As long as y is also a real number, exp(log(x) · y) will be a real number.

But when x is a negative number, log(x) is a complex number. Specifically, it is equal to [log(-x) + π · i]. When we multiply such a complex number by y and then apply exp(), the result will usually be a complex number - which is not what you hoped for.

3
hiro protagonist On

if you want to work in the integers only (and ignore the complex solutions of your function) this may be a way. at least for your example in the title it does what you want. (so this only addresses the title of your question; as the rest has changed now it will not help with that... Nayuki's answer will)

gmpy2 has an iroot method:

import gmpy2
print(gmpy2.iroot(343, 3))  # -> (mpz(7), True)

starting from there you should be able to combine your function.

import gmpy2
from fractions import Fraction

def h(x):
    sign = 1 if x >= 0 else -1
    root, is_int = gmpy2.iroot(abs(x), 3)
    if not is_int:
        return None  # the result is not an integer
    root = int(root)
    return -sign * Fraction(1, root)

print(h(-343))  # -> 1/7

and the inverse:

def g(x):
    return -Fraction(1, x**3)

print(g(h(-343)))  # -> -343