牛顿法未返回定义域内全部收敛根的问题排查与优化咨询
牛顿法未返回定义域内全部收敛根的问题排查与优化咨询
嘿,我来帮你捋捋这个问题——你的代码逻辑其实犯了几个关键错误,导致你没法拿到所有收敛的根,甚至会出现奇怪的结果,咱们一步步拆解:
首先,你的批量初始点处理逻辑完全错了
你现在的代码把所有初始点当成一个整体来迭代,还要求所有点同时收敛才返回结果,这完全不符合牛顿法对批量初始点的处理方式啊!
举个例子:你在(-2,2)里取的初始点,有的会收敛到0,有的会收敛到π(属于牛顿法的吸引域问题),但你的代码会检查np.all(adz < TOL)——也就是所有点的误差都要小于阈值才返回。这显然不可能,因为收敛到0和π的点进度不一样,所以代码会一直迭代到最大次数,要么返回False,要么因为某些点迭代过度,反而跑到更远的根(比如±3π)去了。
而且你把所有点放在同一个数组里统一更新,这相当于把不同收敛路径的点强行混在一起处理,完全违背了每个初始点独立迭代的原则。
其次,代码里还有几个细节问题
- 收敛判断逻辑错误:应该逐个点判断是否收敛,标记已收敛的点停止迭代,而不是要求所有点同时达标。
- 未处理迭代中的导数为0情况:你只在最开始筛选了导数不为0的点,但迭代过程中某个点可能走到导数为0的位置,直接会导致除以0的报错。
- 没有根去重机制:不同初始点会收敛到同一个根,你需要对结果去重才能得到唯一的根列表。
改进后的代码示例
我给你改了一个适合批量初始点的版本,每个点独立跟踪收敛状态,最后返回所有去重后的收敛根:
import numpy as np def newton_batch(z0, f, fp, MAX_IT): TOL = 1e-10 z = np.copy(z0) # 标记哪些点还在迭代中 active = np.ones(z.shape, dtype=bool) roots = [] for i in range(MAX_IT): # 只处理还没收敛的点 z_active = z[active] if len(z_active) == 0: break # 所有点都收敛了,提前退出 # 过滤掉导数接近0的点(避免除以0) fp_active = fp(z_active) valid = np.abs(fp_active) > 1e-12 # 用小阈值代替严格等于0 z_valid = z_active[valid] fp_valid = fp_active[valid] # 计算牛顿更新量 dz = f(z_valid) / fp_valid z_valid -= dz # 更新原数组里的有效点 z[active][valid] = z_valid # 检查哪些点已经收敛 converged = np.abs(dz) < TOL # 收集新收敛的根 new_roots = z_valid[converged] roots.extend(new_roots) # 更新活跃标记:收敛的点不再参与后续迭代 active[active] &= ~np.concatenate([converged, np.ones(len(z_active) - len(z_valid), dtype=bool)]) # 对根去重(考虑浮点误差,保留8位小数后去重) if roots: roots = np.array(roots) unique_roots = np.unique(np.round(roots, decimals=8)) return unique_roots else: return np.array([])
关于牛顿法本身的限制(你提到的多项式问题)
即使代码改对了,也要明白:牛顿法不是万能的,它有吸引域的限制。每个根都有自己的吸引域,只有初始点落在某个根的吸引域内,才会收敛到那个根。比如多项式的牛顿分形,吸引域是分形结构,有些区域的初始点可能会发散到无穷,或者在几个根之间震荡。
如果想要覆盖所有根,可以结合其他方法:比如先用二分法(针对实根)或者其他全局寻根方法找到根的大致位置,再用牛顿法细化;或者用足够密集的初始点覆盖定义域,确保每个根的吸引域都被命中。
备注:内容来源于stack exchange,提问作者fractal_girl
相关产品推荐
相关产品推荐

