浅谈数学与信息学关系

   众所周知,数学是所有理科科目的基础。在信息学竞赛中,无论是NOIP,NOI乃至IOI对竞赛选手的数学水平,数学思维以及对数学模型的构造都提出了更高的要求。当然,在信息学中,在程序中并不是生搬硬套数学公式,需要有灵活的对数据的处理,高效的算法才能完美地解决一题。
1、一些实用的数论定理及其应用.
小试牛刀:
例1求一组满足方程x^n+y^n=z^n 的正整数解(x,y,z).其中n为给定的数,且1<x,y,z,n<2^31-1。当然,无解请输出”no solution”
分析:本题虽然简单,但相信有不少同学会去老老实实地写模拟(真是傻的可以)。但只要知道费马大定理的仁兄都能轻松AC。
*费马大定理:
当n>2时,关于x,y,z的不定方程x^n+y^n=z^n(n为整数)无正整数解。
利用此定理,本题就迎刃而解了。
1.    当n=2时,任意输出一组解,如(3,4,5);
2.    当n>2时,直接输出”no solution”.
*欧几里德定理(辗转相除法):
gcd(a,b)=gcd(b,a mod b)
利用此公式我们可以较快地求出a,b的最大公约数
(程序略,证明见附录)。
例2狼找兔子:一座山周围有n个洞,顺时针编号为0,1,2,…n-1。而一只狼从0号洞开始,顺时针方向计数,每遇到m个洞就进洞找兔子。例如n=5,m=3,狼经过的洞依次为0,3,1,4,2,0。输入n和m。试问兔子有没有幸免的机会,如果有,该藏在哪儿?
分析:本题是初学者学循环结构时必做的一题,现在我们在数论的高度重新认识此题。
因为0号洞一开始被狼占据,我们不妨让兔子躲在1号洞,因为若狼能从0号洞到达1号洞,则必能从1号洞到达2号洞,……,狼肯定能到达每一个洞,兔子难逃厄运。反之,若有安全洞,则1号洞就是其中一个。
再来看狼。狼的第i次运动后的洞址应该是(m*i)mod n.若gcd(m,n)=1,则必存在i使得(m*i)≡1(mod n),也就是狼经过若干次变换能从0好洞到达1号洞。所以兔子逃生的条件为gcd(m,n)>1.
利用上式我们保证了至少有一个安全洞(1号洞),不难得出本题正解:兔子应躲在除编号为{i*gcd(m,n)|i=0…(n/gcd(m,n,)-1)}.
*欧几里德定理的推广:
求解ax+by=gcd(a,b)=d.(x,y为整数)
   分析:
若b=0,则gcd(a,b)=gcd(a,0)=a.(x,y)=(1,t)其中t为整数;
若b≠0,则令d’=gcd(b,a mod b),即找出满足d’=bx’+(a mod b)y’的整数x’,y’.做如下变换:
d’=bx’+(a mod b)y’=bx’+(a-[a/b]*b)y’=ay’+b(x’-[a/b]*y’)
   上式对应d=ax+by立即可得:(x,y)=(y’, x’-[a/b]*y’).
C语言代码如下:
下面我们看一个有关此定理的另一个实例:
例3已知x,y满足如下条件:
ax+by=c且x1<=x<=x2,y1<=y<=y2.x,y均为整数。
其中:a,b,c,x1,x2,y1,y2都是绝对值不超过10^8的整数。
求(x,y)的解的个数。
输入:a,b,c,x1,x2,y1,y2
输出:输出解的个数
*样例输入
1 1 -1 -10 10 -9 9
*样例输出
19
分析:本题给人的第一感觉仍然是朴素的穷举法,时间复杂度O((x2-x1)*(y2-y1))显然是会超时的。在这里,数学方法发挥了至关重要的作用。
设a,b,c,d为整数,则在不定方程ax+by=c中有如下两个重要命题:
(1)若(a,b)=d且c≠0(mod d),则方程ax+by=c无整数解;
(2)若x0,y0是方程ax+by=c且(a,b)=1的一组整数解(特解),则
(x,y)=(x0+bt,y0-at),其中t为整数。这是方程的通解。(证明见附录)
有了此定理的支撑,本题也变得小儿科了。方法如下:
   1、调用gcd(a,b,x,y)求出d=(a,b)。
   2、(i)若c≠0(mod d),total=0;
      (ii)若c≡0(mod d)
         利用已求出的特解(x0,y0)带入x1<=x<=x2,y1<=y<=y2即:
           x1<=x0+bt<=x2,y1<=y0-at<=y2求出t的范围[p,q];
         total=q-p+1;
   3、输出total.
刚接触信息学时我们就做过有关素数的判定,记得好像是用的2-sqrt(n)的枚举,其实还有更强的算法。
*欧拉定理:aψ(n) ≡1(mod n),其中ψ(n) 是n的欧拉函数,ψ(n) =不大于n的但与n互质的正整数个数。a可以取任意值。
易知,ψ(素数n)=n-1 从而,当n是素数的时候,a^n-1≡1(mod n),这是欧拉定理的特殊情况,也称为费尔马小定理。(证明见附录)
但费马小定理的逆定理是不成立的,即如果n满足a^(n-1)≡1(mod n), n不一定是素数。但是这种情况很少。也就是说,对于a取1~n-1的任意一个值的时候,都有a^(n-1)≡1(mod n)成立的话,则几乎可以确认n是素数。
当a=2时,小于10^4的n值中,只会产生22个不成立的n。当a=3时,在小于108地n值中,只会产生255个不成立的n。显然可以看出这个出错的概率是很小的。关于a^(n-1)的快速求法,可用分治法,在O(logN)的复杂度内解决。所以判断n是否为素数,总的复杂度为O(k*logN)其中k是a的取值次数。一般情况下,我们只需要让a=2,3,4判断一下就可以了。于是费尔马小定理的逆定理成为了我们进行素数测试的有效算法之一。

2、灵活运用数学思想,构造数学模型,数学方法与其他算法相结合
例1、一元三次方程求解(改编自NOIP2001)
问题描述:有形如ax^3+bx^2+cx+d=0这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d均为实数),并约定该方程存在三个不同实根(根的范围在-100~100),且根与根之差的绝对值大于或等于1.要求从大到小依次在同一行输出这三个实根,并精确到小数点后4位。
*样例输入:
1 -5 -4 20
*样例输出:
-2.0000 2.0000 5.0000
分析:本题用枚举法显然摆脱不了超时的厄运,需要一个更为高效的算法。记f(x)=ax^3+bx^2+cx+d,若存在x1,x2,使得f(x1)*f(x2)<0,则在区间(x1,x2)中至少有一实根.(零点定理)我们只需加入二分法查找即可。具体方法如下:
(1)枚举[-100,-99],[-99,-98],…..,[99,100]每一个区间。(因为题目中交代根与根之差的绝对值大于或等于1,所以在每一区间中最多有一实根);
(2)取当前存在解的区间(a,b).
若a+0.00001>b或f((a+b)/2)=0,则可确定跟为(a+b)/2;
若f(a)*f((a+b)/2)<0,则由零点定理可知跟在区间(a,(a+b)/2).
若f(a)*f((a+b)/2)>0,则根在((a+b)/2,b).
C语言源程序:

例2极值问题。
已知m,n为整数,且满足下列两个条件:
1、m,n∈1,2,3,…,k(1<=k<=10^9)
2、(n^2-mn-m^2)^2=1
编一程序,输入k,求一组满足上述两个条件的m,n,使得m^2+n^2最大。
*样例输入:
1995
*样例输出
987 1597
分析:
算法1:这显然是一道数学题。通过我们已学的知识,不难得出以下算法:        由(n^2-mn-m^2)^2=1可得:
n^2-mn-m^2+1=0或n^2-mn-m^2-1=0
可求得n的表达式:
n=(m±sqrt(5*m^2-4))或n=(m±sqrt(5*m^2+4))
但由于n>1
所以n=(m+sqrt(5*m^2-4))/2或n=(m+sqrt(5*m^2+4))
因为m,n是整数,所以判别式也应为整数。
有了以上关系式,又m^2+n^2是单调递增的。我们只需从m=k开始,逐步递减枚举m,每次计算n的值。找到第一组符合题意的(m,n),那么此时m^2+n^2一定最大。
伪代码如下:
for(flag<-0,m<-k,flag<-0;flag!=1&&m>=1;m--)
{
   p<-sqrt(5*m^2-4);
   if(p为整数)
   {
       n<-(m+p)/2;
       if(n为整数&&n<=k)
       {
           输出m,n;
           flag<-1;
}
}
q<-sqrt(5*m^2+4);
   if(q为整数)
   {
       n<-(m+q)/2;
       if(n为整数&&n<=k)
       {
           输出m,n;
           flag<-1;
}
}

}
上述算法在数学上是十分严谨的,时间复杂度O(k).在k不超过10^7还能在15s内出解。但k的范围,k<=10^6.显然我们需要一个更高效的算法。
算法2:既然从简单的数学演算中不能找出高效的算法,我们不妨利用刚才的结论找找规律。
k
1
2
3
4
5
m
1
2
3
2
5
n
1
1
2
3
3
我们简单地推导出k=1,2,3,4,5时(m,n)的值,从表格中红框中你是否发现了什么?对啊,这就是著名的Fibonacci数列。我们尝试着按此规律再往后推导几项。
k
2
3
5
8
13
21
34
m
2
3
5
8
13
21
34
n
1
2
3
5
8
13
21
上表的几项都满足Fibonacci数列,显然我们的猜想是正确的。现在本人证明下:
因为(n^2-mn-m^2)^2=(m^2-mn-n^2)^2
又m^2-mn-n^2=(m+n)^2-mn-2n^2=(m+n)^2-(m+n)n-n^2
对应原题中的n^2-mn-m^2.令n’=m+n,m’=n.那么m’,n’也是符合题意的一组解。
换句话说,所有满足题意的(m,n)可以排成一个Fibonacci数列{1,1,2,3,5,8…}.
而且满足题意的(m,n)必然是数列中小于k(不是数列中的编号)的最大的两个相邻数。现在我们得出了一种更高效且较简单的算法。
C语言源程序:
最后需要指出,由于Fibonacci数列增长非常快,第43位为701408733,是小于10^9的最后一个数。也就是说程序循环最多运行43次,时间复杂度可认为O(1).其实计算Fibonacci数列第n项可利用矩阵乘法将时间复杂度从O(n)降为O(logn),具体实现方法可见附录fib.c。但就本题而言,此算法以足够优秀。
例3约瑟夫问题(加强)
问题描述:现有n(1~n)个人围成一个圈,从任意一人开始报数,从1报到m,并且报m的人退出,现在给定m,n.请你求出经过若干次报数,最后剩下的人的编号。输入数据满足m<n,n<10^7
*样例输入:
13
*样例输出
11
分析:
这也是一个经典问题,相信单向循环链表的模拟是个人都会写,关键在于高效。(模拟的时间复杂度高达O(mn)).显然,本题要在数学分析做点功夫。
假设这n个人编号为0~n-1,报数从0报到m-1。令k=m mod n.显然k-1为第一位退出者的编号。剩余的n-1人一次为:
K,k+1,k+2,…,n-1,0,1,2,…,k-2
我们换个角度看问题,这n-1人组成了一个新的约瑟夫环,开始了新一轮的报数,而且新约瑟夫环中的0号就是原约瑟夫环的k号。那么按照这个顺序我们讲新环重新编号:
旧约瑟夫(人)
k
k+1
……
n-1
0
1
2
……
k-2
新约瑟(n-1人)
0
1
……
n-k-1
n-k+1
n-k+2
n-k+3
……
n-1
它们对应之间的关系已经很明显了,p’=(p+k)mod n.
既然通过n能够推导出n-1,那么n-1照样能推导出n-2,……,2推导1.相信你已看出算法了,递推(或递归)。f(i)表示i个人报数m最后剩下的人的编号。则最后结果为f(n).有如下递推式
f(1)=0;
f(i)=(f(i-1)+m)mod i;i>1
C语言源程序:
#include <stdio.h>
main()
{
  long n, m, i, f=0;
  scanf("%ld%ld", &n, &m);
  for (i=2; i<=n; i++)
f=(f+m)%i;
  printf ("%ld", f+1);
}
   本程序采用的是迭代,其实质仍是递推思想。时间复杂度O(n). 相对于模拟算法已经有了很大的提高。
本文小结:
   通过对以上几例的分析,相信大家对于信息学中数学策略的应用已有了初步的认识,可见,适当地运用数学策略,不仅可以让编程变得简单,而且往往会成倍地提高算法执行效率。最后,我借用某奶牛的一句话:
   战争是政治关系的延续,OI是 MATH 生命的升华!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值