是否可向量化scipy.optimize.fminbound以快速计算轨迹点到拟合曲线最短距离
性能优化方案
你当前的性能瓶颈来自Python原生for循环的开销,以及scipy.optimize.fminbound仅支持标量输入的特性,针对你的场景有两类高性价比优化方案:
方案1:Numba JIT编译加速(改造成本最低)
不需要修改原有核心逻辑,仅需通过Numba编译核心函数和循环,速度可提升100~1000倍,完全满足数千次计算的需求:
首先安装依赖:pip install numba
代码改写示例:
import numpy as np import pandas as pd from numba import jit # 原有数据、拟合逻辑保持不变 data = np.array( [ # 原有数据内容不变,此处省略 ], dtype=[ ('x', 'f8'), ('y', 'f8'), ('t', 'f8'), ('dmin', 'f8'), ] ) coeffs = np.polyfit( data['t'], pd.DataFrame(data[['x', 'y']]).values, 3 ) # 适配Numba编译的核心函数 @jit(nopython=True) def curve(t, coeffs): x = coeffs[0,0]*t**3 + coeffs[1,0]*t**2 + coeffs[2,0]*t + coeffs[3,0] y = coeffs[0,1]*t**3 + coeffs[1,1]*t**2 + coeffs[2,1]*t + coeffs[3,1] return x, y @jit(nopython=True) def distance(t, px, py, coeffs): x, y = curve(t, coeffs) return np.hypot(x - px, y - py) # 编译整个计算循环,内置黄金分割搜索替代fminbound(适配Numba编译) @jit(nopython=True) def calc_all_dmin(data, coeffs, t_low=-50, t_high=6659.525): n = len(data) dmin_res = np.zeros(n, dtype=np.float64) gr = (np.sqrt(5) - 1) / 2 # 黄金分割比例 for i in range(n): px = data['x'][i] py = data['y'][i] # 黄金分割搜索最优t a, b = t_low, t_high c = b - gr * (b - a) d = a + gr * (b - a) fc = distance(c, px, py, coeffs) fd = distance(d, px, py, coeffs) # 迭代50次,精度远高于默认fminbound要求 for _ in range(50): if fc < fd: b = d d = c fd = fc c = b - gr * (b - a) fc = distance(c, px, py, coeffs) else: a = c c = d fc = fd d = a + gr * (b - a) fd = distance(d, px, py, coeffs) t_opt = (a + b) / 2 dmin_res[i] = distance(t_opt, px, py, coeffs) return dmin_res # 直接调用计算 data['dmin'] = calc_all_dmin(data, coeffs)
该方案实现成本极低,计算数千个点的耗时基本在毫秒级。
方案2:多项式求根法(性能最优)
由于你拟合的是三次参数曲线,最短距离的必要条件是点到曲线上点的连线与曲线切线垂直,对应的距离导数方程为五次多项式,直接求根即可得到所有极值点,筛选区间内的根计算距离取最小值即可,完全不需要迭代搜索,性能比方案1还高2~5倍,适合超大规模数据场景:
# 预计算x、y方向多项式的导数系数 dx_coeffs = coeffs[:, 0] * np.array([3, 2, 1, 0]) dy_coeffs = coeffs[:, 1] * np.array([3, 2, 1, 0]) def get_min_distance(px, py): # 构造距离导数对应的五次多项式系数 poly_x = np.poly1d(coeffs[:, 0]) poly_y = np.poly1d(coeffs[:, 1]) poly_dx = np.poly1d(dx_coeffs) poly_dy = np.poly1d(dy_coeffs) # 距离平方导数 = 2*(x(t)-px)*dx(t) + 2*(y(t)-py)*dy(t) deriv_poly = 2 * ((poly_x - px) * poly_dx + (poly_y - py) * poly_dy) # 求所有实根 roots = deriv_poly.roots # 筛选区间内的实根,加上区间端点 valid_ts = roots[(roots.real >= -50) & (roots.real <= 6659.525) & (np.abs(roots.imag) < 1e-6)].real valid_ts = np.append(valid_ts, [-50, 6659.525]) # 计算所有候选t对应的距离取最小值 dists = np.hypot(poly_x(valid_ts) - px, poly_y(valid_ts) - py) return dists.min() # 批量计算可以结合np.vectorize或者numba编译批量求根
内容的提问来源于stack exchange,提问作者mapf
相关产品推荐
相关产品推荐

