首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

(压位)高精度思维教程 与 代码实现

2012-08-15 
(压位)高精度思想教程 与 代码实现高精度这玩意儿真是好久没写了,时隔多年,从 Pascal 转到 C , 代码风也变

(压位)高精度思想教程 与 代码实现

高精度这玩意儿真是好久没写了,时隔多年,从 Pascal 转到 C++ , 代码风格也变了

重新编了一下(经过潘神指导!),觉得比以前写的好多了~


想了想干脆写一个小教程,希望初学者能有所收获~~


一.高精度四则运算思想

高精度这个东西其实谁都会,上过小学数学课就知道高精度怎么做了,就是按照人的运算方式,一位一位运算。

1.加法 

就是 从个位开始 两个数字相加,如果有进位,就加到十位,再算十位相加,……

2.减法 

也是从个位开始 两个数字相减,如果得到的数字小于 0,那么就加上 10,并且把被减数的十位减一,……

3.乘法 

第一个数乘以第二个数的个位,写下来,右边与个位对齐,再与第二个数的十位相乘,右边与十位对齐,……

仔细想一想,会发现其实 第一个数 从右数起的第 i 位,不妨设为 2 乘以 第二个数从右数起的第 j 位,不妨设为 8,就相当于 2*10^(i-1) * 8*10^(j-1)

这个乘积所贡献的就是 答案从右数起的 第 i+j-1 位, 即 16 *10^(i+j-2) ,把 1 进位到 从右数起的第 i+j 位上,留下 6 . 即可。

4.除法 

除法其实是减法的延伸,打个比方,24723 除以 123 。

按照人的做法,从高位开始一位一位取出被除数的数字

先是 2,判断是否小于 123, 小于那答案这一位就置 0

再取一位,24, 还是小于,答案这一位置0

再取一位,247,这时候比 123 大了,那就看能减多少个 123, 发现减 2 次之后比 123 小了,那么答案的第 3 位上就是2, 减剩下的数是 1  

重复那个过程,取一位,12, 小于 123 ,答案这一位置 0

再取一位, 123, 不小于 123 了, 看能减多少个, 发现能减一个, 那么答案第 5 位就是 1 

剩下的数为 0 , 而被除数的数也被取光了,运算结束。

答案为 00201 ,整理一下即为 201。


二、存储方式

实践下来为了方便起见,把最低位存在数组的第一位,最高位存在最后一位,是最容易编写程序的,道理很简单,因为每次都是从最低位开始做的,而且乘法显然会方便很多。

我一般用一个 struct 来表示一个高精度数,里面有个 a 数组, a[0] 为该数的长度,a[1] 是个位,a[2] 是十位,以此类推。


三.压位高精度

本质其实是一样的,只不过存储方式与输出方式有不同。

比如数 103 

用十位的高精度存 a[0] = 3, a[1] = 3, a[2] = 0, a[3] = 1

用百位的高精度存 a[0] = 2, a[1] = 3, a[2] = 1

要注意的是输出的时候 a[1] 不能只输出一个 3, 而要输出 03.


四.代码实现

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int power = 1;      //每次运算的位数为10的power次方,在这里定义为了方便程序实现const int base = 10;      //10的power次方。//要压位的时候,只需改power 和 base即可,如压万位高精,那么power = 4, base = 10000const int MAXL = 1001;    //数组的长度。char a[MAXL], b[MAXL];struct num{    int a[MAXL];    num() { memset(a, 0, sizeof(a)); }                      //初始化    num(char *s)                                            //将一个字符串初始化为高精度数    {        memset(a, 0, sizeof(a));        int len = strlen(s);        a[0] = (len+power-1) / power;                       //数的长度        for (int i=0, t=0, w; i < len ;w *= 10, ++i)                {            if (i % power == 0) { w = 1, ++t; }            a[t] += w * (s[i]-'0');        }        //初始化数组,这里自己模拟一下,应该很容易懂的~    }    void add(int k) { if (k || a[0]) a[ ++a[0] ] = k; }     //在末尾添加一个数,除法的时候要用到    void re() { reverse(a+1, a+a[0]+1); }                   //把数反过来,除法的时候要用到    void print()                                            //打印此高精度数    {        printf("%d", a[ a[0] ]);              //先打印最高位,为了压位 或者 该高精度数为0 考虑        for (int i = a[0]-1;i > 0;--i)        printf("%0*d", power, a[i]);          //这里"%0*d", power的意思是,必须输出power位,不够则前面用0补足        printf("\n");    }} p,q,ans;bool operator < (const num &p, const num &q)              //判断小于关系,除法的时候有用{    if (p.a[0] < q.a[0]) return true;    if (p.a[0] > q.a[0]) return false;    for (int i = p.a[0];i > 0;--i)    {        if (p.a[i] != q.a[i]) return p.a[i] < q.a[i];    }    return false;}num operator + (const num &p, const num &q)               //加法,不用多说了吧,模拟一遍,很容易懂{    num c;    c.a[0] = max(p.a[0], q.a[0]);    for (int i = 1;i <= c.a[0];++i)    {        c.a[i] += p.a[i] + q.a[i];        c.a[i+1] += c.a[i] / base;        c.a[i] %= base;    }    if (c.a[ c.a[0]+1 ]) ++c.a[0];    return c;}num operator - (const num &p, const num &q)               //减法,也不用多说,模拟一遍,很容易懂{    num c = p;    for (int i = 1;i <= c.a[0];++i)    {        c.a[i] -= q.a[i];        if (c.a[i] < 0) { c.a[i] += base; --c.a[i+1]; }    }    while (c.a[0] > 0 && !c.a[ c.a[0] ]) --c.a[0];              //我的习惯是如果该数为0,那么他的长度也是0,方便比较大小和在末尾添加数时的判断。    return c;}num operator * (const num &p, const num &q)                 //乘法,还是模拟一遍。。其实高精度就是模拟人工四则运算!{    num c;    c.a[0] = p.a[0]+q.a[0]-1;    for (int i = 1;i <= p.a[0];++i)    for (int j = 1;j <= q.a[0];++j)    {        c.a[i+j-1] += p.a[i]*q.a[j];        c.a[i+j] += c.a[i+j-1] / base;        c.a[i+j-1] %= base;    }    if (c.a[ c.a[0]+1 ]) ++c.a[0];    return c;}num operator / (const num &p, const num &q)               //除法,这里我稍微讲解一下{    num x, y;    for (int i = p.a[0];i >= 1;--i)                       //从最高位开始取数    {        y.add(p.a[i]);             //把数添到末尾(最低位),这时候是高位在前,低位在后        y.re();                    //把数反过来,变为统一的存储方式:低位在前,高位在后        while ( !(y < q) )         //大于等于除数的时候,如果小于的话,其实答案上的该为就是初始的“0”            y = y - q, ++x.a[i];   //看能减几个除数,减几次,答案上该为就加几次。        y.re();                    //将数反过来,为下一次添数做准备    }    x.a[0] = p.a[0];    while (x.a[0] > 0 && !x.a[x.a[0]]) --x.a[0];    return x;}int main(){    scanf("%s", a);    scanf("%s", b);    reverse(a, a+strlen(a));    reverse(b, b+strlen(b));    p = num(a), q = num(b);    ans = p + q;    ans.print();    ans = p - q;    ans.print();    ans = p * q;    ans.print();    ans = p / q;    ans.print();}


热点排行