Big Number-大数运算
来源:互联网 发布:lua和javascript 编辑:程序博客网 时间:2024/04/26 07:27
Big Number
我个人觉得「 Big Number 」这个英文辞汇,听起来一点都不学术,何况,一般来说我们只讨论整数的部分。另外还有一种称呼是「 Big Integer 」,这听起来就正式多了。
言归正传。大数就是很大的数字,大到无法以一个简单的变数型态储存这个值。
一般来说, int 这个变数型态,记忆体大小为32 bit ,可以储存数值范围为-2^31 到2^31 - 1 的整数,大约是1 后面再接九个零;而long long 这个变数型态是64 bit 的,可以储存数值范围为-2^63 到2^63 - 1 的整数。另外还有unsigned 这个关键字,它能让原本的变数型态能够存入更大一点的正整数。
虽然int 、 long long 的数值大小已经够用了,但是人的欲望是无止尽的,总是想让电脑能够处理更大的数字、算得更精准。于是大数的技术就这样产生了。
资料结构
要让电脑存放这么大的数字,有个好方法就是使用阵列。阵列有很多格子,一个格子存一个数字;只要宣告1000 格大小的int 阵列,就可以存1000 位数了!至于一个int 变数,充其量也不过十位数而已──阵列能存放的数值大小,和int 相比之下,实在是多很多很多。
- struct BigNumber
- {
- int array [ 1000 ]; //一个栏位存一个数字,可以存1000位数
- bool sign ; //正负号
- int length ; //位数
- };
通常我们习惯将低位数放在索引值( index )比较小的位置,高位数放在索引值比较大的位置。假设要存放680468975231245 。
每个人对阵列的思考模式不一样,像这里就是由左至右的,另外也有人觉得阵列是由右至左、由上至下、弯弯曲曲的、……。要怎么思考都是可以的,一以贯之就好啰。
阵列右端划上横线的格子,通常我喜欢存0 进去,这样子做运算的时候会比较方便;如果将横线的部分设成-1 ,在运算时会出现点麻烦,所以我不喜欢、也不建议这么做。
大数的各种功能
设计好了资料结构之后,接下来便要开始设计大数的各种功能,例如说显示大数,以及大数的四则运算。
为了让初学者能够清楚了解大数运算的方式,以下的程式码举要治繁,而不修边幅。各位了解箇中道理之后,可以自行添加修改,让程式码更美观。
显示大数
在萤幕上印出大数可以这么做。
- void print ( int a [ 100 ])
- {
- int i = 100 - 1 ; //要印的数字位置
- while ( a [ i ] == 0 ) i --; //数字开头的零都不印
- while ( i >= 0 ) cout << a [ i --];
- }
如果这个大数有可能是零,就得加个几行程式码。
- void print ( int a [ 100 ])
- {
- int i = 100 - 1 ;
- while ( i >= 0 && a [ i ] == 0 ) i --;
- if ( i < 0 )
- cout << '0' ;
- else
- while ( i >= 0 ) cout << a [ i --];
- }
比较大小
比较哪个数字比较大。
- // a > b
- bool largerthan ( int a [ 100 ], int b [ 100 ])
- {
- for ( int i = 100 - 1 ; i >= 0 ; i --) //从高位数开始比,对应的位数相比较。
- if ( a [ i ] != b [ i ]) //发现ab不一样大,马上回传结果。
- return a [ i ] > b [ i ];
- return false ; //完全相等
- }
加法运算
大数的四则运算不会很困难。这里提供大数加法的粗略程式码,希望能一目了然。
- // c = a + b;
- void add ( int a [ 100 ], int b [ 100 ], int c[ 100 ])
- {
- for ( int i = 0 ; i < 100 ; i ++) //对应的位数相加
- c [ i ] = a [ i ] + b [ i ];
- for ( int i = 0 ; i < 100 - 1 ; i ++) //一口气进位
- {
- c [ i + 1 ] += c [ i ] / 10 ; //进位
- c [ i ] %= 10 ; //进位后余下的数
- }
- }
大数的运算有个有趣的地方,就是运算时不用立即进位,可以后来再去一口气进位。这件事情值得细想。
UVa 10035
减法运算
这里继续提供大数减法的粗略程式码。
- void sub ( int a [ 100 ], int b [ 100 ], int c[ 100 ])
- {
- for ( int i = 0 ; i < 100 ; i ++)
- c [ i ] = a [ i ] - b [ i ];
- for ( int i = 0 ; i < 100 - 1 ; i ++) //一口气借位和补位
- if ( c [ i ] < 0 )
- {
- c [ i + 1 ]--; //借位
- c [ i ] += 10 ; //补位
- }
- }
乘法运算
大数乘法的粗略程式码。我一定要强调它是粗略的。
- void mul ( int a [ 100 ], int b [ 100 ], int c[ 100 ])
- {
- for ( int i = 0 ; i < 100 ; i ++)
- c [ i ] = 0 ;
- for ( int i = 0 ; i < 100 ; i ++)
- for ( int j = 0 ; j < 100 ; j ++)
- if ( i + j < 100 )
- c [ i + j ] += a [ i ] * b [ j ];
- for ( int i = 0 ; i < 100 - 1 ; i ++) //一口气进位
- {
- c [ i + 1 ] += c [ i ] / 10 ;
- c [ i ] %= 10 ;
- }
- }
至于大数乘以int 是比较容易的。
- void mul ( int a [ 100 ], int b , int c [ 100])
- {
- for ( int i = 0 ; i < 100 ; i ++)
- c [ i ] = a [ i ] * b ;
- for ( int i = 0 ; i < 100 - 1 ; i ++) //一口气进位
- {
- c [ i + 1 ] += c [ i ] / 10 ;
- c [ i ] %= 10 ;
- }
- }
UVa 338 10106
除法运算
大数除法可直接使用长除法。还满复杂的。程式码就随便写写啰!
- void div ( int a [ 100 ], int b [ 100 ], int c[ 100 ])
- {
- int t [ 100 ];
- for ( int i = 100 - 1 ; i >= 0 ; i --)
- for ( int k = 9 ; k > 0 ; k --) //尝试商数
- {
- mul ( b + i , k , t );
- if ( largerthan ( a + i , t ))
- {
- sub ( a + i , t , c + i );
- break ;
- }
- }
- }
商数范围是零到九,所以必须一一尝试。可以利用高位数相除来估计商数的范围,便不必一一尝试。这里不加说明。
至于大数除以int 是比较容易的。
- void div ( int a [ 100 ], int b , int c [ 100])
- {
- int r = 0 ;
- for ( int i = 100 - 1 ; i >= 0 ; i --)
- {
- r = r * 10 + a [ i ];
- c [ i ] = r / b ;
- r %= b ;
- }
- }
开平方根运算
大数开平方根可利用直式开方法。【待补文字】
UVa 10023
改进资料结构
一个栏位只存一个数字有点浪费。
int 的范围约为十位数字,一个栏位其实能够存入九个位数的。一个栏位可存九个位数,那么1000 格的阵列,便可从原来的1000 位数,摇身一变成为9000 位数;一个栏位可存九个位数,若要表示1000 位数,只需要112 格的阵列就可以了。这个新想法,相当的节省空间,运算次数也会随之减少。
不过,如果一个栏位存了很多位数,会对运算造成什么影响呢?
从最简单的加法、乘法开始思考好了:
首先,进位会受影响。如果一个栏位存了两位数字,那么做进位时,要每到100 才能进位。
第二,进位后会溢位( overflow )吗?进位会让隔壁的栏位增加一些数字。如果隔壁的栏位原本就有一个很大的数字,那么它加上进位的数值之后,会不会产生溢位?
第三,乘法是将某两个栏位相乘,加到另一个栏位上。两个栏位相乘,如果他们各是8 位数,相乘之后至少也有15 位数,这远超过int 的上限了,怎么可能存进一个int 之中呢?
或许还会有很多的问题需要考虑。
虽然问题重重,但是也并不代表一个栏位还是只能存一个数字吧?一个栏位存个两三位,应该不成问题吧?这些问题就留给大家思考,在此不加赘述。
UVa 288 10220 10814 10925 748
GMP
GMP 是一个C/C++ 大数运算函式库,相当实用,读者可上网搜寻之。
- Big Number-大数运算
- 大数big number的加减运算
- 杭电1212 Big Number (大数求模运算)
- 大数加法 big number add
- HDU 1212 Big Number 【大数】
- HDU-1212-Big Number【大数】
- Big Number(hdu1018,大数的位数log)
- hdu1212 Big Number,大数求模
- HDU 1212 Big Number 大数求余数
- hdu 1212 Big Number(大数取模)
- HDU1212:Big Number(大数求模)
- HDOJ 1018 Big Number(大数)
- HDOJ 1212 Big Number (大数求模)
- hdu 1212 Big Number 大数取模
- 杭电1018-Big Number(大数)
- HDU 1212 Big Number 大数取模
- hdu 1212 Big Number(大数取余)
- hdu 1212 Big Number(大数取模)
- 嗓子疼 是感冒的前兆
- 无法打开登录所请求的数据库DbName.登录失败。用户'IIS APPOOL\DefaultAppPool'登录失败的解决方案
- 为Android安装BusyBox —— 完整的bash shell
- 用Python语言显示图片的倒影效果
- Spring事务配置的五种方式
- Big Number-大数运算
- spring的annotation-driven配置事务管理器详解
- 求最大公约数。
- Silverlight中如何动态创建行
- 先思再行 闭着眼睛编程
- VC++2012编程演练数据结构《17》稀疏矩阵
- 理解POCO
- centos 6.3 软件源设置问题
- Beyond Compare as a Diff and Merge tool with Git