如何从sympy.diff提取函数适配scipy.fsolve?导数根求解提速
问题分析与解决方案
你遇到的核心问题是:sympy.solve会尝试求解所有解析根,导致速度极慢,但你只需要靠近x0的单个数值根;而用scipy.fsolve时报错,是因为sympy的表达式对象(如Mul)不能直接作为可调用函数传入。
以下是几种高效的解决方法:
方案1:用sympy.lambdify转换表达式适配scipy.fsolve
把sympy的符号表达式转换成numpy兼容的可调用函数,就能直接给fsolve使用。同时可以优化求导逻辑,避免开根号简化计算:
import sympy from scipy.optimize import fsolve def diff_dist_func(a, b, c): x = sympy.Symbol('x') x0 = sympy.Symbol('x0') y0 = sympy.Symbol('y0') # 优化:直接对距离平方求导,和原距离导数为0的根完全一致,避免开根号 dist_sq = (x - x0) ** 2 + (a * x ** 3 + b * x ** 2 + c * x - y0) ** 2 return sympy.diff(dist_sq, x) a = -0.00020129919480721813 b = 0.10107634020780536 c = -12.305150031126267 shortest_dist = diff_dist_func(a, b, c) x0_val = 252.3007982720215 y0_val = 96.55526056735049 # 代入x0、y0的数值,保留x作为变量的符号表达式 expr = shortest_dist.subs({'x0': x0_val, 'y0': y0_val}) # 将符号表达式转成可调用的数值函数 func = sympy.lambdify(sympy.Symbol('x'), expr, 'numpy') # 用fsolve求解,初始值设为x0_val,快速得到附近的根 solve_x = fsolve(func, x0_val) print(solve_x)
方案2:直接用sympy.nsolve求数值解
如果不想依赖scipy,sympy自带的nsolve支持指定初始值,专门求解单个数值根,速度远快于solve:
import sympy def diff_dist_func(a, b, c): x = sympy.Symbol('x') x0 = sympy.Symbol('x0') y0 = sympy.Symbol('y0') dist_sq = (x - x0) ** 2 + (a * x ** 3 + b * x ** 2 + c * x - y0) ** 2 return sympy.diff(dist_sq, x) a = -0.00020129919480721813 b = 0.10107634020780536 c = -12.305150031126267 shortest_dist = diff_dist_func(a, b, c) x0_val = 252.3007982720215 y0_val = 96.55526056735049 expr = shortest_dist.subs({'x0': x0_val, 'y0': y0_val}) x = sympy.Symbol('x') # 指定初始值x0_val,直接求解附近的根 solve_x = sympy.nsolve(expr, x, x0_val) print(solve_x)
关键说明
- 为什么原代码报错:你用
evalf(subs=...)得到的是sympy的数值表达式对象(如Mul),这不是可调用函数,fsolve需要的是能接受数值参数并返回数值的函数,lambdify就是做这个转换的。 - 求导优化的原因:原距离函数的导数为0,等价于距离平方的导数为0(因为距离是正数,链式法则推导可得),去掉根号后sympy的计算量大幅降低,求导和后续处理都更快。
内容的提问来源于stack exchange,提问作者xc-2021
相关产品推荐
相关产品推荐

