- 积分
- 7
- 注册时间
- 2002-9-10
- 仿真币
-
- 最后登录
- 1970-1-1
|
楼主 |
发表于 2009-10-12 08:45:35
|
显示全部楼层
来自 山东淄博
以下实例选自Matlab中文论坛:http://www.ilovematlab.cn/thread-52145-1-5.html
该实例反应了Forcal的解题能力和运行效率。
实例:三房室模型给药后,药物在房室1和2,2和3间转运,并经房室3排出。假定转运均符合一级速率模型,则各房室药物量(X1,X2,X3)与时间t的关系如下:
dX1/dt=-K12*X1+K21*X2
dX2/dt=(-K21-K23)*X2+K12*X1+K32*X3
dX3/dt=(-K32-K30)*X3+K23*X2
已知初值t=0时:X1=100, X2=X3=0 。
实验值:
t=1: X1=90, X2=8,X3=2
t=3: X1=73, X2=19,X3=7
t=5: X1=60, X2=14,X3=23
求拟合参数K12,K21,K23,K32,和K30。
限制条件:K12,K21,K23,K32,和K30均大于零;X1+X2+X3小于等于100(总量不能超过给药量)。
拟合可以用非线形最小二乘法,即求取minΣ(y实验值-y真实值)2时的K值。
当然这个问题的结构相对简单,所以也可以求出各方程积分式,然后用EXCEL规划求解拟合。这是药物动力学中的方法。但是对于更复杂的系统,如十房室以上,积分式将非常复杂,手工求方程积分式几近不可能。
算法分析:用徐士良算法库XSLSF中的求n维极值的复形调优法函数cplx求最优参数值K12,K21,K23,K32,和K30,目标函数为理论值与实验值差的平方和。根据提供的微分方程初值,用连分式法对微分方程组积分一步函数pbs1求理论值。理论值与实验值差的平方和最小时获得参数的最优解。
- //Forcal代码
- //若长时间没有结果,按Ctrl+Alt+q退出Forcal运行
- i: OutVector(p:k,i)= k=FCDLen(p),printff{"\r\n"},i=0,(i<k).while{printff{"{1,r,14.6}",get[p,i]},i++},printff{"\r\n"}; //输出一维数组
- !using["XSLSF","sys"]; //使用命名空间XSLSF和sys
- f(t,X1,X2,X3,dX1,dX2,dX3::k12,k21,k23,k32,k30)={ //函数定义,连分式法对微分方程组积分一步函数pbs1中要用到
- dX1=-k12*X1+k21*X2,
- dX2=(-k21-k23)*X2+k12*X1+k32*X3,
- dX3=(-k32-k30)*X3+k23*X2
- };
- t_i(hf,a,step,eps,t1,t2,x1,x2,x3:h,i)= //用于约束条件定义
- {
- h=(t2-t1)/step,
- { pbs1[hf,t1,a,h,eps], //连分式法对微分方程组积分一步函数pbs1,hf为函数f的句柄
- t1=t1+h
- }.until[abs(t1-t2)<h/2],
- a.getra(0,&x1,&x2,&x3)
- };
- t_i_2(hf,a,step,eps,t1,t2,x_1,x_2,x_3:x1,x2,x3,h,i)= //用于计算目标函数
- {
- h=(t2-t1)/step,
- { pbs1[hf,t1,a,h,eps], //连分式法对微分方程组积分一步函数pbs1,hf为函数f的句柄
- t1=t1+h
- }.until[abs(t1-t2)<h/2],
- a.getra(0,&x1,&x2,&x3),
- (x1-x_1)^2+(x2-x_2)^2+(x3-x_3)^2
- };
- J(_k12,_k21,_k23,_k32,_k30:t1:hf,Array,step,eps,k12,k21,k23,k32,k30)={ //目标函数定义
- k12=_k12,k21=_k21,k23=_k23,k32=_k32,k30=_k30,
- t1=0, Array.setra(0,100,0,0),
- t_i_2(hf,Array,step,eps: &t1, 1 : 90, 8, 2)+
- t_i_2(hf,Array,step,eps: &t1, 3 : 73, 19, 7)+
- t_i_2(hf,Array,step,eps: &t1, 5 : 60, 14, 23)
- };
- S(_k12,_k21,_k23,_k32,_k30,c0,c1,c2,d0,d1,d2,w0,w1,w2:t1,x1,x2,x3:hf,Array,step,eps,k12,k21,k23,k32,k30)= //约束条件定义
- {
- c0=0, c1=0, c2=0,
- d0=100, d1=100, d2=100,
- k12=_k12,k21=_k21,k23=_k23,k32=_k32,k30=_k30,
- t1=0, Array.setra(0,100,0,0),
- t_i(hf,Array,step,eps: &t1, 1 : &x1, &x2, &x3), w0=x1+x2+x3,
- t_i(hf,Array,step,eps: &t1, 3 : &x1, &x2, &x3), w1=x1+x2+x3,
- t_i(hf,Array,step,eps: &t1, 5 : &x1, &x2, &x3), w2=x1+x2+x3
- };
- main(:a,b,alpha,_eps,k,x,xx,dx,i,tm:hf,Array,step,eps)=
- {
- tm=clock(),
- hf=HFor("f"),
- Array=new[rtoi(real_s),rtoi(45)],
- step=20,eps=1e-6, //step越大,eps越小越精确,用于积分一步函数pbs1
- a=new[rtoi(real_s),rtoi(5),rtoi(EndType),1e-50,1e-50,1e-50,1e-50,1e-50], //存放常量约束条件中的变量的下界
- b=new[rtoi(real_s),rtoi(5),rtoi(EndType), 1, 1, 1, 1, 1 ], //存放常量约束条件中的变量的上界
- x=new[rtoi(real_s),rtoi(6),rtoi(EndType), 0.1, 0.1, 0.1, 0.1, 0.1 ], //其中前n个分量存放初始复形的第一个顶点坐标值(要求满足所有的约束条件),返回时存放极小值点各坐标值。最后一个分量返回极小值。
- xx=new[rtoi(real_s),rtoi(6),rtoi(10)],
- _eps=1e-10, alpha=1.01,k=800,dx=1e-4, //_eps控制精度要求;alpha为反射系数;k为允许的最大迭代次数;dx为微小增量。需选择合适的alpha,_eps,k,dx值求解。
- i=cplx[HFor("J"),HFor("S"),a,b,alpha,_eps,x,xx,k,dx],
- printff{"\r\n实际迭代次数={1,r}\r\n",i},
- OutVector[x],
- delete[Array],delete[a],delete[b],delete[x],delete[xx] ,
- printff{"\r\n程序运行{1,r}秒。\r\n",[clock()-tm]/1000}
- };
复制代码
结果:
-
- 实际迭代次数=661.
- 0.112939 7.11927e-002 0.346605 1.514e-007 9.5563e-003 33.4978
- 程序运行9.3279999999999994秒。
复制代码
徐士良算法库XSLSF中的数值计算函数取自徐士良主编的《C常用算法程序集》第二版。在XSLSF中,Forcal仅对徐士良全部数值算法进行了封装,对算法本身未作更改。因而容易比较Forcal程序与C/C++程序的运行效率。
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- #include "math.h"
- double k12,k21,k23,k32,k30;
- double Array[3];
- double aa[5]={1.0e-50,1.0e-50,1.0e-50,1.0e-50,1.0e-50};
- double bb[5]={ 1, 1, 1, 1, 1 };
- double xx[6]={ 0.1, 0.1, 0.1, 0.1, 0.1};
- double xxx[6][10];
- double step, eps;
- void gpbs1f(double t,double *y,int n,double *d)
- {
- d[0]=-k12*y[0]+k21*y[1];
- d[1]=(-k21-k23)*y[1]+k12*y[0]+k32*y[2];
- d[2]=(-k32-k30)*y[2]+k23*y[1];
- return;
- }
- static void rkt1(double t,double h,int n,double *y,double *b,double *d,double *e)
- { int i,k;
- double a[4],tt;
- a[0]=h/2.0; a[1]=a[0]; a[2]=h; a[3]=h;
- gpbs1f(t,y,n,d);
- for (i=0; i<=n-1; i++) { b[i]=y[i]; e[i]=y[i];}
- for (k=0; k<=2; k++)
- { for (i=0; i<=n-1; i++)
- { y[i]=e[i]+a[k]*d[i];
- b[i]=b[i]+a[k+1]*d[i]/3.0;
- }
- tt=t+a[k];
- gpbs1f(tt,y,n,d);
- }
- for (i=0; i<=n-1; i++)
- y[i]=b[i]+h*d[i]/6.0;
- return;
- }
- void gpbs1(double t,double h,int n,double *y,double eps)
- { int i,j,k,m,nn,it;
- double x,hh,dd,q,p,g[10],*b,*d,*u,*v,*w,*e;
- b=(double *)malloc(10*n*sizeof(double));
- d=(double *)malloc(n*sizeof(double));
- u=(double *)malloc(n*sizeof(double));
- v=(double *)malloc(n*sizeof(double));
- w=(double *)malloc(n*sizeof(double));
- e=(double *)malloc(n*sizeof(double));
- for (j=0; j<=n-1; j++) v[j]=y[j];
- x=t; nn=1; hh=h; g[0]=hh;
- rkt1(x,hh,n,y,w,d,e);
- for (j=0; j<=n-1; j++)
- { b[j]=y[j]; u[j]=y[j];}
- k=1; it=1;
- while (it==1)
- { nn=nn+nn; hh=hh/2.0; it=0;
- g[k]=hh;
- for (j=0; j<=n-1; j++) y[j]=v[j];
- t=x;
- for (j=0; j<=nn-1; j++)
- { rkt1(t,hh,n,y,w,d,e);
- t=t+hh;
- }
- for (j=0; j<=n-1; j++)
- { dd=y[j]; m=0;
- for (i=0; i<=k-1; i++)
- if (m==0)
- { q=dd-b[i*n+j];
- if (fabs(q)+1.0==1.0) m=1;
- else dd=(g[k]-g[i])/q;
- }
- b[k*n+j]=dd;
- if (m!=0) b[k*n+j]=1.0e+35;
- }
- for (j=0; j<=n-1; j++)
- { dd=0.0;
- for (i=k-1; i>=0; i--)
- dd=-g[i]/(b[(i+1)*n+j]+dd);
- y[j]=dd+b[j];
- }
- p=0.0;
- for (j=0; j<=n-1; j++)
- { q=fabs(y[j]-u[j]);
- if (q>p) p=q;
- }
- if ((p>=eps)&&(k<7))
- { for (j=0; j<=n-1; j++) u[j]=y[j];
- k=k+1; it=1;
- }
- }
- free(b); free(d); free(u); free(v); free(w); free(e);
- return;
- }
- double rn(double *rr)
- { int m;
- double y,s,u,v;
- s=65536.0; u=2053.0; v=13849.0;
- *rr=u*(*rr)+v; m=(int)(*rr/s); *rr=*rr-m*s;
- y=*rr/s;
- return(y);
- }
- void t_i(double *a,double step,double eps,double &t1,double t2,double &x1,double &x2,double &x3) //用于约束条件定义
- { double h;
- h=(t2-t1)/step;
- do{ gpbs1(t1,h,3,a,eps); //连分式法对微分方程组积分一步函数gpbs1
- t1=t1+h;
- }while(abs(t1-t2)>=h/2);
- x1=a[0];x2=a[1];x3=a[2];
- };
- double t_i_2(double *a,double step,double eps,double &t1,double t2,double x_1,double x_2,double x_3) //用于计算目标函数
- { double h;
- h=(t2-t1)/step;
- do{ gpbs1(t1,h,3,a,eps); //连分式法对微分方程组积分一步函数gpbs1
- t1=t1+h;
- }while(abs(t1-t2)>=h/2);
- return (a[0]-x_1)*(a[0]-x_1)+(a[1]-x_2)*(a[1]-x_2)+(a[2]-x_3)*(a[2]-x_3);
- };
- double jcplxf(double *x,int n)
- { double t1,s=0;
- k12=x[0];k21=x[1];k23=x[2];k32=x[3];k30=x[4];
- t1=0; Array[0]=100; Array[1]=0; Array[2]=0;
- s=s+ t_i_2(Array,step,eps,t1, 1 , 90, 8, 2);
- s=s+ t_i_2(Array,step,eps,t1, 3 , 73, 19, 7);
- return s+t_i_2(Array,step,eps,t1, 5 , 60, 14, 23);
- }
- void jcplxs(int n,int m,double *x,double *c,double *d,double *w)
- { double x1,x2,x3,t1;
- c[0]=0.0; c[1]=0.0; c[2]=0.0;
- d[0]=100; d[1]=100; d[2]=100;
- k12=x[0];k21=x[1];k23=x[2];k32=x[3];k30=x[4];
- t1=0; Array[0]=100; Array[1]=0; Array[2]=0;
- t_i(Array,step,eps,t1, 1 ,x1, x2, x3); w[0]=x1+x2+x3;
- t_i(Array,step,eps,t1, 3 ,x1, x2, x3); w[1]=x1+x2+x3;
- t_i(Array,step,eps,t1, 5 ,x1, x2, x3); w[2]=x1+x2+x3;
- return;
- }
- int jcplx(int n,int m,double *a,double *b,double alpha,double eps,double *x,double *xx,int k,double dx)
- {
- int r,g,i,j,it,kt,jt,kk;
- double fj,fr,fg,z,rr,*c,*d,*w,*xt,*xf;
- c=(double *)malloc(m*sizeof(double));
- d=(double *)malloc(m*sizeof(double));
- w=(double *)malloc(m*sizeof(double));
- xt=(double *)malloc(n*sizeof(double));
- xf=(double *)malloc(n*sizeof(double));
- rr=0.0;
- for (i=0; i<=n-1; i++)
- xx[i*n*2]=x[i];
- xx[n*n*2]=jcplxf(x,n);
- for (j=1; j<=2*n-1; j++)
- { for (i=0; i<=n-1; i++)
- { xx[i*n*2+j]=a[i]+(b[i]-a[i])*rn(&rr);
- x[i]=xx[i*n*2+j];
- }
- it=1;
- while (it==1)
- { it=0; r=0; g=0;
- while ((r<n)&&(g==0))
- { if ((a[r]<=x[r])&&(b[r]>=x[r])) r=r+1;
- else g=1;
- }
- if (g==0)
- { jcplxs(n,m,x,c,d,w);
- r=0;
- while ((r<m)&&(g==0))
- { if ((c[r]<=w[r])&&(d[r]>=w[r])) r=r+1;
- else g=1;
- }
- }
- if (g!=0)
- { for (r=0; r<=n-1; r++)
- { z=0.0;
- for (g=0; g<=j-1; g++)
- z=z+xx[r*n*2+g]/(1.0*j);
- xx[r*n*2+j]=(xx[r*n*2+j]+z)/2.0;
- x[r]=xx[r*n*2+j];
- }
- it=1;
- }
- else xx[n*n*2+j]=jcplxf(x,n);
- }
- }
- kk=1; it=1;
- while (it==1)
- { it=0;
- fr=xx[n*n*2]; r=0;
- for (i=1; i<=2*n-1; i++)
- if (xx[n*n*2+i]>fr)
- { r=i; fr=xx[n*n*2+i];}
- g=0; j=0; fg=xx[n*n*2];
- if (r==0)
- { g=1; j=1; fg=xx[n*n*2+1];}
- for (i=j+1; i<=2*n-1; i++)
- if (i!=r)
- if (xx[n*n*2+i]>fg)
- { g=i; fg=xx[n*n*2+i];}
- for (i=0; i<=n-1; i++)
- { xf[i]=0.0;
- for (j=0; j<=2*n-1; j++)
- if (j!=r)
- xf[i]=xf[i]+xx[i*n*2+j]/(2.0*n-1.0);
- xt[i]=(1.0+alpha)*xf[i]-alpha*xx[i*n*2+r];
- }
- jt=1;
- while (jt==1)
- { jt=0;
- z=jcplxf(xt,n);
- while (z>fg)
- { for (i=0; i<=n-1; i++)
- xt[i]=(xt[i]+xf[i])/2.0;
- z=jcplxf(xt,n);
- }
- j=0;
- for (i=0; i<=n-1; i++)
- { if (a[i]>xt[i])
- { xt[i]=xt[i]+dx; j=1;}
- if (b[i]<xt[i])
- { xt[i]=xt[i]-dx; j=1;}
- }
- if (j!=0) jt=1;
- else
- { jcplxs(n,m,xt,c,d,w);
- j=0; kt=1;
- while ((kt==1)&&(j<m))
- { if ((c[j]<=w[j])&&(d[j]>=w[j])) j=j+1;
- else kt=0;
- }
- if (j<m)
- { for (i=0; i<=n-1; i++)
- xt[i]=(xt[i]+xf[i])/2.0;
- jt=1;
- }
- }
- }
- for (i=0; i<=n-1; i++)
- xx[i*n*2+r]=xt[i];
- xx[n*n*2+r]=z;
- fr=0.0; fg=0.0;
- for (j=0; j<=2*n-1; j++)
- { fj=xx[n*n*2+j];
- fr=fr+fj/(2.0*n);
- fg=fg+fj*fj;
- }
- fr=(fg-2.0*n*fr*fr)/(2.0*n-1.0);
- if (fr>=eps)
- { kk=kk+1;
- if (kk<k) it=1;
- }
- }
- for (i=0; i<=n-1; i++)
- { x[i]=0.0;
- for (j=0; j<=2*n-1; j++)
- x[i]=x[i]+xx[i*n*2+j]/(2.0*n);
- }
- z=jcplxf(x,n); x[n]=z;
- free(c); free(d); free(w);
- free(xt); free(xf);
- return(kk);
- }
- int main(int argc, char *argv[])
- {
- clock_t tm;
- double _eps,alpha,dx;
- int k,i;
- tm=clock();
- step=20;eps=1e-6; //step越大,eps越小越精确,用于积分一步的变步长欧拉方法
- _eps=1e-10; alpha=1.01;k=800;dx=1e-4; //_eps控制精度要求;alpha为反射系数;k为允许的最大迭代次数;dx为微小增量。需选择合适的alpha,_eps,k,dx值求解。
- i=jcplx(5,3,aa,bb,alpha,_eps,xx,&xxx[0][0],k,dx);
- printf("\r\n实际迭代次数=%d\r\n",i);
- printf("%12.6e, %12.6e, %12.6e, %12.6e, %12.6e, %12.6e\n",xx[0],xx[1],xx[2],xx[3],xx[4],xx[5]);
- printf("程序运行 %e 秒。\n", double(clock()-tm)/1000.0);
- }
复制代码
结果:
-
- 实际迭代次数=661
- 1.129389e-001, 7.119267e-002, 3.466046e-001, 1.513995e-007, 9.556298e-003, 3.349781e+001
- 程序运行 3.500000e+000 秒。
复制代码
可以看出,Forcal运行结果与C/C++完全一致,但效率约为C/C++的30%左右,是可以接受的。但Forcal程序代码的书写难度远远小于C/C++,可大幅提高用户的工作效率。 |
|