我正在解决2条三次曲线的解析交集,其参数在以下代码中的两个独立函数中定义。
通过绘制曲线,可以很容易地看出有一个交叉点:
缩放版:
但是,sym.solve
找不到交集,即在询问print 'sol_ H_I(P) - H_II(P) =', sol
时,没有返回结果:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import sympy as sym
def H_I(P):
return (-941.254840173) + (0.014460465765)*P + (-9.41529726451e-05)*P**2 + (1.23485231253e-06)*P**3
def H_II(P):
return (-941.254313412) + (0.014234188877)*P + (-0.00013455013645)*P**2 + (2.58697027372e-06)*P**3
fig = plt.figure()
# Linspace for plotting the curves:
P_lin = np.linspace(-5.0, 12.5, 10000)
# Plotting the curves:
p1, = plt.plot(P_lin, H_I(P_lin), color='black' )
p2, = plt.plot(P_lin, H_II(P_lin), color='blue' )
# Labels:
fontP = FontProperties()
fontP.set_size('15')
plt.legend((p1, p2), ("Curve 1", "Curve 2"), prop=fontP)
plt.ticklabel_format(useOffset=False)
plt.savefig('2_curves.pdf', bbox_inches='tight')
plt.show()
plt.close()
# Solving the intersection:
P = sym.symbols('P', real=True)
sol = sym.solve(H_I(P) - H_II(P) , P)
print 'sol_ H_I(P) - H_II(P) =', sol
答案 0 :(得分:5)
是你对解决方案的假设是真实的,并且同意对数值不确定性的错误判断。 如果您取消作业,最终会得到以下代码:
import sympy as sym
def H_I(P):
return (-941.254840173) + (0.014460465765)*P + (-9.41529726451e-05)*P**2 + (1.23485231253e-06)*P**3
def H_II(P):
return (-941.254313412) + (0.014234188877)*P + (-0.00013455013645)*P**2 + (2.58697027372e-06)*P**3
P = sym.symbols('P')
sol = sym.solve(H_I(P) - H_II(P) , P)
sol = [x.evalf() for x in sol]
print(sol)
输出:
[ - 6.32436145176552 + 1.0842021724855e-19 * I,1.79012202335501 + 1.0842021724855e-19 * I,34.4111917095165 - 1.35525271560688e-20 * I]
您可以通过sym.re(x)
如果您有特定的数字精度,我认为收集实际结果的最简单方法与此代码类似:
def is_close(a,b,tol):
if abs(a-b)<tol: return True
else: return False
P = sym.symbols('P')
sol = sym.solve(H_I(P) - H_II(P) , P)
sol = [complex(x.evalf()) for x in sol]
real_solutions = []
for x in sol:
if is_close(x,x.real,10**(-10)): real_solutions.append(x.real)
print(real_solutions)
因为你问:我使用复合物作为品味问题。根据您的进一步目的,不需要。但是,这样做没有限制。我把这个is_close()写成函数是出于一般性原因。您可能希望将此代码用于其他多项式或在不同的上下文中使用此函数,那么为什么不以智能和可读的方式编写代码呢?然而,最初的目的是告诉我变量 x 和它的实部 re(x)是否相同达到一定的数值精度,即假想部分可以忽略不计。你应也检查可忽略的实部,我遗漏了。
小的虚部通常是在求解过程中出现的复数上的减法残差。被视为准确,同情不会删除他们。 evalf()为您提供精确解的数值计算或近似。 这不是关于更好的准确性。考虑例如:
import sympy as sym
def square(P):
return P**2-2
P = sym.symbols('P')
sol2 = sym.solve(square(P),P)
print(sol2)
此代码打印:
[ - sqrt(2),sqrt(2)]
而不是您可能预期的浮点数。解决方案准确且完全准确。但是,在我看来,它不适合进一步计算。这就是我在每个同情结果上使用evalf()的原因。如果对此示例中的所有结果使用数值计算,则输出变为:
[ - 1.41421356237310,1.41421356237310]
为什么它不适合您可能会问的进一步计算?记住你的第一个代码。发现的第一个根发现是
嗯,想象中的部分是零,很好。但是,如果您打印-6.32436145176552 + 0.e-19 *我
sym.im(x) == 0
,则输出为 False 。计算机和声明'确切'是敏感的组合。那里要小心。
如果你想在没有真正强加显式数值精度的情况下只去掉小的虚部,你可以在数值评估中使用关键字.evalf(chop = True)
。这实际上忽略了不必要的小数字,并且在原始代码中会切断虚部。考虑到你甚至可以忽略你在答案中所说的任何想象部分,这对你来说可能是最好的解决方案。出于完整性原因,这里是相应的代码
P = sym.symbols('P')
sol = sym.solve(H_I(P) - H_II(P) , P)
sol = [x.evalf(chop=True) for x in sol]
但是请注意,如果一个人也会实现真正的“切断”,那么这与我的第一种方法并没有太大不同。但不同之处在于:您对此实施的准确性一无所知。 如果你从不使用任何其他多项式,它可能没问题。以下代码应说明问题:
def H_0(P):
return P**2 - 10**(-40)
P = sym.symbols('P')
sol = sym.solve(H_0(P) , P)
sol_full = [x.evalf() for x in sol]
sol_chop = [x.evalf(chop=True) for x in sol]
print(sol_full)
print(sol_chop)
即使你的根完全没用并且在使用evalf()之后仍然准确,它们会被切断,因为它们太小了。这就是为什么我会一直建议使用最简单,最通用的解决方案。之后,查看多项式并了解所需的数值精度。
答案 1 :(得分:0)
让我们从第一个结果开始:
sol = sym.solve(H_I(P) - H_II(P) , P)
print 'sol_ H_I(P) - H_II(P) =', sol
打印以下内容:
[ - 6.32436145176552 + 0.e-19 * I,1.79012202335501 + 0.e-19 * I,34.4111917095165 - 0.e-20 * I]
我完全同意您的evalf()
策略,因为它可以提高精确度:
evalf_result = [x.evalf() for x in sol]
print '[x.evalf() for x in sol] = ', evalf_result
产生:
[ - 6.32436145176552 + 1.0842021724855e-19 * I,1.79012202335501 + 1.0842021724855e-19 * I,34.4111917095165 - 1.35525271560688e-20 * I]
你的解决方案意味着使用complex
python的内置函数,它将上面的结果转换为更好的元组结果,其中“I”符号被“j”很好地替换:
complex_evalf_result = complex(x.evalf()) for x in sol
print 'complex(x.evalf()) for x in sol = ', complex_evalf_result
产生以下结果:
[( - 6.324361451765517 + 1.0842021724855044e-19j),(1.7901220233550066 + 1.0842021724855044e-19j),(34.41119170951654-1.3552527156068805e-20j)]
由于type(complex_evalf_result)
返回complex
,您现在可以使用complex_evalf_result.real
策略获取x
中每个complex_evalf_result
的真实部分。这是你的策略,我同意。
一旦应用了evalf
和complex
函数,那么您现在实现了is_close
函数方法,我发现它非常有趣:
“如果实部和复杂部分之间的abs差异小于10E-10,则丢弃复杂部分。”
对于复杂部分小于10E-10的情况,这通常是正确的。例如,对于
[( - 6.324361451765517 + 1.0842021724855044e-19j),(1.7901220233550066 + 1.0842021724855044e-19j),(34.41119170951654-1.3552527156068805e-20j)]
碰巧:
abs(-6.324361451765517+1.0842021724855044e-19j - (-6.324361451765517)) =
1.0842021724855044e-19
,
总是小于10E-10。
你的功能实际上是丢弃了复杂的部分(请原谅我,如果还有另一个应用程序这个功能,我有点傻到不理解它。)
那么,为什么不使用这个更简单的解决方案?
import numpy as np
import sympy as sym
from sympy.functions import re
# Crude intersection:
sol = sym.solve(H_I(P) - H_II(P) , P)
print 'sol_ H_I(P) - H_II(P) =', sol
print """
"""
# Use of evalf to obtain better precision:
evalf_result = [x.evalf() for x in sol]
print '[x.evalf() for x in sol] = ', evalf_result
print """
"""
# Now, let's grab the real part of the evalf_result:
real_roots = []
for x in evalf_result:
each_real_root = re(x)
real_roots.append(each_real_root)
print 'real_roots = ', real_roots
直接打印:
real_roots = [-6.32436145176552,1.79012202335501,34.4111917095165]
通过遵循此策略,它会发生:
1)无需调用python的complex
内置策略。 evalf
函数完成作业后,可以通过re(x)
简单地提取实部。
2)没有必要通过is_close
函数传递我们的交集结果只是为了丢弃复杂的部分。
请告诉我,如果有什么是我误解的,或者你不太同意的事情 - 我很乐意讨论:)你所有的帮助都很棒,非常感谢!
答案 2 :(得分:0)
要查找多项式的根,请使用专用方法roots而不是通用solve
。
sol = sym.roots(H_I(P) - H_II(P) , P)
这将根作为具有多重性的字典返回,
{-6.32436145176552: 1, 1.79012202335501: 1, 34.4111917095165: 1}
获取根列表通常更方便(多个根,如果有的话,将重复):
sol = sym.roots(H_I(P) - H_II(P) , P, multiple=True)
返回[-6.32436145176552, 1.79012202335501, 34.4111917095165]
如果这个等式不是多项式,我建议使用像fsolve
这样的SciPy求解器而不是SymPy。 SymPy不是找到充满浮点系数的方程的数值解的正确工具。它被设计用于符号数学,符号数学和浮点数不能很好地混合。