士郎小天使 2021-10-30 23:13 采纳率: 81.1%
浏览 193
已结题

用多元指数多项式去拟合一组数据,如何加上x在0到40,y在0到360积分为1这个约束

下图为多元指数多项式,去拟合一组数据(x,y,f(x,y)),约束条件为x在0到40,y在0到360(xy的范围最好可变),f(x,y)的积分等于1。
一维的指数多项式,拟合一组数据,积分为1的方法,可以用非线性方程组方法和比例系数法,为下面2个程序。然后二维数据的多元指数多项式不带积分为1的程序也在下面写了,如果把一维指数多项式的非线性方程组方法和比例系数法用在二维数据里,要如何编写,写了一半的程序和仿造的程序都在下面。

img

一维指数多项式积分为1,非线性方程组法

x = [0:1:40]; % x随意
mu = 20; sigma = 10;
y = 1/sqrt(2*pi)/sigma*exp(-(x-mu+rand(size(x))*2).^2/2/sigma^2);% y随意设置,在正弦函数上加随机浮动
y(y<=0) = 10*eps;
n = 5; % n随意
x = x(:);
y = y(:);
S = zeros(length(x), n+1);
q = ones(length(x),1);
S(:,n+1)=q;
for i = n:-1:1
    q = q.*x;
    S(:,i) = q;
end
logy = log(y);
A = S'*S;
b = S'*logy;
a = A\b;%最小二乘
ff = @(x,a) exp(polyval(a,x));
eq = @(a)[A*a-b; integral(@(x)ff(x,a),0,40)-1];%构建了方程组后面一项指的是积分为1
options = optimoptions('fsolve','Display','off','FunctionTolerance',1e-2,'algorithm','levenberg-marquardt');
[a, f, flag] = fsolve(eq, a, options);
while (flag>=4 || flag<=0)
    [aa, f, flag] = fsolve(eq, a+a.*(2*rand(size(a))-1), options);
end
a = aa;
xfit = linspace(min(x),max(x),1001);
plot(x,y,'r-',xfit,exp(polyval(a,xfit)),'b-')
legend('原值','拟合后')

一维指数多项式积分为1,比例系数法

x = [0:1:40]; % x随意
mu = 20; sigma = 10;
y = 1/sqrt(2*pi)/sigma*exp(-(x-mu+rand(size(x))*2).^2/2/sigma^2);% y随意设置,在正弦函数上加随机浮动
y(y<=0) = 10*eps;
n = 5; % n随意
x = x(:);
y = y(:);
S = zeros(length(x), n+1);
q = ones(length(x),1);
S(:,n+1)=q;
for i = n:-1:1
    q = q.*x;
    S(:,i) = q;
end
logy = log(y);
A = S'*S;
b = S'*logy;
a = A\b;%最小二乘
ff = @(x,a) exp(polyval(a,x));
S = integral(@(x)ff(x,a),0,40);
a(end) = a(end) - log(S);%整体除以S
xfit = linspace(min(x),max(x),1001);
plot(x,y,'r-',xfit,exp(polyval(a,xfit)),'b-')
legend('原值','拟合后')

多元指数多项式,不带积分为1的约束程序

%% 为了得到分布,假设了一个协方差矩阵
mu=[20,180];%数学期望
sigma=[20/3 0;0,60].^2;%协方差矩阵
r=mvnrnd(mu,sigma,100000);%生成100000个样本
x = r(:,1);
y = r(:,2);
nx = 40;
ny = 40;
xmax = 40;
ymax = 360;
dx = xmax/nx;
dy = ymax/ny;
[X, Y, C, xmid, ymid] = ef2(x,y,nx+1,ny+1,[0,xmax],[0,ymax]);
C = C/(dx*dy);%单位面积上的概率即概率密度
figure(1);clf;
bar3(C)
title('原先数据')
xtick = 1:size(C,2); xticklabel = xmid; % xtick和xticklabel一定要对应,长度相等
ytick = 1:size(C,1); yticklabel = ymid; % ytick和yticklabel一定要对应,长度相等
set(gca, 'xtick', xtick, 'xticklabel',xticklabel,...
    'ytick', ytick, 'yticklabel',yticklabel)
xlabel('X')
ylabel('Y')
x = X(:);
y = Y(:);
z = C(:);%根据x和y算出的频率,x,y,z为输入数据
n = 7; % n为多项式的阶数,参数一共为(n+1)²个
[i1, i2] = meshgrid(0:n);
F = arrayfun(@(i)x.^i1(i).*y.^i2(i), 1:numel(i1), 'uniform', 0);
F = cell2mat(F);

F1=[z,F]%这里是因为z数据有时候频率为0,必须把含0的数据去掉
[r,c]=size(F1);
index=1:r; %一维矢量, F1的行指标
all(F1') %将F1转置一下, 返回一个矢量,它的每个元素进行判断, F1所在行的元素全不为0则是1, 否则为0
F1=F1(index(all(F1')),:) %取出F1不含0元素的行,构成新的矩阵
F1(:,1)=[]
z(z==0)=[]
logz=log(z)

A = F1'*F1;
b = F1'*logz;
a = A\b;%最小二乘法
zfit = polyfunval(X,Y,a,n);
zfit1 = exp(zfit);
surf(X,Y,zfit1);%这之后的非线性方程组法和比例系数法如何仿造上面的程序继续

function f = polyfunval(x,y,a,n)%指数多元多项式的公式
[i1, i2] = meshgrid(0:n);
Cfit = arrayfun(@(i)a(i)*x.^i1(i).*y.^i2(i), 1:numel(i1), 'uniform', 0);
f = Cfit{1};
for i = 2:numel(Cfit)
    f = f + Cfit{i};
end
end

function [X, Y, CDF, xmid, ymid] = ef2(x,y,nx,ny,xminmax,yminmax)%输入数据,只知道xy,算出频率z
num = length(x);
if(num~=length(y))
   error('输入的x和y长度必须相等') 
end
if(nargin>6) % 如果变量个数大于6个,太多了
    error('太多输入变量')
elseif(nargin<2) % 如果变量个数小于2个,太少了
    error('输入变量数目不足!!')
end
if(nargin==6) % 如果变量个数等于6个,赋值给ymin和ymax
    ymin = yminmax(1);
    ymax = yminmax(2);
end
if(nargin>=5)% 如果变量个数大于等于5个,赋值给xmin和xmax
    xmin = xminmax(1);
    xmax = xminmax(2);
end
if(nargin<=4)% 如果变量个数小于等于4个,自定义xmin和xmax
    xmin = min(x);
    xmax = max(x)+eps;
    ymin = min(y);
    ymax = max(y)+eps;
end
if(nargin<=3)% 如果变量个数小于等于3个,自定义y方向划分段数ny
    ny = 30;
end
if(nargin==2)% 如果变量个数等于2个,自定义x方向划分段数nx
    nx = 30;
end
xg = linspace(xmin, xmax, nx);%x方向的点
yg = linspace(ymin, ymax, ny);%y方向的点
xmid = (xg(1:end-1)+xg(2:end))/2;
ymid = (yg(1:end-1)+yg(2:end))/2;
[X,Y] = meshgrid(xmid, ymid);%形成网格
[I,J] = meshgrid(2:nx, 2:ny);%下标网格
CDF = arrayfun(@(i,j)sum(x>=xg(i-1)&x<xg(i)&y<yg(j)&y>=yg(j-1))/num,I,J);%形成经验分布
end
  • 写回答

2条回答 默认 最新

  • joel_1993 2021-11-05 22:29
    关注

    你好,这里面直接构造最小二乘法然后用比例系数比较方便,建议n不要取太大,容易造成过拟合,使得拟合误差过大:

    %% 为了得到分布,假设了一个协方差矩阵
    mu=[20,180];%数学期望
    sigma=[20/3 0;0,60].^2;%协方差矩阵
    r=mvnrnd(mu,sigma,100000);%生成100000个样本
    x = r(:,1);
    y = r(:,2);
    nx = 40;
    ny = 40;
    xmax = 40;
    ymax = 360;
    dx = xmax/nx;
    dy = ymax/ny;
    [X, Y, C, xmid, ymid] = ef2(x,y,nx+1,ny+1,[0,xmax],[0,ymax]);
    C = C/(dx*dy);%单位面积上的概率即概率密度
    figure(1);clf;
    bar3(C)
    title('原先数据')
    xtick = 1:size(C,2); xticklabel = xmid; % xtick和xticklabel一定要对应,长度相等
    ytick = 1:size(C,1); yticklabel = ymid; % ytick和yticklabel一定要对应,长度相等
    set(gca, 'xtick', xtick(1:4:end), 'xticklabel',xticklabel(1:4:end),...
        'ytick', ytick(1:4:end), 'yticklabel',yticklabel(1:4:end))
    xlabel('X')
    ylabel('Y')
    x = X(:);
    y = Y(:);
    z = C(:);%根据x和y算出的频率,x,y,z为输入数据
    n = 4; % n为多项式的阶数,参数一共为(n+1)²个, 注意n不要取太大,容易产生过拟合
    [i1, i2] = meshgrid(0:n);
    F = arrayfun(@(i)x.^i1(i).*y.^i2(i), 1:numel(i1), 'uniform', 0);
    F = cell2mat(F);
    %% 标注------------改动的地方-------------------
    z(z==0) = eps; %如果z等于零,赋值给一个很小的数字即可
    logz=log(z);
    A = F'*F;
    b = F'*logz;
    a = A\b;%最小二乘法
    zfit = polyfunval(X,Y,a,n);
    zfun = @(x,y)exp(polyfunval(x,y,a,n)); % 构建z的函数获取整体积分值
    V = integral2(zfun, 0, 40, 0, 360);
    a(1) = a(1) - log(V);
    zfun = @(x,y)exp(polyfunval(x,y,a,n)); % 构建z的函数获取整体积分值,修正了a,使得积分为1
    V_after_correct = integral2(zfun,0, 40, 0, 360) %这里可以显示积分值
    zfit1 = exp(zfit);
    figure(2);clf
    surf(X,Y,zfit1, 'facealpha', 0.8);%这之后的非线性方程组法和比例系数法如何仿造上面的程序继续
    
    function f = polyfunval(x,y,a,n)%指数多元多项式的公式
    [i1, i2] = meshgrid(0:n);
    Cfit = arrayfun(@(i)a(i)*x.^i1(i).*y.^i2(i), 1:numel(i1), 'uniform', 0);
    f = Cfit{1};
    for i = 2:numel(Cfit)
        f = f + Cfit{i};
    end
    end
    function [X, Y, CDF, xmid, ymid] = ef2(x,y,nx,ny,xminmax,yminmax)%输入数据,只知道xy,算出频率z
    num = length(x);
    if(num~=length(y))
       error('输入的x和y长度必须相等') 
    end
    if(nargin>6) % 如果变量个数大于6个,太多了
        error('太多输入变量')
    elseif(nargin<2) % 如果变量个数小于2个,太少了
        error('输入变量数目不足!!')
    end
    if(nargin==6) % 如果变量个数等于6个,赋值给ymin和ymax
        ymin = yminmax(1);
        ymax = yminmax(2);
    end
    if(nargin>=5)% 如果变量个数大于等于5个,赋值给xmin和xmax
        xmin = xminmax(1);
        xmax = xminmax(2);
    end
    if(nargin<=4)% 如果变量个数小于等于4个,自定义xmin和xmax
        xmin = min(x);
        xmax = max(x)+eps;
        ymin = min(y);
        ymax = max(y)+eps;
    end
    if(nargin<=3)% 如果变量个数小于等于3个,自定义y方向划分段数ny
        ny = 30;
    end
    if(nargin==2)% 如果变量个数等于2个,自定义x方向划分段数nx
        nx = 30;
    end
    xg = linspace(xmin, xmax, nx);%x方向的点
    yg = linspace(ymin, ymax, ny);%y方向的点
    xmid = (xg(1:end-1)+xg(2:end))/2;
    ymid = (yg(1:end-1)+yg(2:end))/2;
    [X,Y] = meshgrid(xmid, ymid);%形成网格
    [I,J] = meshgrid(2:nx, 2:ny);%下标网格
    CDF = arrayfun(@(i,j)sum(x>=xg(i-1)&x<xg(i)&y<yg(j)&y>=yg(j-1))/num,I,J);%形成经验分布
    end
    

    直接求解非线性方程组无法搜索到合理解,在这里给出,只作为参考

    %% 为了得到分布,假设了一个协方差矩阵
    mu=[20,180];%数学期望
    sigma=[20/3 0;0,60].^2;%协方差矩阵
    r=mvnrnd(mu,sigma,100000);%生成100000个样本
    x = r(:,1);
    y = r(:,2);
    nx = 40;
    ny = 40;
    xmax = 40;
    ymax = 360;
    dx = xmax/nx;
    dy = ymax/ny;
    [X, Y, C, xmid, ymid] = ef2(x,y,nx+1,ny+1,[0,xmax],[0,ymax]);
    C = C/(dx*dy);%单位面积上的概率即概率密度
    figure(1);clf;
    bar3(C)
    title('原先数据')
    xtick = 1:size(C,2); xticklabel = xmid; % xtick和xticklabel一定要对应,长度相等
    ytick = 1:size(C,1); yticklabel = ymid; % ytick和yticklabel一定要对应,长度相等
    set(gca, 'xtick', xtick(1:4:end), 'xticklabel',xticklabel(1:4:end),...
        'ytick', ytick(1:4:end), 'yticklabel',yticklabel(1:4:end))
    xlabel('X')
    ylabel('Y')
    x = X(:);
    y = Y(:);
    z = C(:);%根据x和y算出的频率,x,y,z为输入数据
    n = 4; % n为多项式的阶数,参数一共为(n+1)²个, 注意n不要取太大,容易产生过拟合
    [i1, i2] = meshgrid(0:n);
    F = arrayfun(@(i)x.^i1(i).*y.^i2(i), 1:numel(i1), 'uniform', 0);
    F = cell2mat(F);
    %% 标注------------改动的地方-------------------
    z(z==0) = eps; %如果z等于零,赋值给一个很小的数字即可
    logz=log(z);
    A = F'*F;
    b = F'*logz;
    a = A\b;%最小二乘法
    
    ff = @(x,y,a)exp(polyfunval(x,y,a,n)); % 构建z的函数获取整体积分值
    eq = @(a)[A*a-b; integral2(@(x,y)ff(x,y,a),0,40,0,360)-1];%构建了方程组后面一项指的是积分为1
    options = optimoptions('fsolve','Display','off','FunctionTolerance',1e-2,'algorithm','levenberg-marquardt');
    [aa, f, flag] = fsolve(eq, a, options);
    count = 0;
    while (flag>=4 || flag<=0)
        count = count + 1;
        fprintf('第%d次寻根\n',count);
        [aa, f, flag] = fsolve(eq, a+a.*(0.0002*rand(size(a))-1), options);
        if(count>100)
            fprintf('寻根未成功\n')
            aa = a;
            break;
        end
    end
    a = aa;
    zfit = polyfunval(X,Y,a,n);
    V_after_correct = integral2(zfun, min(x), max(x), min(y), max(y)) %这里可以显示积分值
    zfit1 = exp(zfit);
    figure(2);clf
    surf(X,Y,zfit1, 'facealpha', 0.8);%这之后的非线性方程组法和比例系数法如何仿造上面的程序继续
    
    function f = polyfunval(x,y,a,n)%指数多元多项式的公式
    [i1, i2] = meshgrid(0:n);
    Cfit = arrayfun(@(i)a(i)*x.^i1(i).*y.^i2(i), 1:numel(i1), 'uniform', 0);
    f = Cfit{1};
    for i = 2:numel(Cfit)
        f = f + Cfit{i};
    end
    end
    function [X, Y, CDF, xmid, ymid] = ef2(x,y,nx,ny,xminmax,yminmax)%输入数据,只知道xy,算出频率z
    num = length(x);
    if(num~=length(y))
       error('输入的x和y长度必须相等') 
    end
    if(nargin>6) % 如果变量个数大于6个,太多了
        error('太多输入变量')
    elseif(nargin<2) % 如果变量个数小于2个,太少了
        error('输入变量数目不足!!')
    end
    if(nargin==6) % 如果变量个数等于6个,赋值给ymin和ymax
        ymin = yminmax(1);
        ymax = yminmax(2);
    end
    if(nargin>=5)% 如果变量个数大于等于5个,赋值给xmin和xmax
        xmin = xminmax(1);
        xmax = xminmax(2);
    end
    if(nargin<=4)% 如果变量个数小于等于4个,自定义xmin和xmax
        xmin = min(x);
        xmax = max(x)+eps;
        ymin = min(y);
        ymax = max(y)+eps;
    end
    if(nargin<=3)% 如果变量个数小于等于3个,自定义y方向划分段数ny
        ny = 30;
    end
    if(nargin==2)% 如果变量个数等于2个,自定义x方向划分段数nx
        nx = 30;
    end
    xg = linspace(xmin, xmax, nx);%x方向的点
    yg = linspace(ymin, ymax, ny);%y方向的点
    xmid = (xg(1:end-1)+xg(2:end))/2;
    ymid = (yg(1:end-1)+yg(2:end))/2;
    [X,Y] = meshgrid(xmid, ymid);%形成网格
    [I,J] = meshgrid(2:nx, 2:ny);%下标网格
    CDF = arrayfun(@(i,j)sum(x>=xg(i-1)&x<xg(i)&y<yg(j)&y>=yg(j-1))/num,I,J);%形成经验分布
    end
    

    总体来讲,比例系数法会更加合理且简单易操作,但是要注意n的取值不要过大(建议5以内),根据自己的数据调整n的取值会好一些

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

问题事件

  • 系统已结题 11月13日
  • 已采纳回答 11月5日
  • 修改了问题 11月2日
  • 赞助了问题酬金 11月2日
  • 展开全部

悬赏问题

  • ¥15 用ns3仿真出5G核心网网元
  • ¥15 matlab答疑 关于海上风电的爬坡事件检测
  • ¥88 python部署量化回测异常问题
  • ¥30 酬劳2w元求合作写文章
  • ¥15 在现有系统基础上增加功能
  • ¥15 远程桌面文档内容复制粘贴,格式会变化
  • ¥15 关于#java#的问题:找一份能快速看完mooc视频的代码
  • ¥15 这种微信登录授权 谁可以做啊
  • ¥15 请问我该如何添加自己的数据去运行蚁群算法代码
  • ¥20 用HslCommunication 连接欧姆龙 plc有时会连接失败。报异常为“未知错误”