6数学模型和数学建模

很简单的例子:

           已知有五个数,求前四个数与第五个数分    别相乘后的最大当数。给出两个算法分别如下:

max1(int a,b,c,d,e)                   max2(int a,b,c,d,e)
 {  int x ;                                    { int x
    a=a*e;                                      if (a>b)
    b=b*e;                                         x=a;
    c=c*e;                                      else
    d=d*e;                                        x=b;
    if( a>b)                                       if (c>x)
        x=a;                                       x=c;
    else                                         if (d>x) 
       x=b;                                       x=d; 
    if (c>x)                                       x=x*e; 
       x=c;                                      print(x); 
    if (d>x)                                     }                         
       x=d;                            
    print(x);                          
    }

  

  以上两个算法基于的数学模型是不同的,一个算法先积再求最大值,另一个算法先求最大值再求积,求从上表可以看出,后一个算法的效率明显要高于前一个算法。

    数学建模就是把现实世界中的实际问题加以提炼,抽象为数学模型,求出模型的解,验证模型的合理性,并用该数学模型所提供的解答来解释现实问题,我们把数学知识的这一应用过程称为数学建模。   

数学建模的基本方法

  从分析问题的几种简单的、特殊的情况中,发现一般规律或作出某种猜想,从而找到解决问题的途径。这种研究问题方法叫做归纳法。即归纳法是从简单到复杂,由个别到一般的一种研究方法。

杨辉三角形的应用

【例】求n次二项式各项的系数:已知二项式的展 开式为:

问题分析:若只用的数学组合数学的知识,直接建模

  k=0,1,2,3……n。

用这个公式去计算,n+1个系数,即使你考虑到了前后系数之间的数值关系:算法中也要有大量的乘法和除法运算,效率是很低的。

数学知识是各阶多项式的系数呈杨辉三角形的规律

(a+b)0                        1  

(a+b)                1       1

(a+b)        1      2      1

(a+b)3         1     3        3       1

(a+b)   1      4        6        4       1

(a+b)5    ……

     则求n次二项式的系数的数学模型就是求n阶杨辉三角形。

算法设计要点:   除了首尾两项系数为1外,当n>1时,(a+b)n的中间各项系数是(a+b)n-1的相应两项系数之和,如果把(a+b)n的n+1的系数列为数组c,则除了c(1)、c(n+1)恒为1外,设(a+b)n的系数为c(i),(a+b)n-1 的系数设为c’(i)。则有:

        c(i)=c’(i)+c’(i-1)

   而当n=1时,只有两个系数 c(1) 和 c(2) (值都为1)。不难看出,对任何n,  (a+b)n的二项式系数可由(a+b)n-1的系数求得,直到n=1时,两个系数有确定值,故可写成递归子算法。

coeff(int  a[ ],int  n)
 {  if  (n==1) 
     {  a[1]=1;
        a[2]=1;}
    else
     {  coeff(a,n-1);
        a[n+1]=1;
        for (i=n;i>=2;i- -)
        a[i]=a[i]+a[i-1];
        a[1]=1;
     }  
  }  
  main( )
{int  a[100],i,n;
 input( n );
 for(i=1;i<=n;i=i+1)
    input(a[i]);
 coeff(a,n);
for(i=1;i<=n;i=i+1)
    print(a[i]);
}

  

最大公约数的应用

【例】数组中有n个数据,要将它们顺序循环向后移k位,即前面的元素向后移k位,后面的元素则循环向前移k位,例:1、2、3、4、5循环移3位后为:3、4、5、1、2。考虑到n会很大,不允许用2*n以上个空间来完成此题。

    问题分析1:若题目中没有关于存储空间的限制,我们可以方便地开辟两个一维数组,一个存储原始数据,另一个存储移动后的数据。

main( )
{int  a[100],b[100],i,n,k;
       input(n,k );
       for(i=0;i<n;i=i+1)
          input(a[i]);
       for(i=0;i<n;i=i+1)
    b[(k+i) mod n]=a[i];
for(i=0;i<n;i=i+1)
   print(b[i]);
}

这个算法的时间效率是n次移动(赋值)。

有可能k>n,这样移动会出现重复操作,可以在输入数据后,执行k = k mod n;   以保证不出现重复移动的问题。这个算法的移动(赋值)次数为k*n。当n较大时不是一个好的算法。

问题分析2:

● 将最后一个存储空间的数据,存储在临时存储空间中;

● 其余数据逐个向后移动一位。

这样操作共k次就能完成问题的要求。

算法2如下:

main( )
{int  a[100],b[100],i,j,n,k,temp;
       input(n,k);
       for(i=0;i<n;i=i+1)
          input(a[i]);
       for(i=0;i<k;i=i+1)
   {temp=a[n-1];
for(j=n-1;j>0;j=j-1)
a[j]=a[j-1];
a[0]= temp ;
}
for(i=0;i<n;i=i+1)
   print(b[i]);
}

  

问题分析3 利用一个临时存储空间,把每一个数据一次移动到位:

1) 一组循环移动的情况:

        通过计算我们可以确定某个元素移动后的具体位置,如n=5, k=3时:

          0、1、2、3、4循环移3位后为2、3、4、0、1。

可通过计算得出0移到3的位置,3移到1的位置,1移到4的位置,4移到2的位置,2移到0的位置;一组移动(0-3-1-4-2-0)正好将全部数据按要求进行了移动。这样只需要一个辅助变量,每个数据只需一次移动就可完成整个移动过程。

如果算法就这样按一组移动去解决问题,就错了。因为还有其它情况。

2)多组循环移动的情况:算法不能按一组移动去解决问题。

   看下一个例子,当n=6,k=3时,1、2、3、4、5、6经移动 的结果是4、5、6、1、2、3. 并不象上一个例子,一组循环 移动没有将全部数据移动到位。还需要(2-5-2)(3-6-3)两组移动,共要进行三组循环移动(1-4,2-5,3-6)才能将全部数据操作完毕。

             循环移动的组数,与k、n的是怎么样的关系呢?

           我们再看一个例子,当N=6,K=2时,     1、2、3、4、5、6经移动的结果是5、6、1、2、3、4.     1移到3的位置,3移到5的位置,5移到1的位置,一组移动完成了3个数据的移动,接下来,还有一组2-4-6-2。共进行二组循环移动,就能将全部数据移动完毕。

数学模型:循环移动的组数等于N与K的最大公约数。

算法设计:

1)编写函数,完成求n , k最大公约数m的功能

2)进行m组循环移动。

3)每组移动,和算法2一样,通过计算可以确定某个

     元素移动后的具体位置。在移动之前,用临时变量

     存储需要被覆盖的数据。

算法3如下:

main( )
{int  a[100],b0,b1,i,j,n,k,m,tt;
   print(“input the number of data”); input(n);
print(“input the distant of moving”); input(k );
for(i=0;i<n;i=i+1)      input(a[i]);
m=ff(n,k);
for(j=0;j<m;j=j+1)
  {b0= a[j];  tt=j;
   for(i=0;i<n/m;i=i+1)
     {tt=(tt+k) mod n;b1=a[tt]; a[tt]= b0;  b0=b1;}
   }
for(i=0;i<n;i=i+1) 
   print(a[i]);
}
ff ( int  a ,int b)
{  t = 1;
   for ( i = 2;i<=a and i<=b;i++)
     while  (a  mod  i=0 and b  mod  i=0 )
        { t=t * i ;          a=a / i ;           b= b / i ; }
    return(t) ;
 }

算法分析:每组循环移动都是从前向后进行的,一次移动需要两次赋值,总体大约需要赋值2n次。

    能不能继续提高效率为赋值n次呢?请考虑改进每组循环移动的方式为从后开始移动,以提高运行效率。以例子说明:

n=6,k=2时,第一组循环移动0-2-4,在算法3中是这样实现的:

a[0]=>b0,

a[2]=>b1, b0(a[0])=>a[2],b1=> b0;

a[4]=>b1, b0(a[2])=> a[4],b1=> b0;

a[0]=>b1, b0(a[4])=> a[0] ,b1=> b0;

改进后(和算法2类似):

a[4]=>b ;a[2]=>a[4],a[0]=>a[2], b=>a[0]。

将每组最后一个数据元素存储在辅助存储空间,以后就可以安全地覆盖后面的数组元素了(注意覆盖顺序)。这样,一组循环移动只需一次将数据存入辅助存储空间,其后一次移动只需一次赋值,全部工作大约需要赋值n次就可完成。

公倍数的应用

【例】编程完成下面给“余”猜数的游戏:

你心里先想好一个1~100之间的整数x,将它分别除以3、5和7并得到三个余数。你把这三个余数告诉计算机,计算机能马上猜出你心中的这个数。游戏过程如下:

please  think  of  a  number  between  1  and  100

your  number  divided  by  3  has  a  remainder  of?  1

your  number  divided  by  5  has  a  remainder  of?  0

your  number  divided  by  7  has  a  remainder  of?  5

let  me  think  a  moment…

your  number  was  40

问题分析:算法的关键是:找出余数与求解数之间的关系,

也就是建立问题的数学模型。

数学模型:

 1)不难理解当s=u+3*v+3*w时,s除以3的余数与u除以3的

余数是一样的。

 2)对s=cu+3*v+3*w,当c除以3余数为1的数时, s除以3的

余数与u除以3的余数也是一样的。证明如下:

    c 除以 3余数为1,记c=3*k+1,则s=u+3*k*u+3*v+3*w,由1)的结论,上述结论正确。

   记a,b,c分别为所猜数据d除以3,5,7后的余数,则

   d=70*a+21*b+15*c。

   为问题的数学模型,其中70称作a的系数,21称作b的系数,15称作c的系数。

由以上数学常识,a、b、c的系数必须满足:

1) b、c的系数能被3整除,且a的系数被3整除余1;这样d除以3的余数与a相同。

2) a、c的系数能被5整除,且b的系数被5整除余1;这样d除以5的余数与b相同。

3)   a、b的系数能被7整除,且c的系数被7整除余1;这样d除以7的余数与c相同。

由此可见:

   c的系数是3和5的最公倍数且被7整除余1,正好是15;

   a的系数是7和5的最公倍数且被3整除余1,最小只能是70;

   b的系数是7和3的最公倍数且被5整除余1,正好是21。

算法设计:用以上模型求解的数d,可能比100大,这时只要减去3,5,7的最小公倍数就是问题的解了。

main( )
{  int a,b,c,d;
   print(  “please  think  of  a  number  between  1  and  100.”);
   print(  “your  number  divded  by  3  has  a  remainker  of”);
   input(a);
   print(  “your  number  divded  by  5  has  a  remainker  of”);
   input(b);
   print(  “your  number  divded  by  7  has  a  remainker  of”);
   input(c);
   print(  “let  me  think  a  moment…”);
   for  (i=1 ,i<1500;  i=i+1);   //消耗时间,让做题者思考    d=70*a+21*b+15*c;
   while  (d>105)          d=d-105;
   print(  “your  number  was ”, d);
 }

  

裴波那契数列应用

【例】楼梯上有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,编写算法计算共有多少种不同的上楼梯方法。

数学模型:此问题如果按照习惯,从前向后思考,也就是从第一阶开始,考虑怎么样走到第二阶、第三阶、第四阶……,则很难找出问题的规律;而反过来先思考“到第n阶有哪几种情况?”,答案就简单了,只有两种情况:

      1)  从第n-1阶到第n阶;

      2)  从第n-2阶到第n阶。

   记n阶台阶的走法数为f(n),则

   f(n)= 1                  n=1

  f(n)=2              n=2

  f(n-1)+f(n-2)       n>2

算法设计:算法可以用递归或循环完成。下面是问题的递归算法。

算法如下:

main( )
{ int  n:; 
 print('n=');
 input(n);
 print('f(',n,')=',f(n));
}
f(int n)
{ if (n = 1 )   return(1);
 if (n = 2 )  return(2);
 else 
  return(f(x-1)+f(x-2));
}

  

递推关系求解方程

【例】核反应堆中有α和β两种粒子,每秒钟内一个α粒子可以反应产生3个β粒子,而一个β粒子可以反应产生1个α粒子和2个β粒子。若在t=0时刻的反应堆中只有一个α粒子,求在t时刻的反应堆中α粒子和β粒子数。

数学模型1:本题中共涉及两个变量,设在i时刻α粒子数为ni,β粒子数为mi,则有:n0=1,m0=0,ni=mi—1,mi=3ni—1+2mi—1

算法设计1:本题方便转化为求数列N和M的第t项,可用递推的方法求得nt和mt,此模型的算法如下:

main()
{  int n[100],m[100],t,i;
   input(t);
    n[0]=1;             //初始化操作
    m[0]=0;
   for (i=1;i<=t;i++)    //进行t次递推 
        {    n[i]=m[i-1];
       m[i]=3 * n[i-1] + 2 * m[i-1]; } 
  print(n[t]);    //输出结果
    print(m[t]);
  }

算法分析1:此模型的空间需求较小,时间复杂度为O(n),但随着n的增大,所需时间越来越大。

数学模型2:设在t时刻的α粒子数为f(t),β粒子数为g(t),依题可知:

    g(t)=3f(t -1)+2g(t -1)            (1)

    f(t)=g(t -1)                      (2)

    g(0)=0,f(0)=1

   下面求解这个递归函数的非递归形式

   由(2)得f(t -1)=g(t-2)             (3)

   将(3)代入(1)得

  g(t)=3g(t -2)+2g(t-1)   (t≥2)    (4)

 g(0)=0,g(1)=3

 (4)式的特征根方程为:

 x2—2x—3=0

 其特征根为x1=3,x2= -1

所以该式的递推关系的通解为

g(t)=C1·3t+C2·( -1)t

代入初值g(0)=0,g(1)=3得

C1+C2=0

3C1—C2=3

解此方程组

所以该递推关系的解为

∴g(t)=

由数学模型2,设计算法2如下

main()
{ int t,i;
 input(t); 
  n=int(exp(t*ln(3)));
  m=int(exp((t+1)*ln(3)));
  if (t mod 2=1)
     {  n=n-3;  m=m+3;}
  else  
     {  n=n+3;  m=m-3;}
  n=int(n/4);  // 4|n 
  m=int(m/4);  //  4|m
  print(n);
  print(m);
 }

算法分析:在数学模型2中,运用数学的方法建立了递归函数并转化为非递归函数。它的优点是算法的复杂性与问题的规模无关。针对某一具体数据,问题的规模对时间的影响微乎其微。

猜你喜欢

转载自www.cnblogs.com/gd-luojialin/p/10381477.html
今日推荐