What's wrong with this function to solve cubic equations?

天涯浪子 提交于 2019-12-01 05:35:43

问题


I am using Python 2 and the fairly simple method given in Wikipedia's article "Cubic function". This could also be a problem with the cube root function I have to define in order to create the function mentioned in the title.

# Cube root and cubic equation solver
#
# Copyright (c) 2013 user2330618
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, you can obtain one at http://www.mozilla.org/MPL/2.0/.

from __future__ import division
import cmath
from cmath import log, sqrt

def cbrt(x):
    """Computes the cube root of a number."""
    if x.imag != 0:
        return cmath.exp(log(x) / 3)
    else:
        if x < 0:
            d = (-x) ** (1 / 3)
            return -d
        elif x >= 0:
            return x ** (1 / 3)

def cubic(a, b, c, d):
    """Returns the real roots to cubic equations in expanded form."""
    # Define the discriminants
    D = (18 * a * b * c * d) - (4 * (b ** 3) * d) + ((b ** 2) * (c ** 2)) - \
    (4 * a * (c ** 3)) - (27 * (a ** 2) * d ** 2)
    D0 = (b ** 2) - (3 * a * c)
    i = 1j  # Because I prefer i over j
    # Test for some special cases
    if D == 0 and D0 == 0:
        return -(b / (3 * a))
    elif D == 0 and D0 != 0:
        return [((b * c) - (9 * a * d)) / (-2 * D0), ((b ** 3) - (4 * a * b * c)
        + (9 * (a ** 2) * d)) / (-a * D0)]
        else:
            D1 = (2 * (b ** 3)) - (9 * a * b * c) + (27 * (a ** 2) * d)
            # More special cases
            if D != 0 and D0 == 0 and D1 < 0:
                C = cbrt((D1 - sqrt((D1 ** 2) - (4 * (D0 ** 3)))) / 2)
            else:
                C = cbrt((D1 + sqrt((D1 ** 2) - (4 * (D0 ** 3)))) / 2)
                u_2 = (-1 + (i * sqrt(3))) / 2
                u_3 = (-1 - (i * sqrt(3))) / 2
                x_1 = (-(b + C + (D0 / C))) / (3 * a)
                x_2 = (-(b + (u_2 * C) + (D0 / (u_2 * C)))) / (3 * a)
                x_3 = (-(b + (u_3 * C) + (D0 / (u_3 * C)))) / (3 * a)
                if D > 0:
                    return [x_1, x_2, x_3]
                else:
                    return x_1

I've found that this function is capable of solving some simple cubic equations:

print cubic(1, 3, 3, 1)
-1.0

And a while ago I had gotten it to a point where it could solve equations with two roots. I've just done a rewrite and now it's gone haywire. For example, these coefficients are the expanded form of (2x - 4)(x + 4)(x + 2) and it should return [4.0, -4.0, -2.0] or something similar:

print cubic(2, 8, -8, -32)
[(-4+1.4802973661668753e-16j), (2+2.9605947323337506e-16j), (-2.0000000000000004-1.1842378929335002e-15j)]

Is this more a mathematical or a programming mistake I'm making?

Update: Thank you, everyone, for your answers, but there are more problems with this function than I've iterated so far. For example, I often get an error relating to the cube root function:

print cubic(1, 2, 3, 4)  # Correct solution: about -1.65
...
    if x > 0:
TypeError: no ordering relation is defined for complex numbers
print cubic(1, -3, -3, -1)  # Correct solution: about 3.8473
    if x > 0:
TypeError: no ordering relation is defined for complex numbers

回答1:


Wolfram Alpha confirms that the roots to your last cubic are indeed

(-4, -2, 2)

and not as you say

... it should return [4.0, -4.0, -2.0]

Not withstanding that (I presume) typo, your program gives

[(-4+1.4802973661668753e-16j), (2+2.9605947323337506e-16j), (-2.0000000000000004-1.1842378929335002e-15j)]

Which to accuracy of 10**(-15) are the exact same roots as the correct solution. The tiny imaginary part is probably due, as others have said, to rounding.

Note that you'll have to use exact arithmetic to always correctly cancel if you are using a solution like Cardano's. This one of the reasons why programs like MAPLE or Mathematica exist, there is often a disconnect from the formula to the implementation.

To get only the real portion of a number in pure python you call .real. Example:

a = 3.0+4.0j
print a.real
>> 3.0



回答2:


Hooked's answer is the way to go if you want to do this numerically. You can also do it symbolically using sympy:

>>> from sympy import roots
>>> roots('2*x**3 + 8*x**2 - 8*x - 32')
{2: 1, -4: 1, -2: 1} 

This gives you the roots and their multiplicity.




回答3:


You are using integer values - which are not automatically converted to floats by Python. The more generic solution will be to write coefficients in the function as float numbers - 18.0 instead of 18, etc. That will do the trick An illustration - from the code:

>>> 2**(1/3)
1
>>> 2**(1/3.)
1.2599210498948732
>>> 


来源:https://stackoverflow.com/questions/16270941/whats-wrong-with-this-function-to-solve-cubic-equations

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!