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

如何使用SciPy minimize求解非可逆函数ak的数值逆函数?

如何使用SciPy minimize求解非可逆函数ak的数值逆函数?

我来给你梳理一下具体的实现思路和代码示例,应该能帮你搞定这个问题~

首先,因为你的ak函数是非可逆的,我们没法直接写出解析逆函数,只能用数值优化的思路:构造一个损失函数,衡量ak(u,v,w)和目标球面点的差异,然后用SciPy的minimize找到满足八面体约束的[u,v,w],让这个差异最小。

具体步骤拆解

  1. 定义损失函数
    我们的目标是让ak(u,v,w)尽可能接近输入的球面点[x,y,z],可以用两者的平方欧氏距离作为损失(平方的好处是求导更方便,优化效率更高):
def loss(oct_pt, target_sphere_pt):
    sphere_pt = ak(oct_pt)
    return np.sum((sphere_pt - target_sphere_pt)**2)
  1. 处理约束条件
    输入点必须在单位八面体表面,也就是满足|u| + |v| + |w| = 1。另外,从ak函数的实现来看,tan(np.pi * p / 2)要求p的每个分量不能取到±1(否则tan会无穷大),实际优化时只要初始值在(-1,1)区间,求解器会自动避开边界。

我们把这个约束用SciPy支持的格式定义:

constraints = [
    {'type': 'eq', 'fun': lambda p: np.sum(np.abs(p)) - 1}
]
  1. 初始化猜测值
    优化的初始值很关键,选得好能大大提升收敛速度和结果准确性。我们可以把目标球面点直接投影到八面体上作为初始点,也就是将球面点归一化到L1范数为1:
def sphere_to_oct_initial_guess(sphere_pt):
    l1_norm = np.sum(np.abs(sphere_pt))
    return sphere_pt / l1_norm
  1. 调用SciPy minimize求解
    选择支持等式约束的SLSQP求解器,这是处理这类带约束优化问题的常用选择。

完整代码示例

import numpy as np
from scipy.optimize import minimize

def ak(p):
    # Convert point on octahedron onto the sphere.
    a = 3.227806237143884260376580641604959964752197265625
    p1 = (np.pi * p) / 2.0
    tp1 = np.tan(p1)
    xu, xv, xw = tp1[0], tp1[1], tp1[2]
    u2, v2, w2 = xu ** 2, xv ** 2, xw ** 2
    y0p = xu * (v2 + w2 + a * w2 * v2) ** 0.25
    y1p = xv * (u2 + w2 + a * u2 * w2) ** 0.25
    y2p = xw * (u2 + v2 + a * u2 * v2) ** 0.25
    pv = np.array([y0p, y1p, y2p])
    return pv / np.linalg.norm(pv, keepdims=True)

def loss(oct_pt, target_sphere_pt):
    sphere_pt = ak(oct_pt)
    return np.sum((sphere_pt - target_sphere_pt)**2)

def sphere_to_oct_initial_guess(sphere_pt):
    l1_norm = np.sum(np.abs(sphere_pt))
    return sphere_pt / l1_norm

def ak_inverse(sphere_pt):
    # 初始猜测值
    initial_guess = sphere_to_oct_initial_guess(sphere_pt)
    # 约束条件:|u|+|v|+|w|=1
    constraints = [{'type': 'eq', 'fun': lambda p: np.sum(np.abs(p)) - 1}]
    # 变量边界:每个分量在(-1,1)之间,避免tan出现无穷大
    bounds = [(-0.9999, 0.9999) for _ in range(3)]
    # 调用minimize
    result = minimize(loss, initial_guess, args=(sphere_pt,),
                      method='SLSQP', constraints=constraints, bounds=bounds)
    # 返回最优解
    return result.x

# 测试示例
if __name__ == "__main__":
    # 生成一个八面体点,用ak转成球面点,再求逆验证
    test_oct = np.array([0.5, 0.3, 0.2])  # 满足|0.5|+|0.3|+|0.2|=1
    test_sphere = ak(test_oct)
    print("原始八面体点:", test_oct)
    print("转换后的球面点:", test_sphere)
    
    # 求逆
    inv_oct = ak_inverse(test_sphere)
    print("逆函数得到的八面体点:", inv_oct)
    print("验证:ak(inv_oct)与目标球面点的误差:", np.sum((ak(inv_oct)-test_sphere)**2))
    print("验证:逆点是否满足八面体约束:", np.isclose(np.sum(np.abs(inv_oct)), 1))

一些注意事项

  • 初始值的重要性:如果直接用随机初始值,可能会收敛到局部最优或者不收敛,用球面点投影到八面体的初始值,利用了两个曲面的对应关系,收敛会更稳定。
  • 局部最优问题:因为ak函数可能不是单射,同一个球面点可能对应多个八面体点。如果需要找到所有可能的解,可以尝试用不同的初始值(比如利用八面体的对称性,对初始值的分量取正负号后再优化),然后筛选出误差最小的结果。
  • 求解器选择:除了SLSQP,你也可以尝试COBYLA求解器,它同样支持约束条件,对于某些问题可能有更好的表现。
  • 边界处理:我们给变量加了(-0.9999, 0.9999)的边界,避免tan(np.pi*p/2)出现无穷大的情况,实际优化时求解器会自动避开这些极端值。

备注:内容来源于stack exchange,提问作者Konchog

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 14:29:35