数学归纳法
数学上证明与自然数n有关的命题的一种方法。必须包括两步:(1)验证当n取第一个自然数值n=n1(n1=1,2或其他常数)时,命题正确;(2)假设当n取某一自然数k时命题正确,以此推出当n=k+1时这个命题也正确。从而就可断定命题对于从n1开始的所有自然数都成立。
数学归纳法是一种数学证明方法,典型地用于确定一个表达式在所有自然数范围内是成立的或者用于确定一个其他的形式在一个无穷序列是成立的。有一种用于数理逻辑和计算机科学广义的形式的观点指出能被求出值的表达式是等价表达式;这就是著名的结构归纳法。
已知最早的使用数学归纳法的证明出现于 Francesco Maurolico 的 Arithmeticorum libri duo (1575年)。Maurolico 证明了前 n 个奇数的总和是 n^2。
最简单和常见的数学归纳法证明方法是证明当n属于所有自然数时一个表达式成,这种方法是由下面两步组成:
递推的基础: 证明当n = 1时表达式成立。
递推的依据: 证明如果当n = m时成立,那么当n = m + 1时同样成立。(递推的依据中的“如果”被定义为归纳假设。 不要把整个第二步称为归纳假设。)
这个方法的原理在于第一步证明起始值在表达式中是成立的,然后证明一个值到下一个值的证明过程是有效的。如果这两步都被证明了,那么任何一个值的证明都可以被包含在重复不断进行的过程中。或许想成多米诺效应更容易理解一些;如果你有一排很长的直立着的多米诺骨牌那么如果你可以确定:
第一张骨牌将要倒下。
只要某一个骨牌倒了,与他相临的下一个骨牌也要倒。
那么你就可以推断所有的的骨牌都将要倒。
数学归纳法的原理作为自然数公理,通常是被规定了的(参见皮亚诺公理第五条)。但是它可以用一些逻辑方法证明;比如,如果下面的公理:
自然数集是有序的被使用。
注意到有些其他的公理确实的是数学归纳法原理中的二者择一的公式化。更确切地说,两个都是等价的。
用数学归纳法进行证明的步骤:
(1)(归纳奠基)证明当取第一个值时命题成立;证明了第一步,就获得了递推的基础,但仅靠这一步还不能说明结论的普遍性在第一步中,考察结论成立的最小正整数就足够了,没有必要再考察几个正整数,即使命题对这几个正整数都成立,也不能保证命题对其他正整数也成立;
(2)(归纳递推)假设时命题成立,证明当时命题也成立;证明了第二步,就获得了递推的依据,但没有第一步就失去了递推的基础.只有把第一步和第二步结合在一起,才能获得普遍性的结论;
(3)下结论:命题对从开始的所有正整数都成立。
注:
(1)用数学归纳法进行证明时,“归纳奠基”和“归纳递推”两个步骤缺一不可;
(2)在第二步中,在递推之前,时结论是否成立是不确定的,因此用假设二字,这一步的实质是证明命题对 的正确性可以传递到 时的情况.有了这一步,联系第一步的结论(命题对成立),就可以知道命题对 也成立,进而再由第二步可知 即 也成立,…,这样递推下去就可以知道对于所有不小于 的正整数都成立.在这一步中,时命题成立,可以作为条件加以运用,而 时的情况则有待利用归纳假设、已知的定义、公式、定理加以证明,不能直接将 代入命题.
数学归纳法的第二种形式
数学归纳法是一种重要的论证方法。它们通常所说的“数学归纳法”大多是指它的第一种形式而言,本文想从最小数原理出发,对它的第二种形式即第二数学归纳法进行粗略的探讨,旨在加深对数学归纳法的认识。
第二数学归纳法原理是设有一个与自然数n有关的命题,如果:
(1)当n=1回时,命题成立;
(2)假设当n≤k时命题成立,则当n=k+1时,命题也成立。
那么,命题对于一切自然数n来说都成立。
证明:用反证法证明。
假设命题不是对一切自然数都成立。命N表示使命题不成立的自然数所成的集合,显然N非空,于是,由最小数原理N中必有最小数m,那么m≠1,否则将与(1)矛盾。所以m-1是一个自然数。但m是N中的最小数,所以m-1能使命题成立。这就是说,命题对于一切≤m-1自然数都成立,根据(2)可知,m也能使命题成立,这与m是使命题不成立的自然数集N中的最小数矛盾。因此定理获证。
当然,定理2中的(1),也可以换成n等于某一整数k。
对于证明过程的第一个步骤即n=1(或某个整数a)的情形无需多说,只需要用n=1(或某个整数a)直接验证一下,即可断定欲证之命题的真伪。所以关键在于第二个步骤,即由n≤k到n=k+1的验证过程。事实上,我们不难从例1的第二个步骤的论证过程中发现,证明等式在n=k+1时成立是利用了假设条件;等式在n=k及n=k-1时均需成立。同样地,例2也不例外,只是形式的把n=k及n=k-1 分别代换成了n=k-1和n=k-2。然而例3就不同了,第二个步骤的论证过程,是把论证命题在n=k+1时的成立问题转化为验证命题在n=k-2+1时的成立问题。换言之,使命题在n=k+1成立的必要条件是命题在n=k-2+1时成立,根据1的取值范围,而命题在n=k-k+1互时成立的实质是命题对一切≤k的自然数n来说都成立。这个条件不是别的,正是第二个步骤中的归纳假设。以上分析表明,假如论证命在n=k+1时的真伪时,必须以n取不大于k的两个或两个以上乃至全部的自然数时命题的真伪为其论证的依据,则一般选用第二数学归纳法进行论证。之所以这样,其根本原则在于第二数学归纳法的归纳假设的要求较之第一数学归纳法更强,不仅要求命题在n-k时成立,而且还要求命题对于一切小于k的自然数来说都成立,反过来,能用第一数学归纳法来论证的数学命题,一定也能用第二数学归纳进行证明,这一点是不难理解的。不过一般说来,没有任何必要这样做。
第二数学归纳法和第一数学归纳法一样,也是数学归纳法的一种表达形式,而且可以证明第二数学归纳法和第一数学归纳法是等价的,之所以采用不同的表达形式,旨在更便于我们应用。
递归
递归做为一种算法在程序设计语言中广泛应用.是指函数/过程/子程序在运行过程序中直接或间接调用自身而产生的重入现像.
程序调用自身的编程技巧称为递归( recursion)。
一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。用递归思想写出的程序往往十分简洁易懂。
一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。
注意:
(1) 递归就是在过程或函数里调用自身;
(2) 在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口。
递归算法一般用于解决三类问题:
(1)数据的定义是按递归定义的。(Fibonacci函数)
(2)问题解法按递归算法实现。(回溯)
(3)数据的结构形式是按递归定义的。(树的遍历,图的搜索)
递归的缺点:
递归算法解题的运行效率较低。在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。
例子:
#include <iostream.h>
void move (char getone,char putone)
{
cout <<getone<<"-->"<}
void hanoi(int n,char one ,char two ,char three)
{
void move (char getone,char putone );
if (n==1)
move (one,three);
else
{
hanoi(n-1,one,three,two);
move (one ,three);
hanoi(n-1,two,one,three);
}
}
void main()
{
void hanoi(int n ,char one ,char two ,char three);
int m ;
cout <<"Input the numberof disker:";
cin>>m;
cout<<"the steps to moving "<<m<<"diskes
:"<<endl;
hanoi(m,'A','B','C');
}
第二章 递归
2.1 递归的概念
2.2 如何设计递归算法
2.3 典型例题
递归是计算机科学的一个重要概念,递归的方法是程序设计中有效的方法,采用递归编写
程序能是程序变得简洁和清晰.
2.1 递归的概念
1.概念
一个过程(或函数)直接或间接调用自己本身,这种过程(或函数)叫递归过程(或函数).
如:
procedure a;
begin
.
.
.
a;
.
.
.
end;
这种方式是直接调用.
又如:
procedure b; procedure c;
begin begin
. .
. .
. .
c; b;
. .
. .
. .
end; end;
这种方式是间接调用.
例1计算n!可用递归公式如下:
1 当 n=0 时
fac(n)={n*fac(n-1) 当n>0时
可编写程序如下:
program fac2;
var
n:integer;
function fac(n:integer):real;
begin
if n=0 then fac:=1 else fac:=n*fac(n-1)
end;
begin
write('n=');readln(n);
writeln('fac(',n,')=',fac(n):6:0);
end.
例2 楼梯有n阶台阶,上楼可以一步上1阶,也可以一步上2阶,编一程序计算共有多少种不同的走法.
设n阶台阶的走法数为f(n)
显然有
1 n=1
f(n)={2 n=2
f(n-1)+f(n-2) n>2
可编程序如下:
program louti;
var n:integer;
function f(x:integer):integer;
begin
if x=1 then f:=1 else
if x=2 then f:=2 else f:=f(x-1)+f(x-2);
end;
begin
write('n=');read(n);
writeln('f(',n,')=',f(n))
end.
2.2 如何设计递归算法
1.确定递归公式
2.确定边界(终了)条件
练习:
用递归的方法完成下列问题
1.求数组中的最大数
2.1+2+3+...+n
3.求n个整数的积
4.求n个整数的平均值
5.求n个自然数的最大公约数与最小公倍数
6.有一对雌雄兔,每两个月就繁殖雌雄各一对兔子.问n个月后共有多少对兔子?
7.已知:数列1,1,2,4,7,13,24,44,...求数列的第 n项.
2.3典型例题
例3 梵塔问题
如图:已知有三根针分别用1,2,3表示,在一号针中从小放n个盘子,现要求把所有的盘子
从1针全部移到3针,移动规则是:使用2针作为过度针,每次只移动一块盘子,且每根针上
不能出现大盘压小盘.找出移动次数最小的方案.
程序如下:
program fanta;
var
n:integer;
procedure move(n,a,b,c:integer);
begin
if n=1 then writeln(a,'--->',c)
else begin
move(n-1,a,c,b);
writeln(a,'--->',c);
move(n-1,b,a,c);
end;
end;
begin
write('Enter n=');
read(n);
move(n,1,2,3);
end.
例4 快速排序
快速排序的思想是:先从数据序列中选一个元素,并将序列中所有比该元素小的元素都放到它的右边或左边,再对左右两边分别用同样的方法处之直到每一个待处理的序列的长度为1, 处理结束.
程序如下:
program kspv;
const n=7;
type
arr=array[1..n] of integer;
var
a:arr;
i:integer;
procedure quicksort(var b:arr; s,t:integer);
var i,j,x,t1:integer;
begin
i:=s;j:=t;x:=b;
repeat
while (b[j]>=x) and (j>i) do j:=j-1;
if j>i then begin t1:=b; b:=b[j];b[j]:=t1;end;
while (b<=x) and (i<j) do i:=i+1;
if i<j then begin t1:=b[j];b[j]:=b;b:=t1; end
until i=j;
b:=x;
i:=i+1;j:=j-1;
if s<j then quicksort(b,s,j);
if i<t then quicksort(b,i,t);
end;
begin
write('input data:');
for i:=1 to n do read(a);
writeln;
quicksort(a,1,n);
write('output data:');
for i:=1 to n do write(a:6);
writeln;
end.
练习:
1.计算ackerman函数值:
n+1 m=0
ack(m,n)={ ack(m-1,1) m<>0 ,n=0
ack(m-1,ack(m,n-1)) m<>0,n<>0
求ack(5,4)