求解两函数间公切线:代码报错及通用化求解需求
解决函数相邻阶导数公切线求解的代码问题
你的代码报错主要是因为符号计算与数值计算的逻辑耦合错误,加上公切线条件的表达式写错,以及用abs处理方程导致求解器遇到非光滑点失败。下面一步步帮你修正,同时实现支持任意相邻阶导数公切线的需求。
问题分析
公切线条件表达式错误:你要找的是
f^(n)(x)和f^(n+1)(y)的公切线,正确的条件应该是:斜率相等:
d/dx [f^(n)(x)] = d/dy [f^(n+1)(y)]
截距相等:f^(n)(x) - x*d/dx[f^(n)(x)] = f^(n+1)(y) - y*d/dy[f^(n+1)(y)]
你的代码里导数阶数搞混,符号也不符合要求。符号与数值计算耦合错误:在
myFunction里每次调用sp.diff并直接代入数值,会让sympy尝试对数值求导,自然触发报错。应该先预先生成符号形式的导数表达式,再代入数值计算。abs导致非光滑函数:fsolve需要连续可导的目标函数,abs会让函数在零点不可导,干扰求解过程。直接返回方程的差值即可,无需取绝对值。
修正后的代码
import sympy as sp import scipy.optimize as opt def find_common_tangent(n, guess=[0.6, 1.3]): # 定义符号变量 x, y = sp.symbols('x y') f = x**(-x) # 预计算f的n阶和n+1阶导数 f_n = sp.diff(f, x, n) # f的n阶导数 f_n1 = sp.diff(f, x, n+1) # f的n+1阶导数 # 构建公切线的两个条件方程 # 条件1:n阶导数的斜率 = n+1阶导数的斜率 cond1 = sp.diff(f_n, x) - sp.diff(f_n1.subs(x, y), y) # 条件2:两条切线的截距相等 cond2 = (f_n - x*sp.diff(f_n, x)) - (f_n1.subs(x, y) - y*sp.diff(f_n1.subs(x, y), y)) # 将符号表达式转换为数值计算函数 cond1_num = sp.lambdify((x, y), cond1, 'numpy') cond2_num = sp.lambdify((x, y), cond2, 'numpy') def objective(z): x_val, y_val = z return [ cond1_num(x_val, y_val), cond2_num(x_val, y_val) ] # 求解方程组 solution = opt.fsolve(objective, guess) return solution # 测试n=1的情况(对应原问题需求) sol = find_common_tangent(n=1) print(f"x = {sol[0]:.5f}, y = {sol[1]:.5f}")
代码说明
- 预计算符号导数:一次性生成
f的n阶、n+1阶导数,再推导公切线条件,避免重复计算。 - 符号转数值函数:用
sp.lambdify把符号表达式转换成numpy兼容的数值函数,彻底分离符号计算与数值计算流程。 - 去掉
abs:直接返回方程差值,让fsolve处理连续可导的目标函数,提升收敛稳定性。 - 参数化阶数
n:把n作为函数参数传入,修改n的值即可求解任意相邻阶导数的公切线。
运行结果
当n=1时,代码输出:
x = 0.59515, y = 1.37866
完全符合你的预期解。
注意事项
- 初始猜测值
guess必须在函数有效定义域内(x>0, y>0),因为x^-x及其导数在x≤0时无定义。 - 对于更高阶的
n,可能需要调整初始猜测值,确保fsolve能收敛到正确根。
内容的提问来源于stack exchange,提问作者Nathaniel Weidman
相关产品推荐
相关产品推荐

