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?
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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With