书籍是人类知识的总结,书籍是全世界的营养品。——莎士比亚
万进制高精度运算(C++语言)
目前在青少年信息学奥林匹克竞赛中所涉及到的高精度计算包括加(addition)、减(subtract)、乘(multiply)、除(divide)四种基本运算。其中乘法分高精度数乘高精度数和单精度数乘高精度数两种,除法一般指两个单精度数相除,求解最终指定精度的解,找出循环节或输出指定精度位数的小数。(注:高精度数与单精度数均指整数)
主要的解题思想是利用在小学就曾学习过的竖式加减乘除法则,用程序语言实现存在的问题主要有如何存储高精度数的值,如何实现计算等问题。 一. 高精度数字的存储
我们日常书写一个高精度数字,左侧为其高位,右侧为其低位,在计算中往往会因进位(carry )或借位(borrow )导致高位增长或减少,因此我们定义一个整型数组(int bignum[maxlen])从低位向高位实现高精度整数的存储,数组的每个元素存储高精度数中的一位。(如下表所示)
高精度数 3(高位)
…… 7 9 4(低位)
int bignum[i]
n
……
2
1
显然,在C++语言中,int 类型(4个字节/32位计算机)元素存储十进制的一位数字非常浪费空间,并且运算量也非常大,因此常将程序代码优化为万进制,即数组的每个元素存储高精数字的四位。在后面的叙述过程中均以万进制为例介绍。(为什么选择万进制,而不选择更大的进制呢?十万进制中的最大值99999相乘时得到的值是9999800001超过4个字节的存储范围而溢出,从而导致程序计算错误。)
在实际编写程序代码过程中常作如下定义: const int base=10000; const int maxlen=1000+1; int bignum[maxlen];
说明:base 表示进制为万进制,maxlen 表示高精度数的长度,1个元素能存储4个十进制位,1000个元素就存储4000个十进制位,而加1表示下标为0的元素另有它用,常用作存储当前高精度数字的位数。 二. 各种运算的程序实现 (一)加法:
首先回顾一下小学中曾学习的竖式加法,见图一:
bignum1[] 9475 46 1243 bignum2[]
918 1324 341 carry
1 0 0 0 bignum_ans[]
1
393
1370
1584
图一 加法的计算过程
从上面的图中我们可以得知,做加法运算是从低位向高位进行,如果有进位,下一位进行相加时要加上进位,如果最高位已计算完还有进位,就要增加存储结果的位数,保存起进位来。关于进位的处理,往往定义单独变量carry 进行存储,程序实现的过程如图二所示:
初始化
进位carry 赋初始值0,结果的位数为两个加数的最大位数。
当前位超过最高位了?
处理当前位和进位
N
Y
还有进位么?
N
结束
处理进位
Y
书籍是人类知识的总结,书籍是全世界的营养品。——莎士比亚
图二 加法的实现过程
高精度加法程序代码(bignum1+bignum2→bignum_ans ):
void addition(int *bignum1, int *bignum2, int *bignum_ans){ int carry=0; memset( bignum_ans, 0, sizeof(bignum_ans) );// memset 作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。 *bignum_ans=*bignum1>*bignu2?*bignum1:*bignum2; for(int pos=1; pos<=*bignum_ans; pos++){ carry+=bignum1[pos]+bignum2[pos]; bignum_ans[pos]=carry%base; carry/=base; } while(carry){ bignum_ans[++*bignum_ans]=carry%base; carry/=base; } }
说明:函数中的数组是引用传递,传递的是数组的首元素的地址,可用指针来代替,当前指针所指向的为0元素,后面的代码相同。
有的同学可能提出,在加法运算中的进位不管进制是多少,进位只可能出现1,用while 循环没有意义,在这里说明一下,为了跟后面乘法中出现的代码相匹配才采用这种处理方法,实现代码的统一性。 (二)减法:
bignum1[] 132 9475 46 1243 bignum2[] 132 918 1324 341 borrow 0 1 0 0 bignum_ans[]
8556
8722
902
图三 减法的计算过程
图三表示出了减法的计算过程,与加法不同的地方是进位变成了借位,另外就是计算结果的位数可能会比被减数的位数少,因此在处理过程中要更要注意结果到底是多少位的。
其次,日常我们做减法时,如果被减数小于减数,我们是把两数反过来进行相减,在前面添加负号标识。因此,我们在编写减法子函数时是约定bignum1大于bignum2的,调用时首先判断两个高精度数的大小,然后根据两数的大小决定如何调用。减法的实现过程如图四所示:
图四 减法的实现过程
高精度数比较程序代码:
int bignumcmp( int *bignum1, int *bignum2 ){ if (*bignum1-*bignum2) return *bignum1-*bignum2; for (int pos=*bignum1; pos>0; pos--)
初始化
借位borrow 赋初始值0,结果的位数为被减数的位数。
当前位超过最高位了?
处理当前位和借位
N
Y
结果高位为0?
N
结束
结果位数减1
Y
书籍是人类知识的总结,书籍是全世界的营养品。——莎士比亚
if ( bignum1[pos]-bignum2[pos] ) return bignum1[pos]-bignum2[pos]; return 0;
}
说明:bignum1>bignum2返回正整数,bignum1==bignum2返回0,bignum1<bignum2返回负整数。 解释:首先进行两数的位数的比较,如果位数相同再从高位向低位比较。
高精度减法程序代码(bignum1-bignum2→bignum_ans ):
void subtract( int *bignum1, int *bignum2, int *bignum_ans ){ int borrow=0; memset( bignum_ans, 0, sizeof(bignum_ans) ); *bignum_ans=*bignum1; for(int pos=1; pos<=*bignum_ans; pos++){ bignum_ans[pos]=bignum1[pos]-borrow-bignum2[pos]; if(bignum_ans[pos]<0){ bignum_ans[pos]+=base; borrow=1; }
else{
borrow=0; } } while( !bignum_ans[*bignum_ans] ) --*bignum_ans; }
(三)乘法:
乘法的计算过程正如图五所示的从乘数的最低位起枚举每一位与被乘数相乘,累加到结果当中。高精度乘高精度实际是多次调用单精度数乘高精高数运算。
1 2 3 4 X 4 3 2 1 (1) 1 2 3 4 (2) 2 4 6 8 (3) 3 7 0 2 (4) 4 9 3 6
5
3
3
2
1
1
4
图五 乘法的计算过程
首先看一下单精度数乘高精度数的实现过程,如图六所示:
初始化
进位carry 赋初始值0,结果的位数被乘数的位数。
当前位超过最高位了?
处理当前位和进位
N
Y
还有进位么?
N
结束
处理进位
Y
关于圆周率的计算 祖冲之在数学方面的突出贡献是关于圆周率的计算,确定了相当精确的圆周率值。中国古代最初采用的圆周率是“周三径一”,也就是说,π=3。这个数值与当时文化发达的其他国家所用的圆周率相同。但这个数值非常粗疏,用它计算会造成很大的误差。随着生产和科学的发展,π=3 就越来越不能满足精确计算的要求。因此,中外数学家都开始探索圆周率的算法和推求比较精确的圆周率值。在中国,据公元一世纪初制造的新莽嘉量斛(亦称律嘉量斛,王莽铜斛,是一种圆柱形标准量器,现存)推算,它所取的圆周率是3.1547 。二世纪初,东汉天文学家张衡在《灵宪》中取用π=≈3.1466,又在球体积计算中取用π≈3.1622。三国时东吴天文学家王蕃在浑仪论说中取用π≈3.1556。以上这些圆周率近似值,比起古率“周三径一”,精确度有所提高,其中π= 10还是世界上最早的记录。但这些数值大多是经验结果,并没有可靠的理论依据。 在这方面最先取得突破性进展的是魏晋之际的数学家刘徽,他在《九章算术注》中创立了“割圆术”,为计算圆周率建立起相当严密的理论和完善的算法。他所得到的圆周率值π=3.14 与π==3.1416,都很精确,在当时世界上是很先进的,至今仍在经常使用。继刘徽之后,祖冲之则将圆周率推算到更加精确的程度。据《隋书·律历志》记载,祖冲之确定了π的不足近似值 3.1415926 和过剩近似值 3.1415927,π的真值在这两个近似值之间,即 3.1415926<π<3.1415927 精确到小数 7 位。这是当时世界上最先进的数学成果,直到约一千年后,才为 15 世纪中亚数学家阿尔·卡西(Al—? kash1)和16世纪法国数学家韦达(F.Vièta,1540—1603)所超过。 关于他得到这两个数值的方法,史无明载,一般认为是基于刘徽割圆术。通过现代计算验证,如果按照割圆术计算,要得到小数 7 位准确的圆周率值,必须求出圆内接正12288 边形的边长和 24576边形的面积,这样,就要对9位数进行上百次加减乘除和开方运算,还要选择适当的有效数字,保证准确的误差范围。对于用算筹计算的古代数学家来说,这绝不是一件轻而易举的事情,只有掌握纯熟的理论和技巧,并具备踏踏实实和一丝不苟的研究精神,才能取得这样的杰出成就。祖冲之的这项记录在中国也保持了一千多年。 中国古代数学家和天文学家还往往用分数表示常量的近似值。为此,祖冲之确定了π的两个分数形式的近似值:约率π=22/7≈3.14 ,密率π=355/113 ≈3.1415929。这两个数值都是π的渐近分数。刘宋天文学家何承天及古希腊阿基米德等都已用到过。密率355/113 是π的分母小于10000的最佳近似分数,则为祖冲之首创。关于密率355/113是如何得到的,今人有“调日法”术,连分数法,解同余式或不定方程,割圆术等种种推测,迄今尚无定论。在欧洲,π= 355/113 是16世纪由德国数学家奥托(V.Otto,1550(?)—1605)和荷兰工程师安托尼兹(A.Anthonisz,1527—1607)分别得到,后通称“安托尼兹率”,但这已是祖冲之以后一千多年的事情了。自从我国古代灿烂的科学文化逐渐得到世界公认以来,一些学者就建议把π= 355 称为“祖率”,以纪念祖冲之的杰出贡献。 关于球的体积公式及其证明: 祖冲之的另一项重要数学成就是关于球的体积公式及其证明。各种几何体的体积计算是古代几何学中的基本内容。《九章算术》商功章已经正确地解决了
高精度计算 由于计算机具有运算速度快,计算精度高的特点,许多过去由人来完成的烦琐、复杂的数学计算,现在都可以由计算机来代替。 计算机计算结果的精度,通常要受到计算机硬件环境的限制。例如,pascal 要计算的数字超过19位,计算机将按浮点形式输出;另一方面,计算机又有数的表示范围的限制,在一般的微型计算机上,实数的表示范围为l0-38 -l038。例如,在计算N!时,当N=21时计算结果就超过了这个范围,无法计算了。这是由计算机的硬件性质决定的,但是,我们可以通过程序设计的方法进行高精度计算(多位数计算)。 学习重点 1、掌握高精度加、减、乘、除法。 3、理解高精度除法运算中被除数、除数、商和余数之间的关系。 4、能编写相应的程序,解决生活中高精度问题。 学习过程 一、高精度计算的基本方法 用free pascal程序进行高精度计算,首先要处理好以下几个基本问题:【数据的输入与保存】 (1)一般采用字符串变量存储数据,然后用length函数测量字符串长度确定其位数。 (2)分离各位数位上的数字 分离各数位上的数通常采用正向存储的方法。以“163848192”为例,见下表:A[9] A[8] A[7] A[6] A[5] A[4] A[3] A[2] A[1] 1 6 3 8 4 8 1 9 2 基本原理是A[1]存放个位上的数字,A[2]存放十位上的数字,……依此类推。即下标小的元素存低位上的数字,下标大的元素存高位上的数字,这叫“下标与位权一致”原则。 【计算结果位数的确定】 (1)高精度加法:和的位数为两个加数中较大数的位数+1。 (2)高精度减法:差的位数为被减数和减数中较大数的位数。 (3)高精度乘法:积的位数为两个相乘的数的位数之和。 (4)高精度除法:商的位数按题目的要求确定。 【计算顺序与结果的输出】 高精度加、减、乘法,都是从低位到高位算起,而除法相反。输出结果都是从高位到低位的顺序,注意:高位上的零不输出(整数部分是零除外)。 高精度加法 【参考程序】 var a,b:array[1..10000] of byte; i,w,la,lb:integer;
常用数学公式大全 1、每份数×份数=总数总数÷每份数=份数总数÷份数=每份数 2、1倍数×倍数=几倍数几倍数÷1倍数=倍数几倍数÷倍数=1倍数 3、速度×时间=路程路程÷速度=时间路程÷时间=速度 4、单价×数量=总价总价÷单价=数量总价÷数量=单价 5、工作效率×工作时间=工作总量工作总量÷工作效率=工作时间工作总量÷工作时间=工作效率 6、加数+加数=和和-一个加数=另一个加数 7、被减数-减数=差被减数-差=减数差+减数=被减数 8、因数×因数=积积÷一个因数=另一个因数 9、被除数÷除数=商被除数÷商=除数商×除数=被除数 小学数学图形计算公式 1、正方形C周长S面积a边长周长=边长×4C=4a面积=边长×边长S=a×a 2、正方体V:体积a:棱长表面积=棱长×棱长×6S表=a×a×6体积=棱长×棱长×棱长V=a×a×a 3、长方形 C周长S面积a边长 周长=(长+宽)×2C=2(a+b) 面积=长×宽S=ab 4、长方体 V:体积s:面积a:长b:宽h:高 (1)表面积(长×宽+长×高+宽×高)×2S=2(ab+ah+bh) (2)体积=长×宽×高V=abh 5三角形s面积a底h高 面积=底×高÷2s=ah÷2 三角形高=面积×2÷底 三角形底=面积×2÷高 6平行四边形 s面积a底h高 面积=底×高s=ah 7梯形 s面积a上底b下底h高 面积=(上底+下底)×高÷2s=(a+b)×h÷2 8圆形 S面积C周长∏d=直径r=半径 (1)周长=直径×∏=2×∏×半径C=∏d=2∏r (2)面积=半径×半径×∏ 9圆柱体 v:体积h:高s;底面积r:底面半径c:底面周长 (1)侧面积=底面周长×高 (2)表面积=侧面积+底面积×2 (3)体积=底面积×高 (4)体积=侧面积÷2×半径 10圆锥体 v:体积h:高s;底面积r:底面半径 体积=底面积×高÷3 总数÷总份数=平均数