苏州吴中甪直最新招聘:最优法与黄金分割

来源:百度文库 编辑:九乡新闻网 时间:2024/05/16 05:02:07

优选法应用在我国从70年代初,华罗庚先生最初研究推广并大量应用。对编程领域某些解决问题的方法有所帮助。其实数学上的很多方法可以解决一个程序时间和空间最优复杂度的问题。希望对大家有用……
优选法,是以数学原理(即中国的古代黄金分割)为指导,用最可能少的试验次数,尽快找到生产和科学实验中最优方案的一种科学试验的方法。例如:在现代体育实践的科学实验中,怎样选取最合适的配方、配比;寻找最好的操作和工艺条件;找出产品的最合理的设计参数,使产品的质量最好,产量最多,或在一定条件下使成本最低,消耗原料最少,生产周期最短等。把这种最合适、最好、最合理的方案,一般总称为最优;把选取最合适的配方、配比,寻找最好的操作和工艺条件,给出产品最合理的设计参数,叫做优选。也就是根据问题的性质在一定条件下选取最优方案。最简单的最优化问题是极值问题,这样问题用微分学的知识即可解决。
  实际工作中的优选问题 ,即最优化问题,大体上有两类:

           一类是求函数的极值;如果目标函数有明显的表达式,一般可用微分法、变分法、极大值原理或动态规划等分析方法求解(间接选优)。

           另一类是求泛函的极值。如果目标函数的表达式过于复杂或根本没有明显的表达式,则可用数值方法或试验最优化等直接方法求解(直接选优)。
 
        优选法的优点:用较少的试验次数和外在条件,缩短时间和空间的消费、提高质量,达到解决问题的最经济的方法。

优选法基本步骤
  1)选定优化判据(试验指标),确定影响因素,优选数据是用来判断优选程度的依据。
  2)优化判据与影响因素直接的关系称为目标函数。
  3)优化计算。优化(选)试验方法一般分为两类: 

                         分析法:同步试验法 ;黑箱法:循序试验法

优选法的分类
  优选法分为单因素方法和多因素方法两类。单因素方法有平分法、0.618法(黄金分割法)、分数法、分批试验法等;多因素方法很多.但在理论上都不完备.主要有降维法、爬山法、单纯形调优胜。随机试验法、试验设计法等。优选法已在体育领域得到广泛应用。
  1.单因素优选法
如果在试验时,只考虑一个对目标影响最大的因素,其它因素尽量保持不变,则称为单因素问题。这个概念太复杂 了,通俗一点,把解决问题的结果看做方案的函数,当可供选择的方案分布在一条直线上时,其结果可看作一个单变量函数,此时的最优化问题为单因素优化问题。一般步骤:
  (1)首先应估计包含最优点的试验范围,如果用a表示下限,b表示上限,试验范围为[a,b];
  (2)然后将试验结果和因素取值的关系写成数学表达式,不能写出表达式时,就要确定评定结果好坏的方法。

一般可采用对分法、黄金分割法及牛顿一元函数求根的切线法与割线法等方法解决.也可用最小二乘法解决。

 

当多种方案是在一个平面上更高维数的空间中连续分布时,叫做二因素或多因索优化问题,此时可采用牛顿方法或梯度方法求解。若选择方案的分布是离散的,则可采用单纯形等方法解决。
  

      2.多因素优选法
  多因素问题:首先对各个因素进行分析,找出主要因素,略去次要因素,划“多”为“少”,以利于解决问题。

 

 

 

黄金分割法(又称0.618法)是用来求单峰函数的max(或min)的算法。这是一种搜索法,不需要利用函数的导数值。

所用到的 0.618 是黄金分割比的近似值。
黄金分割比  = (sqrt(5)-1)/2 ~=  0.61803398874989484820... 又等于 斐波那契数列的 a(n)/a(n+1), n->∞

下边是我检索到的一些范例程序,没有经过调试,有兴趣自己研究一下。

Vb代码
  1. Const k2 = 0.618033988749895  
  2.   
  3.  Const k1 = 1 - k2   
  4.   
  5. Function M618(ByVal a#, ByVal b#, r#, L%, Nr%) As Double ' [a,b]-区间,r-精度,L=1求峰,L=0求谷  
  6.   
  7.  Dim u#, v#, tu#, tv#   
  8.   
  9.     If a > b Then   
  10.   
  11.       Text2.Text = a:   
  12.   
  13.       Text1.Text = b   
  14.   
  15.     a = Val(Text1.Text):  
  16.   
  17.     b = Val(Text2.Text)   
  18.   
  19. End If '开始计算   
  20.   
  21.     u = a + k1 * (b - a)   
  22.   
  23.     v = a + b - u  
  24.   
  25.     tu = ff(u)  
  26.   
  27.     tv = ff(v)  
  28.   
  29.     Nr = 0 '   
  30.   
  31. Do Until Abs(u - v) < r Or Nr > 70  
  32.   
  33.  If tu < tv Xor L = 0 Then '判断函数值的大小   
  34.   
  35.    a = u u = v:   
  36.   
  37.    tu = tv 'u 点不再计算,要求K2要严格等于黄金分割比   
  38.   
  39.     v = a + k2 * (b - a) '求出新的v  
  40.   
  41.     tv = ff(v) Else b = v v = u:   
  42.   
  43.     tv = tu u = a + k1 * (b - a)   
  44.   
  45.     tu = ff(u) End If   
  46.   
  47.     Nr = Nr + 1 Loop   
  48.   
  49.     M618 = (u + v) / 2  
  50.   
  51.  End Function  
Const k2 = 0.618033988749895Const k1 = 1 - k2Function M618(ByVal a#, ByVal b#, r#, L%, Nr%) As Double ' [a,b]-区间,r-精度,L=1求峰,L=0求谷Dim u#, v#, tu#, tv#If a > b ThenText2.Text = a:Text1.Text = ba = Val(Text1.Text):b = Val(Text2.Text)End If '开始计算u = a + k1 * (b - a)v = a + b - utu = ff(u)tv = ff(v)Nr = 0 'Do Until Abs(u - v) < r Or Nr > 70If tu < tv Xor L = 0 Then '判断函数值的大小a = u u = v:tu = tv 'u 点不再计算,要求K2要严格等于黄金分割比v = a + k2 * (b - a) '求出新的vtv = ff(v) Else b = v v = u:tv = tu u = a + k1 * (b - a)tu = ff(u) End IfNr = Nr + 1 LoopM618 = (u + v) / 2End Function

  

 

黄金分割法求解 一元二次函数f(x)=x2+2x+1

C代码
  1. #include "math.h"   
  2.   
  3. #include "stdio.h"   
  4.   
  5. #define f(x) x*x+2*x+1 //函数功能是用黄金分割法实现求一元函数 的最优解   
  6.   
  7. double hj(double *a,double *b,double e,int *n)   
  8.   
  9. {   
  10.   
  11. double x1,x2,s;  
  12.   
  13.  if(fabs(*b-*a)<=e) s=f((*b+*a)/2);  
  14.   
  15.  else { x1=*a+0.382*(*b-*a); x2=*a+0.618*(*b-*a);  
  16.   
  17.  if(f(x1)>f(x2)) *a=x1; else *b=x2;   
  18.   
  19. *n=*n+1; s=hj(a,b,e,n);   
  20.   
  21. }   
  22.   
  23. return s;   
  24.   
  25. }   
  26.   
  27. main() {  
  28.   
  29.  double s,a,b,e; int n=0;   
  30.   
  31.  scanf("%lf %lf %lf",&a,&b,&e); // 输入区间[a,b]和精度e的值   
  32.   
  33.  s=hj(&a,&b,e,&n); //调用hj函数,其中n代表迭代次数   
  34.   
  35.  printf("a=%lf,b=%lf,s=%lf,n=%d\n",a,b,s,n);   
  36.   
  37. }   
  38.   
  39. 运行时:输入:0.6 0.5 0.1 输出结果为: 0.6 0.5 0.1 a=0.600000,b=0.500000,s=2.402500,n=0  
#include "math.h"#include "stdio.h"#define f(x) x*x+2*x+1 //函数功能是用黄金分割法实现求一元函数 的最优解double hj(double *a,double *b,double e,int *n){double x1,x2,s;if(fabs(*b-*a)<=e) s=f((*b+*a)/2);else { x1=*a+0.382*(*b-*a); x2=*a+0.618*(*b-*a);if(f(x1)>f(x2)) *a=x1; else *b=x2;*n=*n+1; s=hj(a,b,e,n);}return s;}main() {double s,a,b,e; int n=0;scanf("%lf %lf %lf",&a,&b,&e); // 输入区间[a,b]和精度e的值s=hj(&a,&b,e,&n); //调用hj函数,其中n代表迭代次数printf("a=%lf,b=%lf,s=%lf,n=%d\n",a,b,s,n);}运行时:输入:0.6 0.5 0.1 输出结果为: 0.6 0.5 0.1 a=0.600000,b=0.500000,s=2.402500,n=0

 

 

 

 

0.618算法优化子程序,程序包含4个C文件mhjfgf.c、funct.c、jtf.c、hjfgf.c

C代码
  1. //mhjfgf.c代码如下:  
  2. #include "hjfgf.c"  
  3. main()  
  4. {double xx[1],a[1],b[1],ff;  
  5. double s[]={1},x0[]={0};  
  6. jtf(x0,0.1,s,1,a,b);  
  7. ff=gold(a,b,0.00001,1,xx);  
  8. printf("\nx[0]=%f,,ff=%f",xx[0],ff);  
  9. getch();  
  10. }  
  11.   
  12.   
  13. //funct.c代码如下:  
  14. #include "stdio.h"  
  15. #include "stdlib.h"  
  16. #include "math.h"  
  17. double objf(double x[])  
  18. {double ff;  
  19. ff=8*pow(x[0],3)-2*x[0]*x[0]-7*x[0]+3;  
  20. return(ff);  
  21. }  
  22.   
  23.   
  24. //jtf.c代码如下:  
  25. #include "funct.c"  
  26. void jtf(double x0[],double h0,double s[],int n,double a[],double b[])  
  27. {int i;  
  28. double *x[3],h,f1,f2,f3;  
  29. for(i=0;i<3;i++)  
  30.   x[i]=(double *)malloc(n*sizeof(double));  
  31.   h=h0;  
  32. for(i=0;i
  33.   *(x[0]+i)=x0[i];  
  34. f1=objf(x[0]);  
  35. for(i=0;i
  36.   *(x[1]+i)=*(x[0]+i)+h*s[i];  
  37.   f2=objf(x[1]);  
  38. if(f2>=f1)  
  39.   {h=-h0;  
  40.     for(i=0;i
  41.     *(x[2]+i)=*(x[0]+i);  
  42.    f3=f1;  
  43.     for(i=0;i
  44.     {*(x[0]+i)=*(x[1]+i);  
  45.      *(x[1]+i)=*(x[2]+i);  
  46.     }  
  47.    f1=f2;  
  48.    f2=f3;  
  49.    }  
  50.    for(;;)  
  51.    {h=2*h;  
  52.      for(i=0;i
  53.      *(x[2]+i)=*(x[1]+i)+h*s[i];  
  54.    f3=objf(x[2]);  
  55.    if(f2
  56.    else  
  57.     { for(i=0;i
  58.        {*(x[0]+i)=*(x[1]+i);  
  59.         *(x[1]+i)=*(x[2]+i);  
  60.        }  
  61.       f1=f2;  
  62.       f2=f3;  
  63.     }  
  64.    }  
  65.    if(h<0)  
  66.     for(i=0;i
  67.     {a[i]=*(x[2]+i);  
  68.      b[i]=*(x[0]+i);  
  69.     }  
  70.    else  
  71.     for(i=0;i
  72.     {a[i]=*(x[0]+i);  
  73.      b[i]=*(x[2]+i);  
  74.      }  
  75.    for(i=0;i<3;i++)  
  76.    free(x[i]);  
  77. }  
  78.   
  79.   
  80. //hjfgf.c代码如下:  
  81. #include "jtf.c"  
  82. double gold(double a[],double b[],double eps,int n,double xx[])  
  83. {int i;  
  84. double f1,f2,*x[2],ff,q,w;  
  85. for(i=0;i<2;i++)  
  86.   x[i]=(double *)malloc(n*sizeof(double));  
  87. for(i=0;i
  88.   {*(x[0]+i)=a[i]+0.618*(b[i]-a[i]);  
  89.    *(x[1]+i)=a[i]+0.382*(b[i]-a[i]);  
  90.   }  
  91.   f1=objf(x[0]);  
  92.   f2=objf(x[1]);  
  93.   do  
  94.    {if(f1>f2)  
  95.      {for(i=0;i
  96.       {b[i]=*(x[0]+i);  
  97.        *(x[0]+i)=*(x[1]+i);  
  98.        }  
  99.      f1=f2;  
  100.      for(i=0;i
  101.       *(x[1]+i)=a[i]+0.382*(b[i]-a[i]);  
  102.      f2=objf(x[1]);  
  103.      }  
  104.     else  
  105.      { for(i=0;i
  106.        {a[i]=*(x[1]+i);  
  107.        *(x[1]+i)=*(x[0]+i);}  
  108.      f2=f1;  
  109.     for(i=0;i
  110.      *(x[0]+i)=a[i]+0.618*(b[i]-a[i]);  
  111.     f1=objf(x[0]);  
  112.      }  
  113.   q=0;  
  114.   for(i=0;i
  115.    q=q+(b[i]-a[i])*(b[i]-a[i]);  
  116.   w=sqrt(q);  
  117.   }while(w>eps);  
  118.   for(i=0;i
  119.    xx[i]=0.5*(a[i]+b[i]);  
  120.   ff=objf(xx);  
  121.   for(i=0;i<2;i++)  
  122.   free(x[i]);  
  123.   return(ff);  
  124. }  
//mhjfgf.c代码如下:#include "hjfgf.c"main(){double xx[1],a[1],b[1],ff;double s[]={1},x0[]={0};jtf(x0,0.1,s,1,a,b);ff=gold(a,b,0.00001,1,xx);printf("\nx[0]=%f,,ff=%f",xx[0],ff);getch();}//funct.c代码如下:#include "stdio.h"#include "stdlib.h"#include "math.h"double objf(double x[]){double ff;ff=8*pow(x[0],3)-2*x[0]*x[0]-7*x[0]+3;return(ff);}//jtf.c代码如下:#include "funct.c"void jtf(double x0[],double h0,double s[],int n,double a[],double b[]){int i;double *x[3],h,f1,f2,f3;for(i=0;i<3;i++)x[i]=(double *)malloc(n*sizeof(double));h=h0;for(i=0;i=f1){h=-h0;for(i=0;if2){for(i=0;ieps);for(i=0;i

 

 

前面提到过黄金分割与斐波那契(Fibonacci)数列的相同之处,现在对斐波那契作一些了解,以及世界十大难题中其一“兔子问题”。

★ 扩展(摘录):
    斐波那契是意大利的数学家。他是一个商人的儿子。儿童时代跟随父亲到了阿尔及利亚,在那里学到了许多阿拉伯的算术和代数知识,从而对数学产生了浓厚的兴趣。
  长大以后,因为商业贸易关系,他走遍了许多国家,到过埃及、叙利亚、希腊、西西里和法兰西。每到一处他都留心搜集数学知识。回国后,他把搜集到的算术和代数材料,进行研究、整理,编写成一本书,取名为《算盘之书》,于1202年正式出版。
这本书是欧洲人从亚洲学来的算术和代数知识的整理和总结,它推动了欧洲数学的发展。其中有一道“兔子数目”的问题是这样的:一个人到集市上买了一对小兔子,一个月后,这对小兔子长成一对大兔子。然后这对大兔子每过一个月就可以生一对小兔子,而每对小兔子也都是经过一个月可以长成大兔子,长成大兔后也是每经过一个月就可以生一对小兔子。那么,从此人在市场上买回那对小兔子算起,每个月后,他拥有多少对小兔子和多少对大兔子?
  这是一个有趣的问题。当你将小兔子和大兔子的对数算出以后,你将发现这是一个很有规律的数列,而且这个数列与一些自然现象有关。人们为了纪念这位兔子问题的创始人,就把这个数列称为“斐波那契数列”。 又找到了这么一段话:

规律表:

月数 小兔 中兔 老兔 总数
 1    1    0    0    1
 2    0    1    0    1
 3    1    0    1    2
 4    1    1    1    3
 5    2    1    2    5
 6    3    2    3    8
 7    5    3    5   13

   在计算每一行时,大兔数为上月的大兔数加上月的中兔数,中兔数为上月的小兔数,小兔数为本月的大兔数,算总数为本月的小兔数加本月的中兔数加本月的大兔数。在观察总数的过程中找出了规律:总数的第一、二月都是1,以后的每一月是前两月的和。数列为1,1,2,3,5,8,13,21,34,55,……

   当n=50时,后项与前项的比是1.61803398874989,而前项与后项的比是0.61803398874989,即b/a的值与a/b的值相差1,假设后项与前项的比是φ,则有(φ-1)/φ=1,解这个方程得:φ= (√5+1) /2,这就是黄金分割。
   当n充分大时,斐波纳契数列后前项的比值,与前后项的比值,相差1,它们的比值是黄金分割!黄金分割是一个十分有用的无理数。据此,把黄金分割可用一个有理数近似表示,如斐波纳契数列的第七项与斐波纳契数列的第六项的比13/8,斐波纳契数列的第九项与斐波纳契数列的第八项的比34/21等都可以近似地表示为黄金分割,当然项数越后越精确。

 

C代码
  1. #include   
  2.  int main()  
  3.  {   
  4. long double f1, f2;   
  5. int i;  
  6.  f1 = 1;  
  7.  f2 = 1;   
  8. for (i = 1; i <= 25; i++)   
  9. {  
  10.  f1 = f1 + f2;   
  11. f2 = f1 + f2;   
  12. }   
  13. printf("%lf\n", (long double)f2/f1); return 0; }   
  14.   
  15. 运行结果:1.618034  
#include int main(){long double f1, f2;int i;f1 = 1;f2 = 1;for (i = 1; i <= 25; i++){f1 = f1 + f2;f2 = f1 + f2;}printf("%lf\n", (long double)f2/f1); return 0; }运行结果:1.618034

 

 ★ 有人验证, 如果使用该程序循环7次,即:i<=7,则结果为1.618033,循环8次以上(有效范围内),均为1.618034,由于"对于双精度数,使用%lf格式符输出时,前16位是有效数字,小数6位",如何显示更多的小数位不妨自己测试一下,一下午都在了解黄金分割与最优化法的内容,内容多得怎么都彻底结束不了,有点虚脱……呵呵

 

解决这样一个问题:

      数据规律F1=1    (n=1);F2=1   (n=2);Fn=Fn-1+Fn-2    (n>=3)

      显示Fibonacci数列的前n个数。

 

Java代码
  1. int main()  
  2.  {  
  3.  long int f1, f2; /* 作为循环体的"前两项",会超过32767,所以用long */   
  4. int i; /* 计数 */  
  5.  f1 = 1;  
  6.  f2 = 1;   
  7. for (i = 1; i <= n/2; i++) /* 循环一次输出2个,共进行n/2次 */   
  8. {   
  9. printf("%12ld %12ld", f1, f2); /* 第22个数之后值超过32767,必须用ld,而非d */   
  10. if ( i % 2 == 0) /* 每次输出两个, 两次输出4个, 便换行 */  
  11.  printf("\n"); /* 控制换行 */   
  12. f1 = f1 + f2; /* 计算下两个数 */  
  13.  f2 = f1 + f2;  
  14.  }   
  15. return 0;   
  16. } 若不考虑其他一切条件的话(没人傻得去测试一个循环n的数列)输出结果: ================================================   1         1             2          3             5               8           13  
  17.  21        34          55          89          144          233        377  
  18.  610      987       1597      2584       4181        6765      10946  
  19.  17711  28657   46368    75025    121393    196418   317811  
  20.  ………… ================================================ 另一种数组实现的方法,如果你有还好的方法何不开源一下,呵呵。  
  21.  #include   
  22.  int main() {  
  23.  int i;   
  24. int f[20] = {1,1};   
  25. for (i = 2; i < 20; i++)   
  26. f[i] = f[i-2] + f[i-1];  
  27.  for (i = 1; i <= 20; i++)   
  28. {   
  29. printf("%8d", f[i-1]);   
  30. if (i % 5 == 0)  
  31.  printf("\n");   
  32. }   
  33. return 0;   
  34. }  
int main(){long int f1, f2; /* 作为循环体的"前两项",会超过32767,所以用long */int i; /* 计数 */f1 = 1;f2 = 1;for (i = 1; i <= n/2; i++) /* 循环一次输出2个,共进行n/2次 */{printf("%12ld %12ld", f1, f2); /* 第22个数之后值超过32767,必须用ld,而非d */if ( i % 2 == 0) /* 每次输出两个, 两次输出4个, 便换行 */printf("\n"); /* 控制换行 */f1 = f1 + f2; /* 计算下两个数 */f2 = f1 + f2;}return 0;} 若不考虑其他一切条件的话(没人傻得去测试一个循环n的数列)输出结果: ================================================   1         1             2          3             5               8           1321        34          55          89          144          233        377610      987       1597      2584       4181        6765      1094617711  28657   46368    75025    121393    196418   317811………… ================================================ 另一种数组实现的方法,如果你有还好的方法何不开源一下,呵呵。#include int main() {int i;int f[20] = {1,1};for (i = 2; i < 20; i++)f[i] = f[i-2] + f[i-1];for (i = 1; i <= 20; i++){printf("%8d", f[i-1]);if (i % 5 == 0)printf("\n");}return 0;}

 

 

黄金分割在搜索区域中缩短探索区间的思想能够优化响应时间的问题,你可以拿一个具体的例子然后自己分析,如比较黄金分割查找与二分查找(折半)的时间复杂度和空间复杂度……

引用一个例子:

 

Java代码
  1. #include   
  2. #include /*黄金分割法求最小值的C++程序,部分变量及函数书写并不规范*/   
  3. int n = (lnδ/ln0.618 + 1) + 1;//δ为题给精度  
  4. int i;  
  5. float f(float ai, float bi)   
  6. {   
  7. a(i + 1) = ai + 0.618(bi - ai);   
  8. return ai + 1;   
  9. }  
  10.  float g(float ai, float bi)  
  11.  {  
  12.  b(i + 1) = ai + 0.382(bi - ai);  
  13.  return b(i + 1);   
  14. }   
  15. float F(float ai, float bi)   
  16. {   
  17. //题给的f(x)函数式;   
  18. result;   
  19. }   
  20. float A(float ai, float bi)   
  21. {   
  22.     int i = 1;   
  23.     float result;  
  24.  L: do {   
  25.    a(i + 1) = f(float ai, float bi);  
  26.    b(i + 1) = g(float ai, float bi);   
  27.    float F1 = F(float ai, float bi);   
  28.    float F2 = F(float a(i + 1),   
  29.    float b(i + 1));   
  30.    ai = ai, bi = b(i + 1);  
  31.     i ++;   
  32. }  
  33. while(i <= n && F1 >= F2)  
  34.  if(i < n){   
  35.   B(float ai, float bi);   
  36.   }   
  37. else result = F2;  
  38.    return result;  
  39.    }   
  40. float B(float ai, float bi)   
  41. {   
  42.    do{  
  43.          a(i + 1) = f(float ai, float bi);   
  44.          b(i + 1) = g(float ai, float bi);   
  45.          float F1 = F(float ai, float bi);   
  46.          float F2 = F(float a(i + 1), float b(i + 1));  
  47.          ai = a(i + 1), bi = bi;  
  48.           i ++;   
  49.         }  
  50.    while(i <= n && F1 <= F2)  
  51.     if(i < n)   
  52.        { goto L; }   
  53.     else   
  54.     result = F1;   
  55. return result;   
  56. }  
  57.  void main() {   
  58.   int i = 1;  
  59.   float A(float ai, float bi);  
  60.   cout<<"最小值为:"<
  61. }