This answer quotes ChatGPT
That long
function [xf, S, cnt] = SAM_LMF(varargin)
% SAM_LMF:使用最小二乘法解一组非线性方程,返回最终的解近似值、残差平方和以及迭代次数。
%
% 语法:
% [xf, S, cnt] = SAM_LMF(fcn, x0)
% [xf, S, cnt] = SAM_LMF(fcn, x0, 'Display', d)
% [xf, S, cnt] = SAM_LMF(fcn, x0, 'MaxIter', mi)
% [xf, S, cnt] = SAM_LMF(fcn, x0, 'Display', d, 'MaxIter', mi)
%
% 输入参数:
% fcn - 一个函数句柄,输入为一个向量 x,返回一个与输入具有相同维度的残差向量。
% 该函数必须定义为输出与输入具有相同的维度。
% x0 - 解的初始猜测。
%
% 可选输入参数:
% Display - 整数标量。如果 Display 大于等于 1,则算法会在每次迭代时打印迭代次数、
% 残差平方和以及步长向量的 2-范数。
% 如果 Display 大于 max(1,ceil(MaxIter/10)),则算法还会在每次迭代时绘制
% 残差平方和随迭代次数的变化曲线。
% MaxIter - 整数标量,最大迭代次数。
%
% 输出参数:
% xf - 最终的解近似值。
% S - 残差平方和。
% cnt - 迭代次数。如果算法没有收敛,则 cnt 为 -MaxIter。
%
% 示例:
% Rosenbrock 山谷内有一个单位直径的圆。
% R = @(x) sqrt(x'*x)-.5; % 与半径 r=0.5 的距离
% ros= @(x) [ 10*(x(2)-x(1)^2); 1-x(1); (R(x)>0)*R(x)*1000];
% [x,ssq,cnt]=SAM_LMF(ros,[-1.2,1],'Display',1,'MaxIter',50)
% 返回 x = [0.4556; 0.2059],ssq = 0.2966,cnt = 18。
%
% 注意:对于没有实现匿名函数的旧版本 MATLAB (<7) 的用户,应使用具有残差的命名函数来调用 SAM_LMF。
% 对于上面的示例,可以使用以下函数进行调用:
% [x,ssq,cnt]=SAM_LMF('rosen',[-1.2,1]);
% 其中 rosen.m 函数形式为
% function r = rosen(x)
%% Rosenbrock 山谷带约束
% R = sqrt(x(1)^2+x(2)^2)-.5;
%% 残差:
% r = [ 10*(x(2)-x(1)^2) % 第一部分
% 判断输入参数是否为默认值或结构体数组或字符数组
if nargin==1 && strcmpi('default',varargin(1))
xf.Display = 0; % 不打印迭代次数
xf.MaxIter = 100; % 允许的最大迭代次数
xf.ScaleD = []; % 自动缩放,D = diag(diag(J'*J))
xf.FunTol = 1e-7; % 最终函数值的容差
xf.XTol = 1e-4; % x解的差异的容差
return
% 更新选项
elseif isstruct(varargin{1}) % 输入参数为结构体时
if ~isfield(varargin{1},'Display')
error('Options Structure not correct for LMFsolve.')
end
xf=varargin{1}; % 选项
for i=2:2:nargin-1
name=varargin{i}; % 要更新的选项
if ~ischar(name)
error('Parameter Names Must be Strings.')
end
name=lower(name(isletter(name)));
value=varargin{i+1}; % 选项的值
if strncmp(name,'d',1), xf.Display = value;
elseif strncmp(name,'f',1), xf.FunTol = value(1);
elseif strncmp(name,'x',1), xf.XTol = value(1);
elseif strncmp(name,'m',1), xf.MaxIter = value(1);
elseif strncmp(name,'s',1), xf.ScaleD = value;
else disp(['Unknown Parameter Name --> ' name])
end
end
return
% 对选项进行配对
elseif ischar(varargin{1}) % 输入字符数组时
Pnames=char('display','funtol','xtol','maxiter','scaled');
if strncmpi(varargin{1},Pnames,length(varargin{1}))
xf=SAM_LMF('default'); % 获取默认值
xf=SAM_LMF(xf,varargin{:});
return
end
end
% 获取函数句柄
FUN=varargin{1};
if ~(isvarname(FUN) || isa(FUN,'function_handle'))
error('FUN Must be a Function Handle or M-file Name.')
end
% 获取初始猜测
xc=varargin{2};
% 处理输入参数
if nargin>2 % OPTIONS
if isstruct(varargin{3}) % 输入三个参数时,options为第三个参数
options=varargin{3};
else
if ~exist('options','var')
options = SAM_LMF('default');
end
for i=3:2:size(varargin,2)-1 % 解析选项和选项值
options=SAM_LMF(options, varargin{i},varargin{i+1});
end
end
else % 输入两个参数
if ~exist('options','var') % 检查第二个输出参数的变量是否存在
options = SAM_LMF('default');
end
end
x = xc(:); % 将 xc 内的数据转化为列向量
lx = length(x);
r = feval(FUN,x); % 在起始点处计算残差
% ~~~~~~~~~~~~~~~~~
S = r'*r;
epsx = options.XTol(:);
epsf = options.FunTol(:);
if length(epsx)ones(lx,1); end
J = finjac(FUN,r,x,epsx);
% ~~~~~~~~~~~~~~~~~~~~~~
nfJ = 2;
A = J.'*J; % 系统矩阵
v = J.'*r;
D = options.ScaleD;
if isempty(D)
D = diag(diag(A)); % 自动缩放
for i = 1:lx
if D(i,i)==0, D(i,i)=1; end
end
else
if numel(D)>1
D = diag(sqrt(abs(D(1:lx)))); % 向量类型的缩放
else
D = sqrt(abs(D))*eye(lx); % 标量类型的缩放
end
end
Rlo = 0.25;
Rhi = 0.75;
l=1; lc=.75; is=0;
cnt = 0;
ipr = options.Display;
printit(ipr,-1); % 打印表头
d = options.XTol; % 第一次循环的向量
maxit = options.MaxIter; % 允许的最大迭代次数
% 主迭代循环
while cntabs(d) >= epsx) && ...
any(abs(r) >= epsf)
d = (A+l*D)\v; % 负解增量
xd = x-d;
rd = feval(FUN,xd);
% ~~~~~~~~~~~~~~~~~~~
nfJ = nfJ+1;
Sd = rd.'*rd;
dS = d.'*(2*v-A*d); % 预计的减小量
R = (S-Sd)/dS;
if R>Rhi % 如果 R 值过高,则减半 l
l = l/2;
if l0; end
elseif R% 如果 R 值过低,则重新计算 nu
nu = (Sd-S)/(d.'*v)+2;
if nu<2
nu = 2;
elseif nu>10
nu = 10;
end
l = nu*l;
if l>lc, l=lc; end
else
is = is+1;
l = l*max(1/3,1-(2*R-1)^3);
end
x = xd; r = rd; % 更新 x 和 r
S = Sd; % 更新残差平方和
cnt= cnt+1; % 更新迭代次数
printit(ipr,cnt) % 打印当前结果
end
if cnt>=maxit
cnt = -cnt;
end
xf = x; % 最终结果近似
S = S; % 残差平方和
cnt = abs(cnt); % 迭代次数
if cnt==maxit, cnt=-cnt; end % 迭代不收敛
if l==0
lc = 1/max(abs(diag(inv(A)))); % 计算新的 lambda 的上限
l = lc; % 设置 l 为 lc
nu = nu/2; % 更新 nu
end
l = nu*l;
cnt = cnt+1;
if ipr~=0 && (rem(cnt,ipr)==0 || cnt==1) % 是否打印迭代结果
printit(ipr,cnt,nfJ,S,x,d,l,lc)
end
if Sd% 计算新的 Jacobian 矩阵
% ~~~~~~~~~~~~~~~~~~~~~~~~~
nfJ = nfJ+1;
A = J'*J;
v = J'*r;
end
end % while
xf = x; % 最终结果
if cnt==maxit
cnt = -cnt; % 达到最大迭代次数
end
rd = feval(FUN,xf);
nfJ = nfJ+1;
Sd = rd.'*rd;
if ipr, disp(' '), end
printit(ipr,cnt,nfJ,Sd,xf,d,l,lc) % 打印最终结果
function J = finjac(FUN,r,x,epsx)
% FINJAC numerical approximation to Jacobi matrix
% 计算函数 FUN 在点 x 处的 Jacobian 矩阵的数值近似
% FUN: 函数句柄
% r: 函数 FUN 的残差向量,大小为 mx1
% x: 代表输入参数的列向量,大小为 lx1
% epsx: 一个标量或大小与 x 相同的列向量,代表函数 FUN 在 x 点的数值微分步长
% 返回值:
% J: Jacobian 矩阵的数值近似,大小为 mxl
lx=length(x);
J=zeros(length(r),lx);
for k=1:lx
dx=.25*epsx(k); % 小增量
xd=x;
xd(k)=xd(k)+dx;
rd=feval(FUN,xd);
% 计算数值近似的 Jacobian 矩阵
J(:,k)=((rd-r)/dx);
end
function printit(ipr,cnt,res,SS,x,dx,l,lc)
% PRINTIT Printing of intermediate results
% 打印迭代过程中的结果
% ipr < 0 不打印 lambda 列
% = 0 不打印任何内容
% > 0 每隔 (ipr) 次打印一次
% cnt = -1 打印表头
% 0 打印第二行的结果
% >0 打印第一行的结果
if ipr~=0
if cnt<0 % 表头
disp('')
disp(char('*'*ones(1,75)))
fprintf(' itr nfJ SUM(r^2) x dx');
if ipr>0
fprintf(' l lc');
end
fprintf('\n');
disp(char('*'*ones(1,75)))
disp('')
else % 打印每次迭代结果
if rem(cnt,ipr)==0
f='%12.4e ';
if ipr>0
fprintf(['%4.0f %4.0f ' f f f f f '\n'],...
cnt,res,SS, x(1),dx(1),l,lc);
else
fprintf(['%4.0f %4.0f ' f f f '\n'],...
cnt,res,SS, x(1),dx(1));
end
for k=2:length(x)
fprintf([blanks(23) f f '\n'],x(k),dx(k));
end
end
end
end