You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用scipy least_squares约束变量避免sqrt返回虚数及参数范围

解决方案

一、解决根号内负数问题(避免虚数报错)

你之前的残差函数存在两个关键问题:一是循环判断负数时直接返回布尔值,不符合残差需为数组的要求;二是惩罚项设计不合理,无法有效引导优化器避开非法参数组合。以下是两种可行方案:

方案1:残差中添加惩罚项(推荐)

先通过np.maximum将根号内参数限制为极小正数,避免计算平方根时出错;再对根号内为负的位置添加大惩罚,让优化器主动避开这类参数组合:

import numpy as np
from scipy.optimize import least_squares

def base(r, c, K):
    arg = 1 - (K+1)*(c*r)**2
    # 替换负数为极小正数,防止sqrt报错
    safe_arg = np.maximum(arg, 1e-8)
    return c * r**2 / (1 + np.sqrt(safe_arg))

def base_residual(toFit, r, trueSag):
    c, K = toFit
    arg = 1 - (K+1)*(c*r)**2
    
    # 计算基础残差
    residual = base(r, c, K) - trueSag
    # 给根号内负数的位置添加惩罚
    penalty = np.where(arg < 0, 1e6 * np.abs(arg), 0)
    residual += penalty
    
    return residual

方案2:变量替换转化约束

将根号内的约束条件转化为参数的范围约束:观察1 - (K+1)*(c*r)^2 > 0,可令(K+1)*c² = t²(t为实数),此时约束变为(t*r)^2 < 1,结合数据的r范围可进一步限制t的取值。不过这种方法会增加参数维度,实现复杂度更高,不如惩罚项方案简洁。

二、参数范围约束(-90<K<90,c>0.1或c<-0.1)

由于c的约束是不连续区间,无法直接用least_squares的bounds参数,可通过以下两种方式处理:

方案1:残差中添加参数约束惩罚

对违反参数范围的情况添加大惩罚,引导优化器选择合法参数:

def base_residual(toFit, r, trueSag):
    c, K = toFit
    param_penalty = 0
    
    # K的范围约束惩罚
    if K <= -90 or K >= 90:
        param_penalty += 1e6 * np.abs(K - np.clip(K, -90, 90))
    # c的范围约束惩罚(避开-0.1到0.1区间)
    if -0.1 <= c <= 0.1:
        # 惩罚值取到最近合法区间的距离乘以系数
        param_penalty += 1e6 * min(np.abs(c - 0.1), np.abs(c + 0.1))
    
    arg = 1 - (K+1)*(c*r)**2
    safe_arg = np.maximum(arg, 1e-8)
    residual = c * r**2 / (1 + np.sqrt(safe_arg)) - trueSag
    
    # 根号内负数惩罚
    penalty = np.where(arg < 0, 1e6 * np.abs(arg), 0)
    residual += penalty
    # 将参数惩罚扩展为与残差同维度的数组
    residual += param_penalty * np.ones_like(residual)
    
    return residual

方案2:拆分两次拟合(更可靠)

分别针对c>0.1和c<-0.1两个区间进行拟合,最终选择残差最小的结果:

# 拟合c>0.1的情况
bounds_pos_c = ([0.1, -90], [np.inf, 90])
fit_pos = least_squares(base_residual, [0.2, 0], args=(r_points, truesurf), bounds=bounds_pos_c)

# 拟合c<-0.1的情况
bounds_neg_c = ([-np.inf, -90], [-0.1, 90])
fit_neg = least_squares(base_residual, [-0.2, 0], args=(r_points, truesurf), bounds=bounds_neg_c)

# 选择最优结果
best_fit = fit_pos if fit_pos.cost < fit_neg.cost else fit_neg

三、降低初始猜测的依赖性

通过生成多个随机初始点,选择最优拟合结果:

num_trials = 5
best_cost = float('inf')
best_result = None

for _ in range(num_trials):
    # 随机生成符合约束的初始猜测
    K_guess = np.random.uniform(-90, 90)
    c_guess = np.random.uniform(0.1, 10) if np.random.rand() > 0.5 else np.random.uniform(-10, -0.1)
    init_guess = [c_guess, K_guess]
    
    current_fit = least_squares(base_residual, init_guess, args=(r_points, truesurf), verbose=0)
    if current_fit.cost < best_cost:
        best_cost = current_fit.cost
        best_result = current_fit

print("最优拟合参数:", best_result.x)

内容的提问来源于stack exchange,提问作者user22191622

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 18:32:07