330 likes | 484 Views
P 基础算法. 阿默. 应该了解的基础知识. Pascal 基础:变量类型、表达式、常用系统函数 分支结构: if 、 case 循环结构:三种循环 for 、 while 、 repeat 数组、字符串 自定义函数、过程. 一、循环. 三种循环 while 语句 while 语句用于“当满足某一条件时进行循环”的情况。 while 语句的语法格式: while 布尔表达式 do 语句(复合); < 表达式为真就进入循环体,最实用灵活 >
E N D
P基础算法 阿默
应该了解的基础知识 • Pascal基础:变量类型、表达式、常用系统函数 • 分支结构:if 、case • 循环结构:三种循环for、while、repeat • 数组、字符串 • 自定义函数、过程
一、循环 • 三种循环 • while 语句while语句用于“当满足某一条件时进行循环”的情况。while语句的语法格式:while 布尔表达式 do 语句(复合); • <表达式为真就进入循环体,最实用灵活> • repeat语句repeat 语句用于“重复执行循环体,一直到指定的条件为真时结束”。语法格式为:repeat 语句1;…… 语句n;until 布尔表达式; • <直到型、至少执行一次循环体,表达式为真退出循环> • for 语句用来描述已知重复次数的循环结构。for 语句有两种形式:(1) for 控制变量:=初值 to 终值 do 语句;(2) for 控制变量:=初值 downto 终值 do 语句; • <使用简单,但是变量必须是可数的>
循环应用 • 求1+2+。。+100 • 求 N! • 判断素数 • 打印花式图形 • ………… • ……
二、数组 • 为什么要使用数组 ? • 例1 输入50个学生的某门课程的成绩,打印出低于平均分的同学号数与成绩。 • 分析:在解决这个问题时,虽然可以通过读入一个数就累加一个数的办法来求学生的总分,进而求出平均分。但因为只有读入最后一个学生的分数以后才能求得平均分,且要打印出低于平均分的同学,故必须把50个学生的成绩都保留下来, 然后逐个和平均分比较,把高于平均分的成绩打印出来。如果,用简单变量a1,a2,…,a50存放这些数据,可想而知程序要很长且繁。
数组定义 var a,b,c:array[1..100] of integer; d:array[1..10,0..1] of real;
例1题解 Program ex; var tot,ave:real; a:array[1..50] of real; i:integer; begin tot:=0; {tot表示总分} for i:=1 to 50 do {循环读入每一个学生的成绩,并累加它到总分} begin read(a[i]); tot:=tot+a[i]; end; ave:=tot/50;{计算平均分} for i:=1 to 50 do if a[i]<ave then writeln(‘No.’,i,’ ‘,a[i]:5:2);{如果第i个同学成绩小于平均分,则将输出 } end.
算法 • 什么是算法? 算法是在有限步骤内求解某一问题所使用的一组定义明确的规则。通俗点说,就是计算机解题的过程。
算法的优化 • 一个问题可能有多种算法,当然在这些算法中也有优劣。 例:求1+2+..+100 一种算法:直接累加 另一种:高斯用很短的时间计算出了小学老师布置的任务:对自然数从1到100的求和。他所使用的方法是:对50对构造成和101的数列求和(1+100,2+99,3+98……),同时得到结果:5050。
算法的优化 • 同一种算法中,我们也可以考虑到问题的各种条件来对算法进行一定的优化。 • 例:100元一张 换成若干张10元、5元、2元、1元的零钱,要求每次都换成40张小钞票,每种至少1张。
程序1(为了便于了解程序工作效率,加入了一些统计变量)程序1(为了便于了解程序工作效率,加入了一些统计变量) var i,j,k,l,s:integer; c:longint; {用于统计最内部循环体执行的次数} begin s:=0;c:=0; for i:=1 to 40 do {10元的张数} for j:=1 to 40 do {5元的张数} for k:=1 to 40 do {2元的张数} for l:=1 to 40 do {1元的张数} begin c:=c+1; {统计循环执行次数} if (i+j+k+l=40) and (i*10+j*5+k*2+l=100) then begin {张数一共是40,并且总和是100元} {write('[',i,' ',j,' ',k,' ',l,']');} s:=s+1; {统计可行的方法} end; end; writeln(s);{显示换零方法总数} writeln(c);{显示内部执行总数} end. 程序运行结果: 34 2560000
程序2 • 当我们确定了i,j,k后,就直接可以确定l的值了l就是(40-i-j-k),而不需要再次进行循环。四重循环变成三重循环 var i,j,k,s:integer; c:longint; begin s:=0;c:=0; for i:=1 to 40 do for j:=1 to 40 do for k:=1 to 40 do begin c:=c+1; if (i+j+k<40) and (i*10+j*5+k*2+(40-i-j-k)=100) then begin {上述i,j,k之和一定要小于40 ,否则1元的张数就没有意义} {write(‘[’, i,' ',j,' ',k,' ',l,']');} s:=s+1; end; end; writeln(s); writeln(c); end. 程序运行结果: 34 64000
程序3 再来考虑一些边界值的量的问题,10元的最多取多少张?换100元最多10张,但是每种至少一张,所以最多取到7张。5元的也可以确定下来了,当然,2元的最多可取到37张。 var i,j,k,l,s:integer; c:longint; begin s:=0;c:=0; for i:=1 to 7 do {限定i,j,k的边界值} for j:=1 to 17 do for k:=1 to 37 do begin c:=c+1; if (i+j+k<40) and (i*10+j*5+k*2+(40-i-j-k)=100) then begin {write('[',i,' ',j,' ',k,' ',l,']');} s:=s+1; end; end; writeln(s); writeln(c); end. 程序运行结果: 34 4403
程序4还有可能继续优化么?举个例子:当i=3时,很显然j的取值最多只能到(100-3×10) div 5-2 张 var i,j,k,l,s:integer; c:longint; begin s:=0;c:=0; for i:=1 to 7 do for j:=1 to (100-i*10) div 5 - 2 do for k:=1 to (100-i*10-j*5) div 2 - 1 do begin c:=c+1; if (i+j+k<40) and (i*10+j*5+k*2+(40-i-j-k)=100) then begin {write('[',i,' ',j,' ',k,' ',l,']');} s:=s+1; end; end; writeln(s); writeln(c); end. 程序运行结果: 34 1190
程序运行结果: 34 2560000 程序运行结果: 34 64000 程序运行结果: 34 4403 程序运行结果: 34 1190
基础算法 • 判断一个数是否为素数 素数定义:除了1和本身之外,没有其它的约数。 思路:给出任意数N 分别用(1,N)之间的整除去除N,如果N被任意一个整除就说明N不是素数,否则就是素数 • 一级算法 flag=true; {假设N是素数,设立一个布尔型标志flag为true } for i:=2 to N-1 do if N mod i=0 then {如果N被i整除} flag:=false; {N的标志量置为false,表明N不是素数} writeln(flag); {输出标志量flag} 试除法!
算法优化 • 一级算法 flag=true; {假设N是素数,设立一个布尔型标志flag为true } for i:=2 to N-1 do if N mod i=0 then {如果N被i整除} begin flag:=false; {N的标志量置为false,表明N不是素数} break; end; writeln(flag); {输出标志量flag} 算法中,无论何种情况,哪怕N是偶数,循环体必须运行N-2次。 一次优化,利用break语句,一旦求余为0,则立即退出循环。 此时最差的情况下循环体才运行N-2次。
算法优化 • 二次优化 i的终值到sqrt(N)即可 flag=true; for i:=2 to trunc(sqrt(N)) do if N mod i=0 then begin flag:=false; break; end; writeln(flag); 此时最差的情况循环体循环trunc(sqrt(N))次。 能继续优化么?能,不过要以牺牲空间为代价
判断素数算法 var n:longint; begin readln(n); writeln(n,'-->',isprime(n)) ; end. • 将其设计成一个函数模块 program prime; function isprime(x:longint):boolean; var i:longint; flag:boolean; begin flag:=true; for i:=2 to round(sqrt(x)) do if x mod i=0 then begin flag:=false; break; end; isprime:=flag; end; 蓝色的自定义函数块,下次直接copy就可以用! 应用范围如: 1、找出<N的所有素数 2、证歌德巴赫猜想 ......
求素数——筛法 • 用筛法求出100以内的全部素数,并按每行五个数显示。 解:⑴ 把2到100的自然数放入a[2]到a[100]中(所放入的数与下标号相同); ⑵ 在数组元素中,以下标为序,按顺序找到未曾找过的最小素数minp,和它的位置p(即下标号); ⑶ 从p+1开始,把凡是能被minp整除的各元素值从a数组中划去(筛掉),也就是给该元素值置 0; ⑷ 让p=p+1,重复执行第②、③步骤,直到minp>Trunc(sqrt(N)) 为止; ⑸ 打印输出a数组中留下来、未被筛掉的各元素值,并按每行五个数显示。 用筛法求素数的过程示意如下(图中用下划线作删去标志) : ① 2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {置数} ② 2 3 4 5 6 7 8 9 10 11 12 13 14 15…98 99 100 {筛去被2整除的数 } ③ 2 3 4 5 6 7 8910 11 12 13 1415…9899100 {筛去被3整除的数 } 2 3 4 5 6 7 8910 11 12 13 1415…9899100 {筛去被5整除的数 } …………
Pascal程序: Program Exam53;{下标表示数据} const N=100; type xx=1 .. N; {自定义子界类型xx(类型名)} Var a: array[xx] of boolean; i,j: integer; Begin Fillchar(a,sizeof(a),true); a[1] := False; for i:=2 to Trunc(sqrt(N)) do if a[i] then for j := 2 to N div I do a[i*j]:= False; t:=0; for i:=2 to N do if a[i] then Begin write(a[ i ]:5); inc(t); if t mod 5=0 then writeln end; readln End.
练习:请用试除法设计一个求2~100之间所有素数的程序练习:请用试除法设计一个求2~100之间所有素数的程序
基础算法:求最大公约数,最小公倍数 • 求a,b最大公约数:用辗转相除法: ① r ←a mod b ②如果 a mod b=0 转 ④ 否则转③ ③a←b,b←r ,r ←a mod b ,转② ④输出b
最大公约数 var a,b,r:integer; begin readln(a,b); r:=a mod b; while r <>0 do begin a:=b; b:=r; r:=a mod b; end; writeln(b); end. 请换种算法设计求最大公约数的程序
分解质因数 • var n,k:longint;beginreadln(n);k:=2;write(n,‘=’);while n<>k do begin if n mod k=0 then begin write(k,‘*’);{输出 k* 格式,} n:=n div k; {n的新值为n去掉其中一个因子k} end else inc(k);{如果n不能被k整除,就把K往上递增。} end; writeln(k);{此处一个k为最后一个因子k,因为n=k 时,不满足条件,直接推出while循环,所以无法输出循环内k'*',所以在此处要输出最后一个因子}end.
排序算法——选择排序 选择排序 【示例算法描述】:初始关键字 [49 38 65 97 76 13 27 49]第一趟排序后 13 [38 65 97 76 49 27 49]第二趟排序后 13 27 [65 97 76 49 38 49]第三趟排序后 13 27 38 [97 76 49 65 49]第四趟排序后 13 27 38 49 [49 97 65 76]第五趟排序后 13 27 38 49 49 [97 65 76]第六趟排序后 13 27 38 49 49 65 [97 76]第七趟排序后 13 27 38 49 49 76 [97 76]最后排序结果 13 27 38 49 49 76 76 97
var a:array[1..10] of integer; i,j,t,m:integer; begin randomize; for i:=1 to 10 do begin a[i]:=random(100);write(a[i],' ');end; writeln; for i:=1 to 9 do begin m:=i; for j :=i+1 to 10 do begin if a[m]>=a[j] then m:=j; end ; t:=a[i]; a[i]:=a[m]; a[m]:=t; end; for i:=1 to 10 do write(a[i],' '); writeln; end.
冒泡排序 • 冒泡排序的基本概念是:依次比较相邻的两个数,将大数放在前面,小数放在后面。 • 即首先比较第1个和第2个数,将大数放前,小数放后。然后比较第2个数和第3个数,将大数放前,小数放后,如此继续,直至比较最后两个数,将大数放前,小数放后,此时第一趟结束,在最后的数必是所有数中的最小数。重复以上过程,仍从第一对数开始比较(因为可能由于第2个数和第3个数的交换,使得第1个数不再大于第2个数),将大数放前,小数放后,一直比较到最小数前的一对相邻数,将大数放前,小数放后,第二趟结束,在倒数第二个数中得到一个新的最小数。如此下去,直至最终完成排序。
var i,j,t:integer; a:array[1..10] of integer; begin randomize; for i:=1 to 10 do begin a[i]:=random(100);write(a[i],' '); end;writeln; for i:=1 to 9 do for j:=1 to 10-i do if a[j]>a[j+1] then begin t:=a[j]; a[j]:=a[j+1]; a[j+1]:=t; end; for i:=1 to 10 do write(a[i],' ') ; writeln; end.
顺序查找 • 在对线性表的操作中,经常需要查找某一个元素在线性表中的位置。此问题的输入是待查元素x和线性表L,输出为x在L中的位置或者x不在L中的信息。 L 76 x
const MAXN=9; L:array[1..MAXN] of integer=(2,15,3,49,76,28,99,21,17); var i,x:integer; begin readln(x); i:=1; while i<=MAXN do {进行顺序查找} begin if x=L[i] then break; {如果找到就跳出循环} inc(i); end; if i>MAXN then {根据i的位置,来判断是否找到} writeln('Not found!') else writeln('x is L[',i,']'); end.
min max mid min max mid min max 顺序查找 • 顺序查找法,对于有n个元素的线性表在最坏情况下需要n次比较。 • 下面我们考虑一种简单的情况。假设该线性表已经排好序了,我们是否有改进查找效率的可能呢? L mid x 71
Const MAXN=9; L:array[1..MAXN] of integer=(2,3,17,29,36,38,59,71,97); var i,x,min,max,mid:integer; found:boolean; begin readln(x); min:=1;max:=MAXN;mid:=(min+max) div 2; while min<=max do begin if x=L[mid] then {如果找到,跳出循环} begin found:=true; break; end; if x>L[mid] then {如果x处于位置mid的右半边,那么把min提到mid+1的位置} min:=mid+1 else {如果x处于位置mid的左半边,那么把max提到mid-1的位置} max:=mid-1; mid:=(min +max) div 2;{计算中间mid位置} end; if found then writeln('x is L[',mid,']') else writeln('Not found!'); end.