常州市武进区鸣凰中心小学
专题网站
当前位置:武进教育专题网站 > 常州市武进区鸣凰中心小学 >专题列表 > 专题网站 > 未成年人思想道德建设 > 乡村少年宫 > 少年宫各活动室 > 信息活动室

信息活动教案

来源:本站原创   发布人:蒋薇   发布时间:2012-09-14  浏览次数:
 

高精度算法

教学目标:

1、学习高精度数的存储

2、学会简单高精度的运算:加、减、乘

3、掌握高精度运算的应用

教学重点、难点:

高精度乘,高精度运算的应用

教学过程:

一、为什么要进行高精度计算

计算机的特点:运算速度快、计算精度高、自动化等;

计算机的应用:数值运算和非数值运算,数值运算的一个重要问题就是精度问题。比如π的计算,小数点后面要几万位,计算机中的数据类型无法保存;或者数很大,超过了一般数的范围(integer:-32768——32767longint:-2147483628——2147483627real-1038——-10-3810-38——1038)。这时就属于高精度问题。

二、精度数的存储

数组:每个数组元素存储1位。每一位都可以直接加减,运算方便;但不能直接读入,读入后必须转化成数字;

字符串:最长255位,能直接输入输出,符合数值的输入习惯;但每位是一个字符,必须先转换成数字才能进行运算,运算时不方便。

一般,在输入数据不长于255个字符的情况下,通常采用字符串的方式读入,然后再倒置(为了低位对齐,方便以后的运算和进位)并转换成数字存入数组。这样,取长补短,简化操作。

三、高精度的运算

a)                加法:先加后进位,注意进位问题;两数之和的位数最大为较大数的位数加1

     设进位标志为t,从低位往高位进,t=0表示本位运算无进位,t=1有进位;

     每一项sum[i]:=A[i]+B[i]+t;程序如下:

         定义数组AB,长度为max,定义数组sum,长度为max+1;初始化为0

         假设以字符串输入,stra,strb

         求出长度lena,lenb;存入数组;

         maxlen=max(lena,lenb);

t:=0;

         for I:=1 to maxlen+1 do

           begin

sum[i]:=A[i]+B[i]+t;

t:=sum[i] div 10;

sum[I]:=sum[I] mod 10;

               end;

         输出sumif sum[maxlen+1]<>0 then write(sum[maxlen+1]);

                   for I:=maxlen downto 1 do write(sum[i]);

b)                减法:先减后借位;计算前,首先需要对被减数和减数进行比较,若减数比被减数大,则要先打印“-”号,然后交换被减数和减数求差。主要程序段如下:

t:=0;

For I:=1 to lena do

  Begin

c[I]:=a[I]-b[I]-t;

If c[I]<0 then begin c[I]:=c[I]+10;t:=1;end

          Else t:=0;

  End;

c)                  乘法:乘积的位数最大为两个因子的位数之和;先用被乘数的某位和乘数的每一位求积,填入相应   的位置而不考虑进位的问题,全部计算完后,对积进行一次进位处理。

   For I:=1 to lena do

Begin

  For j:=1 to lenb do cj[I+j-1]:=cj[I+j-1]+a[I]*b[j];

  T:=0;

  For j:=1 to lena+lenb do

    Begin

      Cj[j]:=cj[j]+t;

T:=cj[j] div 10;

Cj[j]:=cj[j] mod 10;

    End;

End;

15-1:用高精度计算出S=1+2+3++n!(n50),输入正整数N,输出计算结果S

[算法设计]  本题涉及高精度加法和乘法运算,程序使用二个一维数组sf分别存储到当前项为止的和与当前项的值,计算当前项的值采用递推的迭代的方法,即k!=(k-1)*k

程序:

program eg15-1(input,output);

const maxlen=100;

type arraytype=array [0..maxlen] of longint;

var i,n:integer;

f,s:arraytype;

procedure mul(var a:arraytype; k:longint);

  var i:longint;

  begin

     for i:=0 to maxlen do f[i]:=f[i]*k;

     for i:=0 to maxlen-1 do

       begin

          a[i+1]:=a[i+1]+a[i] div 10;

          a[i]:=a[i] mod 10

       end

end;

procedure add(var a:arraytype;b:arraytype);

var i:longint;

begin

     for i:=0 to maxlen do a[i]:=a[i]+b[i];

     for i:=0 to maxlen-1 do

         if a[i]>=10 then

            begin

                 a[i+1]:=a[i+1]+1;

                 a[i]:=a[i]-10

            end

end;

procedure print(a:arraytype);

var i,j:longint;

begin

     i:=maxlen;

     while (i>0) and (a[i]=0) do i:=i-1;

     for j:=i downto 0 do write(a[j])

end;

begin  {main}

     write('Input n:');readln(n);

     for i:=0 to maxlen do s[i]:=0;

     for i:=1 to maxlen do f[i]:=0;

     f[0]:=1;

     for i:=1 to n do

       begin

          mul(f,i);

          add(s,f);

       end;

     print(s);

     writeln

end.

三、对高精度的思考和优化

要想提高高精度程序的效率,除了优化存储结构外,更关键的是优化算法的运算过程和方法。比如:计算两个8位数的和:

方法1分成8个小整数(integer),每个都是一位数,需要做8次加法;

方法2分成2个小整数(integer),每个为4位数,需要2次加法。

方法3直接使用lingint1次加法。

时间分析:integer类型计算1位数和计算4位数,系统时间是基本一样的,所以,方法2比方法1优;用 longint计算比用integer计算,系统要花费更多的时间,但运算次数明显减少,实践证明:方法3比方法2快一点。

归纳:一般情况下,使用longint进行加减法运算时,每个小整数可保存到9位;乘法运算时,每个小整数可保存4位;除法运算时(设除数位数为x),当x<=8时,被除数存储时可把每个小整数开到9-x位;若x>8,则被除数和除数的每个小整数用1位,编程运算时最方便,效率也较高。

问题:k进制数的高精度运算

四、举例

 15-3、回文数

若一个数(首位不为零)从左向右读与从右向左读都是一样,我们就将其称之为回文数。

例如:给定一个10进制数56,将5656(即把56从右向左读),得到121是一个回文数。

又如:对于10进制数87

      STEP187+78  = 165                  STEP2165+561 = 726

      STEP3726+627 = 1353            STEP41353+3531 = 4884

在这里的一步是指进行了一次N进制的加法,上例最少用了4步得到回文数4884

写一个程序,给定一个N2<=N<=10N=16)进制数M,求最少经过几步可以得到回文数。

如果在30步以内(包含30步)不可能得到回文数,则输出“Impossible!”

样例:

INPUT

        N = 9  M= 87

      OUTPUT

        STEP=6

[算法设计] 本问题的核心是高精度加法,具体处理起来可以分为三个部分。

第一,是对读入数据的处理。本题中,数据的进制是可变的,所以用整型数(即十进制数)不是很方便,考虑一般情况,我们可以采用字符串读入,再对数据作后期加工,把它的每一位都分离开来,存入一个数组digit里,数组invert存放数组digit的倒序,变量len记录了数据的位数。在转化过程中要注意到16进制的特殊性,要对于字母A B C D E F要单独处理。

第二,进行判断与运算。存储数据时用什么形式表示其实无所谓,关键是做加法运算的时候要符合n进制的规律。所以,运算时一律以10进制形式存储,存储该位数码对应的十进制数值,A10F15表示。判断是否构成回文数时也一样用10进制,逐对比较对称的两个数位上的数字,看它们是否相等。做加法的次数以30次为限。

第三,输出结果。如果当前的数是回文数,则输出步数;否则按“Impossible”输出。

程序:

program eg15-4(input,output);

const max=1000;

type arraytype=array [1..max] of longint;

var i,n,len,step:longint;

    str:string;

    digit,invert:arraytype;

function palindrome(var digit:arraytype;len:longint):boolean;

var i,j:longint;

begin

     i:=1; j:=len;

     while (i<j) and (digit[i]=digit[j]) do

     begin i:=i+1; j:=j-1 end;

     if i<j then palindrome:=false else palindrome:=true

end;

begin

     write('Input n:'); readln(n);

     write('Input number:'); readln(str);

     for i:=1 to max do digit[i]:=0;

     len:=length(str);

     for i:=1 to len do

         if str[i]>='A'

            then digit[len+1-i]:=ord(str[i])-ord('A')+10

            else digit[len+1-i]:=ord(str[i])-ord('0');

     step:=0;

     while (step<30) and not(palindrome(digit,len)) do

     begin

          for i:=1 to len do invert[i]:=digit[len+1-i];

          for i:=1 to len do digit[i]:=digit[i]+invert[i];

          for i:=1 to len do

              if digit[i]>=n then

                 begin digit[i+1]:=digit[i+1]+1;

                       digit[i]:=digit[i]-n end;

          if digit[len+1]>0 then len:=len+1;

          step:=step+1

     end;

     if step=30  then writeln('Impossible!')

                 else writeln('STEP=',step)

end.

 

三、         练习

 

 

                                       

 

 

 

 

 

 

 

 

循环结构的程序设计

教学目标:

1While语句与当型循环的含义及使用方法

2Repeat语句与直到型循环的含义及使用方法

3for语句与计数循环的含义及使用方法

4、多重循环(循环的嵌套)的含义及使用方法

教学重、难点:

三种循环结构的理解及其在实践中的使用

教学过程:

许多处理过程中有连续的重复,这时候如果还是一句句地重复写的话,既麻烦又累赘,当要重复成千上万次时,这种重复的书写几乎是不可能实现的。直接简便的方法是用循环语句来实现循环。

一.While语句与当型循环

1.格式:

while 布尔表达式 do 语句;

2.说明:

格式中whiledo都是保留字,布尔表达式表示条件,它的描述跟条件语句里的条件描述是一样的。

Do后面的语句可以是单一语句也可以是复合语句,称为循环体。只要布尔表达式成立时(即值为TRUE时)就执行循环体,如此反复直到布尔表达式不成立(值为FALSE)时停止。如果一开始就为布尔表达式就不成立(值为FALSE),那么循环体一次也不执行。

 

5-1:输出1100的算术平方根。

【分析】:

计算一个数的算术平方根可以用一个函数sqrt(i),其中的I1一直变化到100,通过循环来实现。

【程序清单】:

program ex5-1;

var  i:integer;

begin

i:=1;

while i<=100 do

     begin

       writeln(‘Squrare root of’, i, ‘ is ‘, sqrt(i):8:4);

       i:=i+1;

     end;

end.

【思考与提高】:

    上面程序中I的初值为1,在循环体中,先是输出I的算术平方根再将I的值增加1,所以最后循环结束时I的值是101while的条件也要写成I<=100或写为 I<101,如果写成I<100,那么在I=99时满足循环的条件,先输出99的算术平方根,然后I的值增加1变为100,此时不满足循环的条件了,所以便结束循环,结果100的算术平方根没有输出。因此我们在用循环时一定要把握好控制循环的边界条件,不能随便扩大边界也不能随便缩小边界。

    另一种写法:i的初值为0,条件的描述及循环体的相关语句的顺序都要作调整:

  i:=0;

while i<100 do

begin

i:=i+1;

writeln(……);

end;

【注意】:

while循环体中一定要有相应的语句使布尔表达式的值可能为false,否则就会构成死循环。

3、循环的强制终止

一般来说,只要循环的条件及循环体描述得当,循环都能顺利结束,不会产生死循环的。有时在程序运行中会有一些特殊的情况,需要终止当前循环的执行,如果将这个条件写到循环体外有不太方便,这时可以使用break来强制终止当前循环的执行。

二、Repeat语句与直到型循环

1.格式:

repeat

      语句;

语句;

……;

语句;

until 布尔表达式;

2.说明

格式中repeatuntil都是保留字,其间的语句构成循环体,最后一个语句的分号可以省略;until后的布尔表达式表示条件,描述的是循环结束的条件。

3.功能

反复执行循环体直到布尔表达式的值为true时为止。

4.示例:

上例的程序段可写为:

begin

i:=1;

repeat

   writeln(‘Square root of  ‘, i, ‘ is ‘,sqrt(i):8:4);

   i:=i+1;

until i>100;

end.

5WhileRepeat语句对比

whilerepeat语句一般情况下可以相互替换。它们的主要区别是:

while是先判断后执行,而repeat是先执行后判断,因此while语句的循环体有可能一次也不执行,而repeat语句至少执行一次;

前者是当条件满足时执行,而后者是当条件不满足时执行;

前者的循环体是复合语句时要用beginend,而后者却不一定要用。

三、for语句与计数循环

1.格式:

1for 变量标识符:=初值表达式 to终值表达式 do 语句;

 从小到大执行的格式

2for 变量标识符:=初值表达式 downto终值表达式 do 语句;

 从大到小执行的格式

2.说明

格式中的for,to,downto,do都是保留字,to一般用在升序的计数,而downto用在降序的计数。变量标识符在这里称作是控制变量,必须是离散(有序)数据类型,如:

for i:=1 to 100  do ….

for ch:=’a’  to  ‘z’  do….

3.示例

上面的例子用for语句写出的程序段为:

for i:=1 to 100 do wirteln(‘Square root of ‘ ,i,’ is’, sqrt(i):8:4);

也可以写成:

for i:=100 downto 1 do

 wirteln(‘Square root of ‘ ,101-i,’ is’, sqrt(101-i):8:4);

 

for语句的形式简单,但它也有一定的局限性,主要是控制变量的值不能随意变化,每次只能取其后继(to的情况)或前趋(用downto的情况),另一限制是控制变量只能用简单的有序的离散量,且循环次数已定,不能象whilerepeat那样通过布尔表达式来控制循环的操作。

5-2:用for 语句计算前NN>1)个自然数中所有偶数的和。

【分析】:

判定偶数可有多种方法,也可以不用判定而直接去构造偶数,由此得到更多的方法。

【方法1】:

构造偶数,1n之间的偶数有 N  DIV 2个,所以从第一个构造到最后一个偶数,并将它加到存储和的变量中。

sum:=0;

for counter:=1 to n div 2 do

 sum:=sum+2*counter;

【方法2】:

    1n中任一个自然数进行检测,如果它除以2的余数为0表明它是偶数,则将其加到和中。

 for counter:=1 to n do

 if counter mod 2=0 then sum:=sum+counter;

【方法3】:

用标准函数odd(x),判定是否是奇数,如果不是奇数说明它是偶数。

for counter:=1 to n do

if not odd(counter) then sum:=sum+counter;

【方法4】:

   在方法1中是构造偶数相加,也可以先相加,再考虑偶数的情况,如10以内的偶数为246810,将它们标明序号为12345,序号为原数的一半,因此只要求出序号的和,再将序号的和乘以2便得到偶数的和。

 For counter:=1 to n div 2 do sum:=sum+counter;

 Counter:=counter*2;

注意:一般情况下不允许在for语句的循环体中改变控制变量的值。

四.多重循环(循环的嵌套)

当程序中要用到多个循环时,如果这些循环是并列的关系,那么它们彼此之间的控制变量不相互影响。

而当一个循环的循环体中又有循环时,这就是循环的嵌套,称为多重循环。如前面要大家输入的“输出九九乘法表”程序。

5-3:编写程序输出如下的字母塔:

    A

             ABA

            ABCBA

…………….

ABCD……DCBA

【分析】:

此题有两个关键:一是确定每一行前导空格符的数目;二是按一定规律输出英文大写字母,共26行。应能保证最后一行前导空格数目至少为0,设最后一行的前面空格数为10个,那么倒数第二行前面的空格数为11,倒数第三行的数目为12……,如果控制输出行的字符变量为c,则空格数为:ord(‘Z’)-ord(c)+10

【程序清单】:

program eg5-3;

var ch,c:char;

begin

for c:=’A’ to ‘Z’ do

 begin

  write(‘ ‘:ord(‘Z’)-ord(c)+10);   {输出空格数}

  for ch:=’A’ to c do write(ch);    {输出一行的左半部分}

  for ch:=pred(c) downto ‘A’ do write(ch);   {输出一行的右半部分}

  writeln;               {换行}

 end;

end.

【思考与提高】:

上面整个程序是个两重循环,而外循环的循环体是两个并列的循环,因此虽然程序中有三个for语句,但只是两重循环。

而程序中输出一行时是通过两个循环来实现的,能不能通过一个循环来实现呢?用字符型变量来控制循环不太方便,可以用整型变量来控制循环列方便一些,相应的程序段如下:

for I:=1 to 26 do

 begin

 write(‘’:36-I);

 for j:=1-I to I-1 do write(chr(64+I-abs(j)));

 writeln;

end;