基于c++ eigen的Nelder-Mead算法(仿照scipy)
<h1 id="简介">简介</h1><p>本文展示了用C++(Eigen)实现的Nelder-Mead算法,该实现仿照了Python SciPy库中的scipy.optimize.fmin函数。虽然目前仅完成了基础功能(fmin不支持full_output和retall),但已经可以应用于实际优化问题。</p>
<h1 id="nelder-mead算法简介">Nelder-Mead算法简介</h1>
<p>Nelder-Mead算法(也称单纯形法)是一种常用的无导数优化方法,特别适合于:</p>
<ul>
<li>函数导数难以计算或不存在的情况</li>
<li>寻找非线性函数的局部最小值</li>
<li>维度适中的问题</li>
</ul>
<p>这种算法被广泛应用于机器学习、计算机视觉、信号处理等领域,特别是当目标函数计算成本高昂时尤为有用。</p>
<p>SciPy版本的特点:</p>
<ol>
<li>边界约束支持:SciPy版本添加了对变量边界的支持,通过bounds参数实现。它使用np.clip确保所有单纯形顶点都在指定边界内,这是标准Nelder-Mead算法的扩展。</li>
<li>自适应参数策略:通过adaptive参数启用,基于Gao和Han<br>
2012年的论文。这种策略根据问题维度自动调整反射、扩展和收缩参数,使算法在高维问题上表现更好。</li>
<li>双重收敛条件:同时使用函数值容差(fatol)和参数值容差(xatol)作为终止条件,这比只用单一条件更稳健。</li>
<li>完善的终止处理:支持通过最大迭代次数(maxiter)或最大函数评估次数(maxfev)限制算法运行时间,并提供详细的终止状态。</li>
</ol>
<p>更详细的分析见本文结尾。</p>
<h1 id="代码">代码</h1>
<p>纯头文件库,包含opti.h,opti.inl。还有一个测试程序main.cpp<br>
opti.h:</p>
<pre><code class="language-cpp">#ifndef MY_OPTIMIZE_H
#define MY_OPTIMIZE_H
#include <Eigen/Core>
#include <algorithm>
#include <cmath>
#include <functional>
#include <iostream>
#include <limits>
#include <vector>
// NelderMead算法选项
struct NelderMeadOptions {
double xatol = 1e-4; // x收敛容差
double fatol = 1e-4; // 函数值收敛容差
int maxiter = -1; // 最大迭代次数,-1表示使用默认值
int maxfev = -1; // 最大函数评估次数,-1表示使用默认值
bool disp = false; // 是否显示收敛消息
bool return_all = false; // 是否返回所有迭代的结果
bool adaptive = false; // 是否使用自适应参数
// 边界,每个变量的(min, max)对,std::nullopt表示无边界
std::vector<std::pair<double, double>> bounds;
};
// NelderMead算法结果
struct NelderMeadResult {
Eigen::VectorXd x; // 最优解
double fun; // 最优解对应的函数值
int nit; // 迭代次数
int nfev; // 函数评估次数
int status; // 状态码:0=成功,1=达到最大函数评估次数,2=达到最大迭代次数
bool success; // 是否成功
std::string message; // 状态信息
std::vector<Eigen::VectorXd> allvecs; // 所有迭代的结果(如果return_all=true)
};
/**
* Nelder-Mead单纯形算法
*
* @param func 目标函数
* @param x0 初始猜测点
* @param initial_simplex 初始单纯形,如果提供则覆盖x0
* @param options 算法参数选项
* @param callback 回调函数,每次迭代后调用
* @return 优化结果
*/
template <typename Scalar = double>
NelderMeadResult
minimize_neldermead(std::function<Scalar(const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>&)> func,
const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>& x0,
const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>* initial_simplex = nullptr,
const NelderMeadOptions& options = NelderMeadOptions(),
std::function<bool(const NelderMeadResult&)> callback = nullptr);
/**
* 简化版Nelder-Mead优化函数,类似于scipy的fmin
*
* @param func 目标函数
* @param x0 初始猜测点
* @param initial_simplex 初始单纯形,如果提供则覆盖x0
* @param xtol x收敛容差
* @param ftol 函数值收敛容差
* @param maxiter 最大迭代次数
* @param maxfun 最大函数评估次数
* @param full_output 是否返回完整输出
* @param disp 是否显示收敛消息
* @param retall 是否返回所有迭代的结果
* @param callback 回调函数
* @return 优化结果点或完整结果(取决于full_output)
*/
template <typename Scalar = double>
Eigen::Matrix<Scalar, Eigen::Dynamic, 1>
fmin(std::function<Scalar(const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>&)> func,
const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>& x0,
const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>* initial_simplex = nullptr,
double xtol = 1e-4,
double ftol = 1e-4,
int maxiter = -1,
int maxfun = -1,
bool full_output = false,
bool disp = true,
bool retall = false,
std::function<bool(const NelderMeadResult&)> callback = nullptr);
#include "opti.inl"
#endif //MY_OPTIMIZE_H
</code></pre>
<p>opti.inl:</p>
<pre><code class="language-cpp">#ifndef MY_OPTIMIZE_INL
#define MY_OPTIMIZE_INL
#include <algorithm>
#include <cmath>
#include <iostream>
template <typename Scalar>
NelderMeadResult minimize_neldermead(std::function<Scalar(const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>&)> func,
const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>& x0,
const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>* initial_simplex,
const NelderMeadOptions& options,
std::function<bool(const NelderMeadResult&)> callback) {
using Vector = Eigen::Matrix<Scalar, Eigen::Dynamic, 1>;
using Matrix = Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>;
// 提取选项参数
double xatol = options.xatol;
double fatol = options.fatol;
int maxiter = options.maxiter;
int maxfev = options.maxfev;
bool disp = options.disp;
bool return_all = options.return_all;
bool adaptive = options.adaptive;
const auto& bounds = options.bounds;
// 设置算法参数
double rho, chi, psi, sigma;
if (adaptive) {
double dim = static_cast<double>(x0.size());
rho = 1.0;
chi = 1.0 + 2.0 / dim;
psi = 0.75 - 1.0 / (2.0 * dim);
sigma = 1.0 - 1.0 / dim;
} else {
rho = 1.0;
chi = 2.0;
psi = 0.5;
sigma = 0.5;
}
double nonzdelt = 0.05;
double zdelt = 0.00025;
// 检查边界
bool has_bounds = !bounds.empty();
Vector lower_bound, upper_bound;
if (has_bounds) {
if (bounds.size() != static_cast<size_t>(x0.size())) {
throw std::invalid_argument("Bounds size does not match x0 size");
}
lower_bound(x0.size());
upper_bound(x0.size());
for (int i = 0; i < x0.size(); ++i) {
lower_bound(i) = bounds.first;
upper_bound(i) = bounds.second;
}
if ((lower_bound.array() > upper_bound.array()).any()) {
throw std::invalid_argument("Nelder Mead - one of the lower bounds is greater than an upper bound.");
}
if (((x0.array() < lower_bound.array()) || (x0.array() > upper_bound.array())).any() && disp) {
std::cerr << "Warning: Initial guess is not within the specified bounds" << std::endl;
}
}
// 将点裁剪到边界内
auto clip = [&](const Vector& x) -> Vector {
if (!has_bounds)
return x;
return x.cwiseMax(lower_bound).cwiseMin(upper_bound);
};
// 按函数值排序单纯形顶点
auto sort_simplex = [](Vector& fsim, Matrix& sim) {
const int N = sim.cols();
std::vector<std::pair<Scalar, int>> pairs(N + 1);
for (int i = 0; i < N + 1; ++i) {
pairs = {fsim(i), i};
}
std::sort(pairs.begin(), pairs.end());
// 重排fsim和sim
Vector fsim_sorted(N + 1);
Matrix sim_sorted(N + 1, N);
for (int i = 0; i < N + 1; ++i) {
fsim_sorted(i) = pairs.first;
sim_sorted.row(i) = sim.row(pairs.second);
}
fsim = fsim_sorted;
sim = sim_sorted;
};
// 创建初始单纯形
Vector x0_copy = x0;
if (has_bounds) {
x0_copy = clip(x0_copy);
}
int N = x0_copy.size();
Matrix sim;
if (initial_simplex == nullptr) {
sim = Matrix(N + 1, N);
sim.row(0) = x0_copy;
for (int k = 0; k < N; ++k) {
Vector y = x0_copy;
if (y(k) != 0) {
y(k) = (1.0 + nonzdelt) * y(k);
} else {
y(k) = zdelt;
}
sim.row(k + 1) = y;
}
} else {
if (initial_simplex->rows() != N + 1 || initial_simplex->cols() != N) {
throw std::invalid_argument("`initial_simplex` should be an array of shape (N+1,N)");
}
sim = *initial_simplex;
}
// 如果边界存在,确保所有单纯形顶点都在边界内
if (has_bounds) {
for (int i = 0; i < sim.rows(); ++i) {
Vector vertex = sim.row(i);
sim.row(i) = clip(vertex);
}
}
// 如果既没有设置maxiter也没有设置maxfev,设置它们为默认值
if (maxiter < 0 && maxfev < 0) {
maxiter = N * 200;
maxfev = N * 200;
} else if (maxiter < 0) {
if (maxfev == std::numeric_limits<int>::max()) {
maxiter = N * 200;
} else {
maxiter = std::numeric_limits<int>::max();
}
} else if (maxfev < 0) {
if (maxiter == std::numeric_limits<int>::max()) {
maxfev = N * 200;
} else {
maxfev = std::numeric_limits<int>::max();
}
}
// 计算初始单纯形中每个点的函数值
Vector fsim = Vector::Constant(N + 1, std::numeric_limits<Scalar>::max());
int fcalls = 0;
for (int k = 0; k < N + 1; ++k) {
fsim(k) = func(sim.row(k).transpose());
fcalls++;
if (fcalls >= maxfev) {
break;
}
}
// 按函数值排序单纯形顶点
sort_simplex(fsim, sim);
// 保存所有迭代结果
std::vector<Vector> allvecs;
if (return_all) {
allvecs.push_back(sim.row(0).transpose());
}
// 开始迭代
int iterations = 1;
while (fcalls < maxfev && iterations < maxiter) {
// 检查收敛
double max_dist = 0.0;
for (int i = 1; i < N + 1; ++i) {
max_dist = std::max(max_dist, (sim.row(i) - sim.row(0)).norm());
}
double max_diff = 0.0;
for (int i = 1; i < N + 1; ++i) {
max_diff = std::max(max_diff, std::abs(fsim(i) - fsim(0)));
}
if (max_dist <= xatol && max_diff <= fatol) {
break;
}
// 计算质心(除了最差点)
// 1. sim.topRows(N) - 选择前N行,等价于Python的sim[:-1]
// (因为单纯形有N+1个顶点,选择前N行就是除了最后一行外的所有行)
// 2. .colwise().sum() - 对每一列分别求和,结果是一个行向量
// (等价于Python的np.add.reduce(..., 0))
// 3. .transpose() - 将行向量转置为列向量
// (因为在Eigen中Vector通常表示列向量)
// 4. / static_cast<Scalar>(N) - 除以N得到平均值(质心)
Vector xbar = sim.topRows(N).colwise().sum().transpose() / static_cast<Scalar>(N);
// 执行反射
Vector xr = (1 + rho) * xbar - rho * sim.row(N).transpose();
if (has_bounds) {
xr = clip(xr);
}
Scalar fxr = func(xr);
fcalls++;
bool doshrink = false;
if (fxr < fsim(0)) {
// 反射点比最好点还好,尝试扩展
Vector xe = (1 + rho * chi) * xbar - rho * chi * sim.row(N).transpose();
if (has_bounds) {
xe = clip(xe);
}
Scalar fxe = func(xe);
fcalls++;
if (fxe < fxr) {
// 扩展点更好,接受扩展
sim.row(N) = xe.transpose();
fsim(N) = fxe;
} else {
// 反射点更好,接受反射
sim.row(N) = xr.transpose();
fsim(N) = fxr;
}
} else {
// 反射点不比最好点好
if (fxr < fsim(N - 1)) {
// 反射点至少比次差点好,接受反射
sim.row(N) = xr.transpose();
fsim(N) = fxr;
} else {
// 执行收缩
if (fxr < fsim(N)) {
// 外收缩
Vector xc = (1 + psi * rho) * xbar - psi * rho * sim.row(N).transpose();
if (has_bounds) {
xc = clip(xc);
}
Scalar fxc = func(xc);
fcalls++;
if (fxc <= fxr) {
sim.row(N) = xc.transpose();
fsim(N) = fxc;
} else {
doshrink = true;
}
} else {
// 内收缩
Vector xcc = (1 - psi) * xbar + psi * sim.row(N).transpose();
if (has_bounds) {
xcc = clip(xcc);
}
Scalar fxcc = func(xcc);
fcalls++;
if (fxcc < fsim(N)) {
sim.row(N) = xcc.transpose();
fsim(N) = fxcc;
} else {
doshrink = true;
}
}
if (doshrink) {
// 收缩整个单纯形
for (int j = 1; j < N + 1; ++j) {
sim.row(j) = sim.row(0) + sigma * (sim.row(j) - sim.row(0));
if (has_bounds) {
sim.row(j) = clip(sim.row(j).transpose()).transpose();
}
fsim(j) = func(sim.row(j).transpose());
fcalls++;
if (fcalls >= maxfev) {
break;
}
}
}
}
}
iterations++;
// 重新排序单纯形
sort_simplex(fsim, sim);
// 保存当前最优解
if (return_all) {
allvecs.push_back(sim.row(0).transpose());
}
if (callback) {
NelderMeadResult intermediate_result;
intermediate_result.x = sim.row(0).transpose();
intermediate_result.fun = fsim(0);
intermediate_result.nit = iterations;
intermediate_result.nfev = fcalls;
if (callback(intermediate_result)) {
break;
}
}
}
// 准备结果
NelderMeadResult result;
result.x = sim.row(0).transpose();
result.fun = fsim(0);
result.nit = iterations;
result.nfev = fcalls;
result.success = true;
result.status = 0;
result.message = "Optimization terminated successfully.";
if (fcalls >= maxfev) {
result.status = 1;
result.success = false;
result.message = "Maximum number of function evaluations exceeded.";
if (disp) {
std::cerr << "Warning: " << result.message << std::endl;
}
} else if (iterations >= maxiter) {
result.status = 2;
result.success = false;
result.message = "Maximum number of iterations exceeded.";
if (disp) {
std::cerr << "Warning: " << result.message << std::endl;
}
} else if (disp) {
std::cout << result.message << std::endl;
std::cout << " Current function value: " << result.fun << std::endl;
std::cout << " Iterations: " << result.nit << std::endl;
std::cout << " Function evaluations: " << result.nfev << std::endl;
}
if (return_all) {
result.allvecs = allvecs;
}
return result;
}
template <typename Scalar>
Eigen::Matrix<Scalar, Eigen::Dynamic, 1>
fmin(std::function<Scalar(const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>&)> func,
const Eigen::Matrix<Scalar, Eigen::Dynamic, 1>& x0,
const Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>* initial_simplex,
double xtol,
double ftol,
int maxiter,
int maxfun,
bool full_output,
bool disp,
bool retall,
std::function<bool(const NelderMeadResult&)> callback) {
NelderMeadOptions options;
options.xatol = xtol;
options.fatol = ftol;
options.maxiter = maxiter;
options.maxfev = maxfun;
options.disp = disp;
options.return_all = retall;
NelderMeadResult result = minimize_neldermead(func, x0, initial_simplex, options, callback);
if (full_output) {
throw std::runtime_error(
"Full output mode not implemented in wrapper function. Use minimize_neldermead directly.");
} else {
if (retall) {
throw std::runtime_error(
"Return all mode not implemented in wrapper function. Use minimize_neldermead directly.");
} else {
return result.x;
}
}
}
#endif //MY_OPTIMIZE_INL
</code></pre>
<p>main.cpp:</p>
<pre><code class="language-cpp">#include <algorithm>
#include <cmath>
#include <functional>
#include <iostream>
#include <limits>
#include <vector>
#include <Eigen/Core>
#include "opti.h"
int main() {
// Rosenbrock函数
std::function<double(const Eigen::VectorXd&)> rosenbrock = [](const Eigen::VectorXd& x) -> double {
return 100 * std::pow(x(1) - x(0) * x(0), 2) + std::pow(1 - x(0), 2);
};
// 初始猜测
Eigen::VectorXd x0(2);
x0 << -1.2, 1.0;
// 设置选项
NelderMeadOptions options;
options.disp = true;
// 运行优化
NelderMeadResult result = minimize_neldermead<double>(rosenbrock, x0, nullptr, options);
// 输出结果
std::cout << "Optimal solution: " << result.x.transpose() << std::endl;
std::cout << "Function value: " << result.fun << std::endl;
return 0;
}
</code></pre>
<h1 id="scipy版nelder-mead算法与标准版的区别">SciPy版Nelder-Mead算法与标准版的区别</h1>
<p>(来自gpt o1,不保证正确性,用于辅助理解)</p>
<h2 id="边界约束bounds参数">边界约束(bounds参数)</h2>
<p>SciPy的Nelder-Mead实现支持简单的边界约束,而标准的Nelder-Mead算法没有原生的边界处理能力。在SciPy的<code>minimize(method="Nelder-Mead")</code>中,你可以传入边界参数,求解器会在每次迭代时将单纯形顶点裁剪到给定的边界内。实际上,这意味着如果反射或扩展的点在任何坐标上超出了范围,它会被拉回到边界(在边界处饱和)。这是一种简单的方法来保持搜索在边界内(正如SciPy开发者确认的:"这只是简单地裁剪到边界")。标准的Nelder-Mead算法(按照最初描述)不包含任何约束处理机制——任何边界处理都需要外部修改或变体算法。因此,SciPy添加的边界处理是对传统Nelder-Mead算法的显著扩展。</p>
<h2 id="自适应参数选项adaptivetrue">自适应参数选项(adaptive=True)</h2>
<p>SciPy的Nelder-Mead提供了一种"自适应"模式,可以调整算法在高维问题中的行为。当<code>adaptive=True</code>时,SciPy使用来自Gao和Han(2012)的修改参数方案。在代码中,这改变了单纯形变换系数如下:反射系数ρ = 1(保持不变),扩展系数χ = 1 + 2/N(而不是2),收缩系数ψ = 0.75 - 1/(2N)(而不是0.5),缩小因子σ = 1 - 1/N(而不是0.5),其中N是维度数量。如果<code>adaptive=False</code>(默认值),SciPy使用标准的Nelder-Mead系数:ρ = 1,χ = 2,ψ = 0.5,σ = 0.5。这些经典值对应于原始的Nelder-Mead算法。自适应模式不会在运行过程中改变系数;相反,它在开始时根据问题维度选择这些依赖于维度的值(使方法在高维中更加稳健)。总之,标准Nelder-Mead对所有问题使用固定参数,而SciPy的版本在需要时可以使用Gao-Han自适应参数集,改善高维搜索的性能(同时在<code>adaptive=False</code>时对2D或低维问题保持与标准Nelder-Mead相同)。</p>
<h2 id="收敛准则fatol和xatol">收敛准则(fatol和xatol)</h2>
<p>SciPy的实现使用两个收敛容差阈值——一个用于函数值(fatol),一个用于解的坐标(xatol)——并要求同时满足这两个准则才能收敛。在每次迭代中,SciPy检查当前最佳顶点与其他单纯形顶点之间的最大坐标距离是否<= xatol,以及最佳顶点与其他顶点之间的函数值最大差异是否<= fatol。只有当这两个条件都成立时,才宣布收敛并停止。这种"双重停止条件"确保单纯形在空间中充分收缩,且函数值已经趋于平稳。相比之下,标准Nelder-Mead描述通常使用单一容差(例如,检查单纯形直径或函数值范围是否低于阈值)。许多实现(包括早期的SciPy fmin)也使用了两个容差(xtol和ftol)——SciPy继续这种方法但将它们重命名为xatol和fatol(明确表示"绝对"容差)。这种双重标准更加稳健:它可以防止过早停止,例如,如果单纯形很小但函数值仍然存在差异,或者反之亦然。简而言之,SciPy的Nelder-Mead只有在参数变化和函数改进都低于各自的阈值时才停止,而简单的Nelder-Mead可能使用其中之一。(事实上,SciPy的文档指出:"ftol和xtol标准必须同时满足才能收敛。")</p>
<h2 id="终止规则maxiter和maxfun">终止规则(maxiter和maxfun)</h2>
<p>SciPy的Nelder-Mead包含迭代次数和函数评估次数的明确限制,如果算法停滞或收敛缓慢,可以安全终止。maxiter(最大迭代次数)和maxfun(又称maxfev,最大函数评估次数)选项将在达到任一限制时停止优化(以先到者为准)。如果两者都设置了,SciPy会同时监控两者,一旦超过其中一个限制就会退出;如果只设置了一个,另一个实际上被视为无穷(或默认值),以便指定的限制起作用。默认情况下,如果用户没有提供这些参数,SciPy使用启发式规则:maxiter = maxfun = 200 * N(其中N是变量数量)。这个默认值(类似于MATLAB的fminsearch默认值)是一个合理的上限,以防容差标准永远不满足而导致无限循环。相比之下,已发表的标准Nelder-Mead算法没有定义特定的迭代限制——理论上它可以一直运行到满足收敛标准。然而,在实践中,各种实现(包括SciPy和其他如MATLAB)都施加了这样的限制,以确保即使对于困难或平坦的目标函数,程序也能终止。SciPy的结果对象会通过设置警告标志和消息(例如,warnflag=1表示达到最大函数评估次数,warnflag=2表示达到最大迭代次数)来指示优化器是否因达到maxiter或maxfun而停止。这是一个实现细节,增强了SciPy的Nelder-Mead求解器相比基础Nelder-Mead的稳健性。</p>
<h2 id="结果存储return_all和回调机制">结果存储(return_all)和回调机制</h2>
<p>SciPy提供了方便的钩子来检查优化进度,这些钩子不是理论Nelder-Mead算法的一部分,但在实践中很有用。如果设置了<code>return_all=True</code>,SciPy的求解器会记录每次迭代中的最佳解决方案,并将其作为结果对象中的一个列表返回(result.allvecs)。这允许用户检查单纯形在搜索空间中所走的路径。此外,SciPy接受一个回调函数:如果提供了回调函数,它会在每次迭代结束时使用当前最佳点xk作为参数调用该函数。回调机制启用了用户定义的监控或日志记录(例如,打印进度,或在自定义条件下提前停止求解器)。这些特性在标准Nelder-Mead描述中没有等效项,后者只关注于找到最小值。它们纯粹是SciPy中的实现增强功能。例如,SciPy代码显示,在每次迭代时,当return_all为True时,它会将sim(当前最佳顶点)附加到allvecs,如果提供了回调函数,则调用callback(sim)。这些特性使SciPy的Nelder-Mead更加用户友好,允许在求解过程中进行自省和交互。</p>
<h2 id="结论和其他总结的准确性">结论和其他总结的准确性</h2>
<p>总之,SciPy的Nelder-Mead与"教科书"Nelder-Mead在几个方面有所不同:它通过将单纯形更新裁剪到边界内来支持有界约束变量,它提供了自适应参数变体(Gao-Han 2012)以在高维中获得更好的性能,它采用必须同时满足的双重收敛标准(xatol和fatol),它使用可配置(和默认)的迭代和函数调用限制以确保终止,它提供return_all/callback用于结果跟踪和用户干预。这些增强功能使SciPy的版本比Nelder-Mead的基本算法描述更加稳健和功能丰富。</p>
<p>如果其他人沿着这些线索总结了SciPy与标准Nelder-Mead的差异,那么他们的总结可能是准确的。上述每个点都由SciPy代码和文档支持(如所示)。然而,任何缺少这些关键差异或不正确描述它们的总结都不会完全准确。例如,必须注意SciPy的终止同时需要fatol和xatol条件(不是二选一),并且"自适应"模式只是基于问题维度使用不同的固定系数集(除了初始选择外,它不会在迭代过程中动态改变它们)。同样,声明SciPy通过"投影"或"反射回"处理边界应该明确意味着裁剪到边界,而不是更复杂的约束处理。总的来说,上述列出的点与正确的区别相符。因此,涵盖这些内容的总结——边界裁剪、自适应系数、双重容差、迭代/函数评估限制以及可选的回调/轨迹输出——是准确的。任何其他人的总结如果反映了这些概念可能是正确的,而遗漏或错误(如误解收敛检查方式或自适应如何工作)需要更正。代码分析确认了SciPy实现的细节,因此任何总结的正确性都可以根据上述事实进行判断。</p>
<h1 id="参考文献">参考文献</h1>
<p>以下是从SciPy中提取的论文引用,并附上中文翻译:</p>
<ol>
<li>
<p>Nelder, J.A. and Mead, R. (1965), "A simplex method for function<br>
minimization", The Computer Journal, 7, pp. 308-313</p>
<p>中文:Nelder, J.A. 和 Mead, R.<br>
(1965),《一种用于函数最小化的单纯形方法》,计算机杂志,第7卷,308-313页</p>
</li>
<li>
<p>Wright, M.H. (1996), "Direct Search Methods: Once Scorned, Now<br>
Respectable", in Numerical Analysis 1995, Proceedings of the 1995<br>
Dundee Biennial Conference in Numerical Analysis, D.F. Griffiths and<br>
G.A. Watson (Eds.), Addison Wesley Longman, Harlow, UK, pp. 191-208.</p>
<p>中文:Wright, M.H.<br>
(1996),《直接搜索方法:曾经被鄙视,如今受人尊敬》,载于1995年数值分析文集,1995年邓迪数值分析两年会议论文集,D.F.<br>
Griffiths和G.A. Watson(编辑),Addison Wesley Longman出版社,英国哈洛,191-208页</p>
</li>
<li>
<p>Gao, F. and Han, L. (2012), "Implementing the Nelder-Mead simplex<br>
algorithm with adaptive parameters", Computational Optimization and<br>
Applications, 51:1, pp. 259-277</p>
<p>中文:Gao, F. 和 Han, L.<br>
(2012),《使用自适应参数实现Nelder-Mead单纯形算法》,计算优化与应用,第51卷第1期,259-277页</p>
</li>
</ol><br><br>
来源:https://www.cnblogs.com/coffeepro123/p/19228703
頁:
[1]