使用Python实现整数参数复杂函数最小化,解决吉他调弦优化问题
最优解决方案推荐
你当前的场景参数搜索空间极小,完全不需要使用线性规划求解器,也不需要手动实现复杂优化算法,优先选择暴力枚举即可,实现成本最低,耗时也可以忽略。
方案1:暴力枚举(最适合当前场景)
你的5个偏移参数o1-o5如果取值范围是-5到5,总共有11^5=16万种左右的组合,现代CPU跑完所有组合不到1秒,直接遍历即可,代码示例如下:
import itertools import numpy as np from scipy import stats # 和弦定义 d=np.array([5,7,7,4,5,np.nan]) bm=np.array([7,7,7,np.nan,5,7]) bm2=np.array([7,7,8,np.nan,6,7]) em=np.array([7,8,9,5,7,np.nan]) a7=np.array([5,5,6,5,0,np.nan]) d2=np.array([5,7,7,7,9,np.nan]) gm=np.array([6,8,7,8,10,np.nan]) g=np.array([7,8,7,np.nan,2,3]) a72=np.array([5,5,6,2,4,np.nan]) d3=np.array([5,7,7,7,5,np.nan]) chords = np.array([d,bm, bm2, em, a7, d2, gm, g, a72, d3]) # 得分计算函数(已修正原代码中stats.mode的调用错误) def scorechord(o1, o2, o3, o4, o5): newchords = np.empty([10,6]) score=0 for i in range(len(chords)): newchords[i][0]=chords[i][0]+o1 newchords[i][1]=chords[i][1]+o2 newchords[i][2]=chords[i][2]+o3 newchords[i][3]=chords[i][3]+o4 newchords[i][4]=chords[i][4]+o5 newchords[i][5]=chords[i][5] for i in newchords: imin=np.nanmin(i) imax=np.nanmax(i) if imax-imin>4 and imin!=0 and imax!=0: score+=100 # 原代码此处写的stats.mode(i[0])是错误的,i[0]是单个数值,求众数无意义 if stats.mode(i, keepdims=True)[0][0]!=imin: score+=10 for j in range(5): if not np.isnan(abs(i[j+1]-i[j])): score+=abs(i[j+1]-i[j]) return score # 定义参数取值范围,可根据你的实际需求调整 param_range = range(-5, 6) best_score = float('inf') best_params = None # 遍历所有参数组合 for o1, o2, o3, o4, o5 in itertools.product(param_range, repeat=5): current_score = scorechord(o1, o2, o3, o4, o5) if current_score < best_score: best_score = current_score best_params = (o1, o2, o3, o4, o5) print(f"最优调弦偏移:o1={best_params[0]}, o2={best_params[1]}, o3={best_params[2]}, o4={best_params[3]}, o5={best_params[4]}") print(f"最优得分:{best_score}")
另外你之前PuLP代码里把每个参数的lowBound和upBound都设为了-5,相当于所有参数固定为常数,属于笔误,根据实际需求调整上面的param_range即可。
方案2:黑盒整数优化(适用于后续参数范围扩大的场景)
如果后续你增加了参数数量,或者参数范围扩大到暴力枚举效率太低,可以直接使用支持整数约束的黑盒优化工具,不需要改写你的目标函数,比如Scipy自带的differential_evolution:
from scipy.optimize import differential_evolution # 每个参数的取值边界 bounds = [(-5, 5), (-5, 5), (-5, 5), (-5, 5), (-5, 5)] # 指定所有参数都是整数 integrality = [True, True, True, True, True] # 适配为接受单个数组入参的目标函数 def objective(params): o1, o2, o3, o4, o5 = params return scorechord(o1, o2, o3, o4, o5) # 执行优化 result = differential_evolution(objective, bounds, integrality=integrality, seed=42) print("最优调弦偏移:", result.x) print("最优得分:", result.fun)
PuLP报错的原因说明
PuLP是线性规划求解器,仅支持线性的目标函数和约束,你的得分函数里包含求最值、求绝对值、条件判断等非线性逻辑,PuLP无法直接识别。如果要强制适配PuLP,需要用大量辅助变量和线性约束改写所有非线性逻辑,实现成本极高,对你的场景完全没有必要。
内容的提问来源于stack exchange,提问作者kciur
相关产品推荐
相关产品推荐

