Scipy求解任意多边形内最大椭圆失败原因咨询
从你描述的问题来看,代码运行失败(Success: False)主要是约束条件的逻辑错误,还有目标函数与约束的定义混淆,下面一步步拆解问题:
1. 核心约束完全错误:椭圆方程的约束写偏了
你写的约束 A**2*(x-x0)**2+B**2*(y-y0)**2 = 0 根本不是椭圆的有效约束——这个等式只有当 (x,y)=(x0,y0)(椭圆中心)时才成立,相当于把优化问题的可行域直接限制在了一个点上,优化器自然找不到符合所有条件的解,直接返回失败。
正确的逻辑应该是:椭圆上的所有点都必须落在你定义的多边形内部。如果你的变量是 A=1/a、B=1/b(a、b 是椭圆的x、y方向半轴长),那椭圆的标准约束应该是 A²(x-x0)² + B²(y-y0)² ≤ 1。但在优化问题中,不能直接对所有点做约束,我们需要把它转化为椭圆与多边形每条边的位置关系约束——即椭圆上的点到每条边的最大“偏移”不能超出边界,具体推导见下文。
2. 多边形约束的不等式方向需要统一
你给出的四个多边形边界不等式方向混乱,先统一为“所有内部点满足不等式 ≤ 0”的形式,方便后续处理:
- 原
x/100.0 + y/80.0 < 1→4x + 5y - 400 ≤ 0 - 原
x/-20.0 + y/80.0 < 1→-4x + y - 80 ≤ 0 - 原
x/-40.0 + y/-40.0 > 1→x + y + 40 ≤ 0(两边乘-1,不等号反转) - 原
x/100.0 + y/-60.0 > 1→-3x + 5y + 300 ≤ 0(同样乘-1反转符号)
先确认这四个不等式围成的是一个有效的四边形(四个交点分别是 (0,80)、(-24,-16)、(12.5,-52.5)、(100,0)),这部分是没问题的。
3. 正确的优化问题构建方式
要最大化椭圆面积,椭圆面积是 πab,所以等价于最大化 ab(忽略常数π)。如果坚持用 A=1/a、B=1/b,那目标函数就是最小化 AB(因为 ab=1/(AB),最小化AB等价于最大化ab),这部分你的逻辑是对的。
关键是约束条件的正确写法:对于每条多边形边的线性函数 L_i(x,y) = p_i x + q_i y + r_i ≤ 0,椭圆上的点满足 L_i(x,y) ≤ 0 的等价约束是:
L_i(x0, y0) + sqrt( (p_i * a)^2 + (q_i * b)^2 ) ≤ 0
(或者用你的变量 A=1/a、B=1/b,转化为 L_i(x0,y0) + sqrt( (p_i/A)^2 + (q_i/B)^2 ) ≤ 0)
这个推导来自于求线性函数在椭圆约束下的最大值——最大值不能超过0,否则椭圆上就有部分点落在多边形外了。
4. 代码层面的潜在问题
除了约束逻辑错误,你用Scipy优化时还要注意:
- 变量的初始值要合理:比如中心
(x0,y0)可以设为多边形的重心,a、b设为一个较小的正数(比如10),避免优化器一开始就进入不可行域。 - 选择合适的优化器:Scipy的
SLSQP适合处理带约束的非线性优化,是这类问题的常用选择,但要确保约束的类型(不等式约束)设置正确。
举个简化的代码框架(用a、b作为变量):
import numpy as np from scipy.optimize import minimize # 多边形四条边的线性函数参数 (p, q, r),满足 p*x + q*y + r ≤0 edges = [ (4, 5, -400), (-4, 1, -80), (1, 1, 40), (-3, 5, 300) ] # 目标函数:最大化ab → 最小化 -ab def objective(vars): x0, y0, a, b = vars return -a * b # 约束条件:每条边对应的不等式约束 def constraints(vars): x0, y0, a, b = vars cons = [] for p, q, r in edges: # L_i(x0,y0) + sqrt( (p*a)^2 + (q*b)^2 ) ≤0 val = p*x0 + q*y0 + r + np.sqrt( (p*a)**2 + (q*b)**2 ) cons.append(val) return cons # 初始猜测:多边形重心,a、b设为20 centroid = np.array([(0-24+12.5+100)/4, (80-16-52.5+0)/4]) x0_init, y0_init = centroid a_init, b_init = 20, 20 initial_guess = [x0_init, y0_init, a_init, b_init] # 优化设置:所有约束都是 ≤0,Scipy的ineq要求fun≥0,所以取反 cons = ({'type': 'ineq', 'fun': lambda vars: -constraints(vars)}) bounds = [(-50, 110), (-60, 90), (1, 100), (1, 100)] # 变量的合理范围 result = minimize(objective, initial_guess, method='SLSQP', bounds=bounds, constraints=cons) print(result)
这个框架应该能得到可行的解,你可以对比自己的代码,看看是不是约束部分写错了。
内容的提问来源于stack exchange,提问作者MJ.Shin

