回 帖 发 新 帖 刷新版面

主题:求助关于用Levenberg-Marquardt方法非线性拟合的问题!

打算用Levenberg-Marquardt方法拟合如下方程:
y=c/(1+exp(a+bx))+d;          方程(1)
主程序中的x[18],y[18]是用来拟合的两列数据;程序funcs是用来计算方程(1)的y值及对需要拟合参数a,b,c,d的偏导数;程序在最后输出拟合的a,b,c,d值,及拟合的y值,用来和原数据进行对比,但是结果却不正确;
但是我却能正确的拟合出形如:
y=ax^3+bx^2+cx+d;
这样的方程;只需要改变一下funcs函数的内容就可以了。
我的问题是,为什么我不能正确拟合出方程(1)呢?希望高手能够指点,万分感谢!

以下是程序内容,用的是“Nemerical Recipes in C”这本书的程序!

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <conio.h>
#define SWAP(a,b) {temp=(a);(a)=(b);(b)=temp;}
#define NR_END 1
#define FREE_ARG char*
void main ()
{   void funcs(float x, float a[], float *y, float dyda[], int na);
void marqmin(float x[], float y[], float sig[], int ndata, float a[], int ia[],
            int ma, float **covar, float **alpha, float *chisq,
            void (*funcs)(float, float [], float *, float [], int), float *alamda);
float y[18]={0,168.3,168.5,170.,174.4,182.9,199.9,231.4,283.2,357.4,456.1,573.4,692.0,787.6,838.1,840,841,841.5},a[5]={1,1,1,1,1};
float x[18]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17};
float sig[18]={1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},th,mm;
float *alamda,**covar,**alpha,*beta,*chisq;
int ndata=17, ia[5]={1,1,1,1,1},ma=4,i;
alamda=(float *)malloc(1*sizeof(float));
covar=(float **)malloc(5*sizeof(float*));
alpha=(float **)malloc(5*sizeof(float*));
beta=(float *)malloc(10*sizeof(float));
chisq=(float *)malloc(1*sizeof(float));
    for(i=0;i<5;i++)
    {covar[i]=(float *)malloc(5*sizeof(float));
            alpha[i]=(float *)malloc(5*sizeof(float));}
*alamda=-1.0;
    marqmin(x,y,sig,ndata,a,ia,ma,covar,alpha,chisq,funcs,alamda);
        printf("chisq=%8.4f",*chisq);
        printf("\n");
    do
    {
        th=*chisq;
        marqmin(x,y,sig,ndata,a,ia,ma,covar,alpha,chisq,funcs,alamda);
        printf("th=%8.4f",th);
        printf("\n");
    }
    while(abs(*chisq-th)>0.001);
    *alamda=0.0;
    marqmin(x,y,sig,ndata,a,ia,ma,covar,alpha,chisq,funcs,alamda);
    for(int i=1;i<5;i++)
    {
        printf("%8.4f",a[i]);
        printf("\n");
    }
    printf("%8.4f",*alamda);
    for(i=1;i<15;i++)
    {
    mm=a[3]/(1+exp(a[1]+a[2]*x[i]))+a[4];
            printf("y[%d]=%8.2f",i,mm);
            printf("\n");
    }
    free(covar);
    free(alpha);
    free(alamda);
    free(beta);
    free(chisq);
    getchar();
}

void marqmin(float x[], float y[], float sig[], int ndata, float a[], int ia[],
            int ma, float **covar, float **alpha, float *chisq,
            void (*funcs)(float, float [], float *, float [], int), float *alamda)
{
float *vector(long nl, long nh);
float **matrix(long nrl, long nrh, long ncl, long nch);
void free_vector(float *v, long nl, long nh);
void free_matrix(float **m, long nrl, long nrh, long ncl, long nch);
void covsrt(float **covar, int ma, int ia[], int mfit);
void gaussj(float **a, int n, float **b, int m);
void mrqcof(float x[], float y[], float sig[], int ndata, float a[],
int ia[], int ma, float **alpha, float beta[], float *chisq,
void (*funcs)(float, float [], float *, float [], int));
int j,k,l;
static int mfit;
static float ochisq,*atry,*beta,*da,**oneda;
if (*alamda < 0.0) {
atry=vector(1,ma);
beta=vector(1,ma);
da=vector(1,ma);
for (mfit=0,j=1;j<=ma;j++)
if (ia[j]) mfit++;
oneda=matrix(1,mfit,1,1);
*alamda=0.001;
mrqcof(x,y,sig,ndata,a,ia,ma,alpha,beta,chisq,funcs);
ochisq=(*chisq);
for (j=1;j<=ma;j++) atry[j]=a[j];
}
for (j=1;j<=mfit;j++) { for (k=1;k<=mfit;k++) covar[j][k]=alpha[j][k];
covar[j][j]=alpha[j][j]*(1.0+(*alamda));
oneda[j][1]=beta[j];
}
for(k=1;k<=3;k++)
{for (j=1;j<=3;j++)
{printf("covar=%8.2f",covar[k][j]);}printf("\n");}
gaussj(covar,mfit,oneda,1);
for(k=1;k<=3;k++)
{for (j=1;j<=3;j++)
{printf("covar=%8.2f",covar[k][j]);}printf("\n");}
for (j=1;j<=mfit;j++) da[j]=oneda[j][1];
if (*alamda == 0.0) {
covsrt(covar,ma,ia,mfit);
covsrt(alpha,ma,ia,mfit);
free_matrix(oneda,1,mfit,1,1);
free_vector(da,1,ma);
free_vector(beta,1,ma);
free_vector(atry,1,ma);
return;
}
for (j=0,l=1;l<=ma;l++)
if (ia[l]) atry[l]=a[l]+da[++j];
mrqcof(x,y,sig,ndata,atry,ia,ma,covar,da,chisq,funcs);
if (*chisq < ochisq) {
*alamda *= 0.1;
ochisq=(*chisq);
for (j=1;j<=mfit;j++) {
for (k=1;k<=mfit;k++) alpha[j][k]=covar[j][k];
beta[j]=da[j];
}
for (l=1;l<=ma;l++) a[l]=atry[l];
} else {
*alamda *= 10.0;
*chisq=ochisq;
}
}

void funcs(float x, float a[], float *y, float dyda[], int na)
{   
    int i;
    float fac,ex,arg;
    *y=0.0;
    for(i=1;i<=na-1;i+=3)
    {
    fac=a[i]+a[i+1]*x;
    ex=exp(fac);
    arg=(1+ex)*(1+ex);
    *y=a[i+2]/(1+ex)+a[4];
    dyda[i]=(-a[i+2])*ex/arg;
    dyda[i+1]=(-a[i+2])*x*ex/arg;
    dyda[i+2]=1/(1+ex);
    dyda[i+3]=1;
    }
}

void mrqcof(float x[], float y[], float sig[], int ndata, float a[], int ia[],
int ma, float **alpha, float beta[], float *chisq,
void (*funcs)(float, float [], float *, float [], int))
{
float *vector(long nl, long nh);
void free_vector(float *v, long nl, long nh);
int i,j,k,l,m,mfit=0;
float ymod,wt,sig2i,dy,*dyda;
dyda=vector(1,ma);    
for (j=1;j<=ma;j++)
if (ia[j]) mfit++;
for (j=1;j<=mfit;j++) {
for (k=1;k<=j;k++) alpha[j][k]=0.0;
beta[j]=0.0;
}
*chisq=0.0;
for (i=1;i<=ndata;i++) {
(*funcs)(x[i],a,&ymod,dyda,ma);
sig2i=1.0/(sig[i]*sig[i]);
dy=y[i]-ymod;
for (j=0,l=1;l<=ma;l++) {
if (ia[l]) {
wt=dyda[l]*sig2i;
for (j++,k=0,m=1;m<=l;m++)
if (ia[m]) alpha[j][++k] += wt*dyda[m];
beta[j] += dy*wt;
}
}
*chisq += dy*dy*sig2i;
}
for (j=2;j<=mfit;j++)
for (k=1;k<j;k++) alpha[k][j]=alpha[j][k];
free_vector(dyda,1,ma);
}

void gaussj(float **a, int n, float **b, int m)
{
int *ivector(long nl, long nh);
void free_ivector(int *v, long nl, long nh);
int *indxc,*indxr,*ipiv;
int i,icol,irow,j,k,l,ll;
float big,dum,pivinv,temp;
indxc=ivector(1,n);indxr=ivector(1,n);
ipiv=ivector(1,n);
for (j=1;j<=n;j++) ipiv[j]=0;
for (i=1;i<=n;i++) {
big=0.0;
for (j=1;j<=n;j++)
if (ipiv[j] != 1)
for (k=1;k<=n;k++) {
if (ipiv[k] == 0) {
if (fabs(a[j][k]) >= big) {
big=fabs(a[j][k]);
irow=j;
icol=k;
}
} else if (ipiv[k] > 1) 
{printf("gaussj: Singular Matrix-1");
getchar();}
}
++(ipiv[icol]);
if (irow != icol) {
for (l=1;l<=n;l++) SWAP(a[irow][l],a[icol][l])
for (l=1;l<=m;l++) SWAP(b[irow][l],b[icol][l])
}
indxr[i]=irow;
indxc[i]=icol;
if (a[icol][icol] == 0.0)
{printf("gaussj: Singular Matrix-2");
getchar();}
pivinv=1.0/a[icol][icol];
a[icol][icol]=1.0;
for (l=1;l<=n;l++) a[icol][l] *= pivinv;
for (l=1;l<=m;l++) b[icol][l] *= pivinv;
for (ll=1;ll<=n;ll++)
if (ll != icol) {
dum=a[ll][icol];
a[ll][icol]=0.0;
for (l=1;l<=n;l++) a[ll][l] -= a[icol][l]*dum;
for (l=1;l<=m;l++) b[ll][l] -= b[icol][l]*dum;
}
}
for (l=n;l>=1;l--) {
if (indxr[l] != indxc[l])
for (k=1;k<=n;k++)
SWAP(a[k][indxr[l]],a[k][indxc[l]]);
}
free_ivector(ipiv,1,n);
free_ivector(indxr,1,n);
free_ivector(indxc,1,n);
}

void covsrt(float **covar, int ma, int ia[], int mfit)
{
int i,j,k;
float swap,temp;
for (i=mfit+1;i<=ma;i++)
for (j=1;j<=i;j++) covar[i][j]=covar[j][i]=0.0;
k=mfit;
for (j=ma;j>=1;j--) {
if (ia[j]) {
for (i=1;i<=ma;i++) SWAP(covar[i][k],covar[i][j])
for (i=1;i<=ma;i++) SWAP(covar[k][i],covar[j][i])
k--;
}
}
}
float *vector(long nl, long nh)
/* allocate a float vector with subscript range v[nl..nh] */
{
float *v;
v=(float *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(float)));
if (!v) 
{printf("allocation failure in vector()");
getchar();}
return v-nl+NR_END;
}
int *ivector(long nl, long nh)
/* allocate an int vector with subscript range v[nl..nh] */
{
int *v;
v=(int *)malloc((size_t) ((nh-nl+1+NR_END)*sizeof(int)));
if (!v) 
{printf("allocation failure in vector()");
getchar();}
return v-nl+NR_END;
}
float **matrix(long nrl, long nrh, long ncl, long nch)
/* allocate a float matrix with subscript range m[nrl..nrh][ncl..nch] */
{
long i, nrow=nrh-nrl+1,ncol=nch-ncl+1;
float **m;
/* allocate pointers to rows */
m=(float **) malloc((size_t)((nrow+NR_END)*sizeof(float*)));
if (!m) 
{printf("allocation failure 1 in matrix()");
getchar();}
m += NR_END;
m -= nrl;
/* allocate rows and set pointers to them */
m[nrl]=(float *) malloc((size_t)((nrow*ncol+NR_END)*sizeof(float)));
if (!m[nrl]) 
{printf("allocation failure 2 in matrix()");
getchar();}
m[nrl] += NR_END;
m[nrl] -= ncl;
for(i=nrl+1;i<=nrh;i++) m[i]=m[i-1]+ncol;
/* return pointer to array of pointers to rows */
return m;
}
void free_vector(float *v, long nl, long nh)
/* free a float vector allocated with vector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_ivector(int *v, long nl, long nh)
/* free an int vector allocated with ivector() */
{
free((FREE_ARG) (v+nl-NR_END));
}
void free_matrix(float **m, long nrl, long nrh, long ncl, long nch)
/* free a float matrix allocated by matrix() */
{
free((FREE_ARG) (m[nrl]+ncl-NR_END));
free((FREE_ARG) (m+nrl-NR_END));
}

回复列表 (共5个回复)

沙发

    为什么没有高人出来解答呢?
    经过试验,我还发现一些比较有趣的事情,我先用已知的x和参数a[1,2,3,4]通过公式:
y=a[3]/(1+exp(a[1]+a[2]*x[i]))+a[4];
计算出y值,然后用计算出的y[]和x[]通过上边的程序来拟合a[],以比较拟合的结果如何。最终发现,对于有的y[]和x[]组合,可以拟合非常好的结果;而有的则完全错误,是什么原因呢?
    是我在程序调用中出了问题还是这个程序本身存在缺陷呢?望高人解答!

板凳

    感谢大家的关注,我又回来了!
    为了解决这个问题,我又试了了另外一个Levenberg-Marquardt nonlinear least squares的程序-levmar(http://www.ics.forth.gr/~lourakis/levmar/)。但是,遗憾的是问题依然没有解决,遇到的问题都是一样的在解线性方程的时候得到——奇异矩阵(singular matrix)。最后输出的结果和实际相差十万八千里。不知道哪位在实际运用中碰到过类似的问题,怎么解决呢?
    我会继续这个问题,如果解决了会及时告诉大家。当然如果你们感兴趣的话,最后感谢大家的关注!

3 楼

    再次回来,问题圆满解决!
    最终还是使用的是levmar的算法(下载地址见上一回复)。之前用的是程序dlevmar_der来计算出现错误,使用dlevmar_dif(),问题解决!具体的差别经参考levmar的网页。另:NR的marqin算法只能解决比较简单的问题,如果高度非线性则拟合不出正确结果(个人经验,或许是我不会用)。
    感谢大家的关注,希望能对大家有所帮助!

4 楼

谢谢

5 楼


您好!
    我现在遇到和您当初一样的问题在网上查到好多资料没有找到相关的信息,您当初解决了这个问题,希望指导下,我求解的方程为 y=(-a*ln((x/b-1+c)/c))^d 有四个点(xi,yi) i=1~4,求解a,b,c,d ,我现在根本不知道怎么来拟合这个方程求解,希望您指导下,如果有代码的话希望发一份给我,再次谢谢!邮箱为;kairayang@ksrii.com.cn QQ:505616016

我来回复

您尚未登录,请登录后再回复。点此登录或注册