程序设计实习 第四讲 高精度计算

34
程程程程程程 程程程 程程程程程

Upload: tobias-raymond

Post on 31-Dec-2015

162 views

Category:

Documents


2 download

DESCRIPTION

程序设计实习 第四讲 高精度计算. 例题: ai2981 大整数加法 (P159) 问题描述 求两个不超过 200 位的非负整数的和。 输入数据 有两行,每行是一个不超过 200 位的非负整数,没有多余的前导 0 。 输出要求 一行,即相加后的结果。结果里不能有多余的前导 0 ,即如果结果是 342 ,那么就不能输出为 0342 。 输入样例 22222222222222222222 33333333333333333333 输出样例 55555555555555555555. 例题: ai2981 大整数加法 (P159) 解题思路 - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 程序设计实习 第四讲 高精度计算

程序设计实习第四讲

高精度计算

Page 2: 程序设计实习 第四讲 高精度计算

例题: ai2981大整数加法 (P159) 问题描述

求两个不超过 200 位的非负整数的和。 输入数据

有两行,每行是一个不超过 200 位的非负整数,没有多余的前导 0 。

输出要求一行,即相加后的结果。结果里不能有多余的前导 0 ,

即如果结果是 342 ,那么就不能输出为 0342 。 输入样例

2222222222222222222233333333333333333333

输出样例55555555555555555555

Page 3: 程序设计实习 第四讲 高精度计算

例题: ai2981 大整数加法 (P159) 解题思路

1) 用字符型或整型数组来存放大整数

an[0] 存放个位数, an[1] 存放十位数, an[2] 存放百位数……

2 )模拟小学生列竖式做加法,从个位开始逐位相加,超过或达到 10 则进位。

用 unsigned an1[201] 保存第一个数,用 unsigned an2[200] 表

示第二个数,然后逐位相加,相加的结果直接存放在 an1 中。要注意处理进位。

Page 4: 程序设计实习 第四讲 高精度计算

#include <stdio.h>#include <string.h>#define MAX_LEN 201int an1[MAX_LEN+10];int an2[MAX_LEN+10];char szLine1[MAX_LEN+10];char szLine2[MAX_LEN+10];int Add(int nMaxLen , int * an1, int * an2)// 将长度最多为 nMaxLen 的大整数 an1 和 an2 相加,结果放在 an1, //an1[0],an2[0] 对应于个位{

int nHighestPos = 0;for(int i = 0;i < nMaxLen; i ++ ) {

an1[i] += an2[i]; // 逐位相加if( an1[i] >= 10 ) { // 看是否要进位

an1[i] -= 10;an1[i+1] ++; // 进位

}if( an1[i] !=0 )

nHighestPos = i; // 记录最高位的位置}return nHighestPos;

}

Page 5: 程序设计实习 第四讲 高精度计算

int main() {scanf("%s", szLine1); scanf("%s", szLine2);int i, j;// 库函数 memset 将地址 an1 开始的 sizeof(an1) 字节内容置成 0//sizeof(an1) 的值就是 an1 的长度//memset 函数在 string.h 中声明memset( an1, 0, sizeof(an1));memset( an2, 0, sizeof(an2));// 下面将 szLine1 中存储的字符串形式的整数转换到 an1 中去,//an1[0] 对应于个位int nLen1 = strlen( szLine1);for(j =0, i = nLen1 - 1;i >= 0 ; i --)

an1[j++] = szLine1[i] - '0';int nLen2 = strlen(szLine2);for(j=0, i = nLen2 - 1;i >= 0 ; i --)

an2[j++] = szLine2[i] - '0';int nHighestPos = Add(MAX_LEN,an1,an2);for( i = nHighestPos; i >= 0; i -- ) printf("%d", an1[i]);return 0;

}

Page 6: 程序设计实习 第四讲 高精度计算

例题: ai2736 大整数减法

问题描述

求 2 个大的正整数相减的差

输入数据

第 1 行是测试数据的组数 n ,每组测试数据占 2 行,第 1 行是被减数 a ,第 2 行是减数 b(a > b) 。每组测试数据之间有一个空行,每行数据不超过 100 个字符

输出要求

n 行,每组测试数据有一行输出是相应的整数差

Page 7: 程序设计实习 第四讲 高精度计算

例题: ai2736 大整数减法

输入样例

2 9999999999999999999999999999999999999 9999999999999

5409656775097850895687056798068970934546546575676768678435435345 1

输出样例

99999999999999999999999900000000000005409656775097850895687056798068970934546546575676768678435435344

Page 8: 程序设计实习 第四讲 高精度计算

#include <stdio.h>#include <string.h>#define MAX_LEN 110int an1[MAX_LEN];int an2[MAX_LEN];char szLine1[MAX_LEN];char szLine2[MAX_LEN];int Substract( int nMaxLen, int * an1, int * an2){ // 两个最多 nMaxLen 位的大整数 an1 减去 an2, an1 保证大于 an2

int nStartPos = 0;for( int i = 0;i < nMaxLen ; i ++ ) {

an1[i] -= an2[i]; // 逐位减if( an1[i] < 0 ) { // 看是否要借位

an1[i] += 10;an1[i+1] --; // 借位

}if( an1[i] != 0)

nStartPos = i; // 记录最高位的位置}return nStartPos; // 返回值是结果里面最高位的位置

}

Page 9: 程序设计实习 第四讲 高精度计算

int main(){

int n;scanf("%d",&n);while(n -- ) {

scanf("%s", szLine1);scanf("%s", szLine2);int i, j;memset( an1, 0, sizeof(an1));memset( an2, 0, sizeof(an2));// 下面将 szLine1 中存储的字符串形式的整数转换到 an1 中去,//an1[0] 对应于个位int nLen1 = strlen( szLine1);for(i=0; i < nLen1 – 1; i++)

an1[i] = szLine1[nLen1-1-i] – ‘0’;int nLen2 = strlen(szLine2);for(i=0; i < nLen2 – 1; i++)

an1[i] = szLine1[nLen2-1-i] – ‘0’;int nStartPos = Substract( MAX_LEN, an1,an2);for( i = nStartPos; i >= 0; i -- )

printf("%d", an1[i]);printf("\n");

} return 0;}

Page 10: 程序设计实习 第四讲 高精度计算

例题: ai2980 大整数乘法

问题描述

求两个不超过 200 位的非负整数的积。

输入数据有两行,每行是一个不超过 200 位的非负整数,没有多余的前

导 0 。输出要求一行,即相乘后的结果。结果里不能有多余的前导 0 ,即如果

结果是 342 ,那么就不能输出为 0342 。 输入样例1234567890098765432100输出样例1219326311126352690000

Page 11: 程序设计实习 第四讲 高精度计算

解题思路

用 unsigned an1[200] 和 unsigned an2[200] 分别存放两个乘数,用 aResult[400] 来存放积。计算的中间结果也都存在 aResult 中。aResult 长度取 400 是因为两个 200 位的数相乘,积最多会有 400位。 an1[0], an2[0], aResult[0] 都表示个位。

一个数的第 i 位和另一个数的第 j 位相乘所得的数,一定是要累加到结果的第 i+j 位上。这里 i, j 都是从右往左,从 0 开始数。

计算的过程基本上和小学生列竖式做乘法相同。为编程方便,并不急于处理进位,而将进位问题留待最后统一处理。

Page 12: 程序设计实习 第四讲 高精度计算

现以 835×49 为例来说明程序的计算过程。

先算 835×9 。 5×9 得到 45 个 1 , 3×9 得到 27 个 10 ,8×9 得到 72 个 100 。由于不急于处理进位,所以 835×9算完后, aResult 如下:

接下来算 4×5 。此处 4×5 的结果代表 20 个 10 ,因此要 aResult[1]+=20 ,变为:

Page 13: 程序设计实习 第四讲 高精度计算

再下来算 4×3 。此处 4×3 的结果代表 12 个 100 ,因此要 aResult[2]+= 12 ,变为:

最后算 4×8 。此处 4×8 的结果代表 32 个 1000 ,因此要

aResult[3]+= 32 ,变为:

Page 14: 程序设计实习 第四讲 高精度计算

乘法过程完毕。接下来从 aResult[0] 开始向高位逐位处理进位问题。 aResult[0] 留下 5 ,把 4 加到 aResult[1] 上, aResult[1] 变为 51 后,应留下 1 ,把 5 加到 aResult[2] 上……最终使得 aResult 里的每个元素都是 1 位数,结果就算出来了:

Page 15: 程序设计实习 第四讲 高精度计算

#include <stdio.h>#include <string.h>#define MAX_LEN 200unsigned an1[MAX_LEN+10];unsigned an2[MAX_LEN+10];unsigned aResult[MAX_LEN * 2 + 10];char szLine1[MAX_LEN+10];char szLine2[MAX_LEN+10];int main(){

gets( szLine1); //gets 函数读取一行gets( szLine2);int i, j;int nLen1 = strlen( szLine1);memset( an1, 0, sizeof(an1));memset( an2, 0, sizeof(an2));memset( aResult, 0, sizeof(aResult));j = 0;for( i = nLen1 - 1;i >= 0 ; i --)

an1[j++] = szLine1[i] - '0';int nLen2 = strlen(szLine2);j = 0;for( i = nLen2 - 1;i >= 0 ; i --)

an2[j++] = szLine2[i] - '0';

Page 16: 程序设计实习 第四讲 高精度计算

// 每一轮都用 an2 的一位,去和 an1 各位相乘,从 an1 的个位开始

for( i = 0;i < nLen2; i ++ ) {// 用选定的 an2 的那一位,去乘 an1 的各位 for( j = 0; j < nLen1; j ++ )

// 两数第 i, j 位相乘,累加到结果的第 i+j 位aResult[i+j] += an2[i]*an1[j];

}// 下面的循环统一处理进位问题int nHighestPos = 0;for( i = 0; i < MAX_LEN * 2; i ++ ) {

if( aResult[i] >= 10 ) {aResult[i+1] += aResult[i] / 10;aResult[i] %= 10;

}if( aResult[i] ) nHighestPos = i;

}for( i = nHighestPos; i >= 0; i -- )

printf("%d", aResult[i]);return 0;

}

Page 17: 程序设计实习 第四讲 高精度计算

例题: ai2737 大整数除法 (P165)求两个大的正整数相除的商

基本的思想是反复做减法,看看从被除数里最多能减去多少个除数,商就是多少。一个一个减显然太慢,如何减得更快一些呢?以 7546 除以 23 为例来看一下:开始商为 0 。先减去 23 的 100 倍,就是 2300 ,发现够减 3 次,余下 646 。于是商的值就增加 300 。然后用 646 减去 230 ,发现够减 2次,余下 186 ,于是商的值增加 20 。最后用 186 减去 23 ,够减 8 次,因此最终商就是 328 。

所以本题的核心是要写一个大整数的减法函数,然后反复调用该函数进行减法操作。

Page 18: 程序设计实习 第四讲 高精度计算

求 2 个大的正整数相除的商Input

第 1 行是测试数据的组数 n ,每组测试数据占 2 行,第 1 行是被除数,第 2 行是除数。每组测试数据之间有一个空行,每行数据不超过 100 个字符Output

n 行,每组测试数据有一行输出是相应的整数商Sample Input3 2405337312963373359009260457742057439230496493930355595797660791082739646 2987192585318701752584429931160870372907079248971095012509790550883793197894

10000000000000000000000000000000000000000 10000000000

5409656775097850895687056798068970934546546575676768678435435345 1

Sample Output0 1000000000000000000000000000000 5409656775097850895687056798068970934546546575676768678435435345

例题: ai2737 大整数除法 (P165)

Page 19: 程序设计实习 第四讲 高精度计算

例题: ai2737 大整数除法 (P165)

基本的思想是反复做减法,看看从被除数里最多能减去多少个除数,商就是多少。一个一个减显然太慢,如何减得更快一些呢?以 7546 除以 23 为例来看一下:开始商为 0 。先减去 23 的 100 倍,就是 2300 ,发现够减 3 次,余下 646 。于是商的值就增加 300 。然后用 646 减去 230 ,发现够减 2次,余下 186 ,于是商的值增加 20 。最后用 186 减去 23 ,够减 8 次,因此最终商就是 328 。

所以本题的核心是要写一个大整数的减法函数,然后反复调用该函数进行减法操作。

Page 20: 程序设计实习 第四讲 高精度计算

#include <stdio.h>#include <string.h>#define MAX_LEN 110int an1[MAX_LEN];int an2[MAX_LEN];int tmpAn2[MAX_LEN];int anResult[MAX_LEN];char szLine1[MAX_LEN];char szLine2[MAX_LEN];char szNouse[MAX_LEN];int Substract( int nMaxLen, int * an1, int * an2)// 大整数 an1 减去 an2 。两者最多 nMaxLen 位, an1 必须不小于 an2, 差放在 an1 里// 返回差的最高非 0 位的位置{ int nStartPos = 0;

for( int i = 0;i < nMaxLen ; i ++ ) {an1[i] -= an2[i]; // 逐位相if( an1[i] < 0 ) { // 看是否要借位

an1[i] += 10;an1[i+1] --; // 借位

}if( an1[i] )

nStartPos = i; // 记录最高位的位置}return nStartPos;

}

Page 21: 程序设计实习 第四讲 高精度计算

int Length( int nMaxLen,int * an)// 求大整数的位数。 0 算 0 位{

int i;for( i = nMaxLen -1 ; an[i] == 0 && i >= 0; i -- );if( i >= 0 )

return i + 1;return 0;

}void ShiftLeft( int nMaxLen,int * an1, int * an2, int n)// 将大整数 an1 左移 n 位,即乘以 10 的 n 次方,结果放到 an2 里{

if( n <= 0 ) memcpy( an2,an1,nMaxLen * sizeof(int)); else{

for( int i = nMaxLen -1 ; i >= 0; i -- )if( i - n >= 0)

an2[i] = an1[i-n];else

an2[i] = 0;}

}

Page 22: 程序设计实习 第四讲 高精度计算

int * Max(int nMaxLen, int * an1, int * an2)// 求大整数 an1 和 an2 里面大的那个// 如果都是 0 ,则返回 NULL{

bool bBothZero = true;for( int i = nMaxLen -1; i >= 0 ; i -- ) {

if( an1[i] > an2[i] )return an1;

else if( an1[i] < an2[i] )return an2;

else if( an1[i] )bBothZero = false;

}if( bBothZero)

return NULL;return an1;

}

Page 23: 程序设计实习 第四讲 高精度计算

int main(){

int n;scanf("%d",&n);gets(szNouse);while(n -- ) {

gets(szLine1);gets(szLine2);gets(szNouse);int i, j;// 库函数 memeset 将地址 an1 开始的 sizeof(an1) 字节内容置成 0//sizeof(an1) 的值就是 an1 的长度//memset 函数在 string.h 中声明memset( an1, 0, sizeof(an1));memset( an2, 0, sizeof(an2));// 下面将 szLine1 中存储的字符串形式的整数转换到 an1 中去,//an1[0] 对应于个位int nLen1 = strlen( szLine1);for(j = 0, i = nLen1 - 1;i >= 0 ; i --)

an1[j++] = szLine1[i] - '0';int nLen2 = strlen(szLine2);for( j = 0, i = nLen2 - 1;i >= 0 ; i --)

an2[j++] = szLine2[i] - '0';

Page 24: 程序设计实习 第四讲 高精度计算

int nHighestPos = 0;memset(anResult,0,sizeof(anResult));int nShiftLen = Length(MAX_LEN,an1) - Length(MAX_LEN,an2);// 只要 an1 大于 an2 ,就不停相减while( Max(MAX_LEN,an1,an2) == an1 ) {

// 算出 an1 的 10 的 nShiftLen 次方倍ShiftLeft(MAX_LEN, an2, tmpAn2,nShiftLen);// 重复减去 an1 的 10 的 nShiftLen 次方倍,看能减几次while( Max(MAX_LEN,an1,tmpAn2) == an1) {

Substract(MAX_LEN, an1,tmpAn2);// 记录商对应位anResult[nShiftLen] ++;

}// 记录结果最高位的位置if( nHighestPos == 0 && anResult[nShiftLen])

nHighestPos = nShiftLen;nShiftLen --;

}for( i = nHighestPos; i >= 0; i -- )

printf("%d", anResult[i]);printf("\n");

} return 0;}

Page 25: 程序设计实习 第四讲 高精度计算

例题: ai2952 循环数 问题描述

当一个 N 位的整数 X 满足下列条件时,称其为循环数: X与任意一个整数 1≤Y ≤ N 相乘时,都将产生一个 X 的“循环”。即:分别将这两个整数的第 1 位数字与最后 1 位数字连在一起,可以得到一个相同的数字循环;当然两个整数在该数字循环中的起始位置不同。例如, 142857 是一个循环数 142857 *1 = 142857

142857 *2 = 285714 142857 *3 = 428571 142857 *4 = 571428 142857 *5 = 714285 142857 *6 = 857142

Page 26: 程序设计实习 第四讲 高精度计算

输入 写一个程序判断一个整数是否是循环数。输入文件

是一个整数序列,每个整数长度为 2~60 。注意:每个整数前面的零被看作是该整数的一部分,在计算 N 时要统计。例如“ 01” 是一个 2 位的整数,而“ 1” 是一个 1 位的整数。

输出 对每个输入整数,输出一行,说明该整数是否是循

环数。

Page 27: 程序设计实习 第四讲 高精度计算

样例输入142857

142856

142858

01

0588235294117647 样例输出

142857 is cyclic

142856 is not cyclic

142858 is not cyclic

01 is not cyclic

0588235294117647 is cycl

Page 28: 程序设计实习 第四讲 高精度计算

解题思路 高精度的乘法:整数可能达 60 位

X*1: Y0 = X;

X*2: Y1 = Y0+X

X*3: Y2 = Y1+X ……

Yi 是否是 X 的“循环”?

Page 29: 程序设计实习 第四讲 高精度计算

解题思路 : Yi 是否是 X的“循环”?穷举: N 位整数,循环移位可以有 N种可能 循环移位方法:

Yi 是否是“ XX” 的子串?

1 2 3 4 5 6 7 1 2 3 4 5 6 7 \0

Page 30: 程序设计实习 第四讲 高精度计算

#include <stdio.h>#include <string.h>#define MAX_LEN 201int an1[MAX_LEN+10];int an2[MAX_LEN+10];char szLine1[MAX_LEN+10];char szLine2[MAX_LEN+10];char szDouble[2 * MAX_LEN + 10];int Add(int nMaxLen , int * an1, int * an2)// 将长度最多为 nMaxLen 的大整数 an1 和 an2 相加,结果放在 an1//an1[0],an2[0] 对应于个位{

int nHighestPos = 0;for(int i = 0;i < nMaxLen; i ++ ) {

an1[i] += an2[i]; // 逐位相加if( an1[i] >= 10 ) { // 看是否要进位

an1[i] -= 10;an1[i+1] ++; // 进位

}if( an1[i] )

nHighestPos = i; // 记录最高位的位置}return nHighestPos;

}

Page 31: 程序设计实习 第四讲 高精度计算

int main(){

while( gets( szLine1 ) && szLine1[0] ) {int i, j;

// 库函数 memeset 将地址 an1 开始的 sizeof(an1) 字节内容置成 0

//sizeof(an1) 的值就是 an1 的长度//memset 函数在 string.h 中声明memset( an1, 0, sizeof(an1));memset( an2, 0, sizeof(an1));

// 下面将 szLine1 中存储的字符串形式的整数转换到 an1 中去,//an1[0] 对应于个位int nLen1 = strlen( szLine1);for(j = 0, i = nLen1 - 1;i >= 0 ; i --) {

an1[j] = szLine1[i] - '0';an2[j++] = szLine1[i] - '0';

}strcpy( szDouble, szLine1);strcat( szDouble, szLine1);

Page 32: 程序设计实习 第四讲 高精度计算

bool bOk = true;for( i = 1; i < nLen1; i ++ ) {

int nHighestPos = Add(MAX_LEN,an1,an2);if ( nHighestPos >= nLen1 ) { // 长度超出了

bOk = false;break;

}for( int k = 0; k < nLen1; k ++ ) // 转换成字符串

szLine2[nLen1- k - 1] = an1[k] + '0';szLine2[nLen1] = 0;if( strstr( szDouble ,szLine2 ) == NULL ) {

bOk = false;break;

}}if( bOk)

printf( "%s is cyclic\n",szLine1);else

printf( "%s is not cyclic\n",szLine1);}

return 0;}

Page 33: 程序设计实习 第四讲 高精度计算

第四讲作业

1,ai 2951 :浮点数求高精度幂 2,ai 2738 :浮点数加法

Page 34: 程序设计实习 第四讲 高精度计算

00948152 唐良晓 00948248 颜聪 00948300 刘琦 00948196 褚海