大整数类的实现(4)(原创)

 5.Helper.h

————————————————————————————————————

/*
 * Copyright (c) 2005 by DoZerg.  ALL RIGHTS RESERVED.
 * Consult your license regarding permissions and restrictions.
 */
#pragma once
#include<string>

namespace DoZerg{
    namespace StringHelper{
        int CheckBaseFromString(const std::string & value){
            int base = 0;
            for(std::string::const_iterator v = value.begin();v != value.end() && base != -1; ++v)
                switch(base){
                    case 0:
                        base = ('+'==*v || '-'==*v)?1:('0'==*v)?2:(*v>'0' && *v<='9')?10:-1;
                        break;
                    case 1:
                        base = ('0'==*v)?2:(*v>'0' && *v<='9')?10:-1;            //only '+' or '-'
                        break;
                    case 2:
                        base = ('x'==*v || 'X'==*v)?3:(*v>='0' && *v<'8')?8:-1;    //just 0
                        break;
                    case 3:
                        base = ((*v>='0' && *v<='9')||(*v>='a' && *v<='f')||(*v>='A' && *v<='F'))?16:-1;    //0x
                        break;
                    case 8:
                        base = (*v>='0' && *v<'8')?8:-1;
                        break;
                    case 10:
                        base = (*v>='0' && *v<='9')?10:-1;
                        break;
                    case 16:
                        base = ((*v>='0' && *v<='9')||(*v>='a' && *v<='f')||(*v>='A' && *v<='F'))?16:-1;
                        break;
                    default:;
                }
            return base;
        }
        bool DivideStringByTwo(std::string & value,bool & IsZero){        //return the highest bit,then divide the string  by 2,in reverse order
            bool carry(false);
            for(std::string::reverse_iterator v=value.rbegin();v!=value.rend();++v){
                if(carry)
                    *v+=10;
                carry=*v&1;
                *v>>=1;
            }
            std::string::size_type p(value.find_last_not_of(char(0))+1);
            IsZero=!p;
            value.erase(p);
            return carry;
        }
        void MutiStringWithTwo(std::string & value,int AddNumber){        //multiple the string,then add the number(0 to 9),in reverse order
            int carry(AddNumber);
            for(std::string::iterator v=value.begin();v!=value.end();++v){
                *v=(*v<<1)+carry;
                if(carry=*v/10)
                    *v%=10;
            }
            if(carry)
                value.push_back(carry);        //this requires 0<=carry<=9
        }
        std::string & ReverseString(std::string & value,int AddValue = 0){        //reverse the string,and add AddValue to all elements
            std::string::size_type l=value.length();
            if(l&1)
                value[l>>1]+=AddValue;
            for(std::string::size_type i=0;i<(l>>1);++i){       
                value[i]^=value[l-i-1];
                value[l-i-1]^=value[i];
                value[i]^=value[l-i-1];
                value[i]+=AddValue;
                value[l-i-1]+=AddValue;
            }
            return value;
        }
    }//namespace StringHelper
}//namespace DoZerg

————————————————————————————————————

这个文件内的函数完成2个重要的任务:把10进制数字字符串转换成2进制比特位,把2进制比特位转换成10进制数字字符串。

首先是CheckBaseFromString。它接受一个8进制的、10进制的或16进制表示的数字字符串,按照C语言标准,8进制数字以'0'开头,后接小于8的数字序列;16进制数字以"0x"或"0X"开头,后接'0'-'9,'a'-'f','A'-'F'的字符序列。数字前面允许一个'-'或'+'表示数值的符号。
CheckBaseFromString按照这个标准来判断一个字符串是否是正确的数字表示,并得到它的基数(8,10,16)。它的实现原理则使用了有限状态机原理,每本《编译原理》都会讲到的东西。具体的流程我就不想多讲了,只要用一个例子走一遍就清楚了。
CheckBaseFromString的返回值中,只有8,10,16是表示数值的真正基数,返回2表示数值为0,其他的数值(0,1,3,-1)都表示字符串不是正确的数字表示。

然后我想介绍排在最后的函数ReverseString。这个函数从名字就可以看出来,它反转传入的字符串参数value,并返回value的引用。不过值得一提的是它的第二个参数AddValue,是需要加到value的每一个字符ASCII码上去的。
具体怎么样呢,看看代码会更清楚。

std::string::size_type l=value.length();

得到value的长度l。

if(l&1)
    value[l>>1]+=AddValue;

如果l是奇数,那么把中间的那个字符ASCII码加AddValue。比如l=5,那么就先把value[2]加AddValue,显然在后面的交换过程中,是不会处理到value[2]的,所以这里先单独处理了。
然后的循环就是首尾交换字符了,

value[i]^=value[l-i-1];
value[l-i-1]^=value[i];
value[i]^=value[l-i-1];

这3行代码交换value[i]和value[l-i-1]的值,如果你不懂怎么做的,那么看看《离散数学》吧。
下面2行代码则给每个字符加AddValue。
这个函数其实非常简单,只要是稍微有经验的coder,一眼就能看出来。所以对不起,我啰嗦了!

好了,该到精彩部分了。
下面我讲DivideStringByTwo。它接受2个参数:
    std::string & value,
    bool & IsZero
第一个参数value是一个表示10进制数字的字符串,但是不是简单的"123"。如果value表示的是123,那么它包含的3个字符是

char[3] = {3,2,1} = {'3' - '0','2' - '0','1' - '0'};

是不是看到了ReverseString的影子?不错,从"123"到上面的形式的转换正是ReverseString的工作,当然它也负责相反方向的转换。
那么究竟为什么用这种形式来表示数字123呢?让我们温习一下10进制数转2进制数的过程。
比如数字123,它转化为2进制是(1111011),计算过程如下:

123 / 2 = 61 余 1
61 / 2 = 30  余 1
30 / 2 = 15  余 0
15 / 2 = 7   余 1
7 / 2 = 3    余 1
3 / 2 = 1    余 1
1 / 2 = 0    余 1
0            结束

于是我们把余数从下往上排列,就是(1111011)。
好了,现在我可以描述DivideStringByTwo的具体作用了,它执行上面的一次除法和求余的工作。
同样用上面的例子,假定输入的字符串value为{3,2,1},代码:

for(std::string::reverse_iterator v=value.rbegin();v!=value.rend();++v){
    if(carry)
        *v+=10;
    carry=*v&1;
    *v>>=1;
}

对value执行除以2的操作,于是value变成了{1,6,0}。代码:

std::string::size_type p(value.find_last_not_of(char(0))+1);
IsZero=!p;
value.erase(p);

得到最后一个非0字符(6)的下一个位置(0),并移除,为什么?显然{1,6,0}实际上表示的61,那么应该用{1,6}就够了,所以这样做的原因就是紧缩value的长度,显然DivideStringByTwo的时间复杂度是与value长度有关的。
于是这里又可以说明另一个设计决定——把123倒置成{3,2,1}来表示——的好处了:每次只需要在末尾erase元素。
IsZero虽然是作为一个参数传进来了,实际上的作用却是返回value是否已经成了0,这一点通过!p来判断。因为std::string::npos通常都是-1,所以如果“最后一个非0字符”不存在,p的值会是0。
最后就是函数的返回值。如果你看懂了上面除以2的代码,就应该知道此时carry的值表示了最后是否有余数(1为true,0为false),所以整个函数的返回值也就表示了“Divide String By Two”之后的余数是多少。
在把10进制数字字符串转化成2进制比特位的时候,DivideStringByTwo是最关键的调用函数之一。

最后我们来看函数MutiStringWithTwo。它的作用与DivideStringByTwo正好相反,它把字符串表示的数字乘以2。为了知道它怎么工作的,我们再来温习一下2进制数怎么转化成10进制数。
以上面的2进制序列(1111011)为例,转化过程如下:

1111011
 111011 结果 1
  11011 结果 1 + 1 * 2 = 3
   1011 结果 1 + 3 * 2 = 7
    011 结果 1 + 7 * 2 = 15
     11 结果 0 + 15 * 2 = 30
      1 结果 1 + 30 * 2 = 61
        结果 1 + 61 * 2 = 123

上面的过程非常清晰,每次把10进制数乘以2,加上2进制序列的最高位。MutiStringWithTwo完成的就是每一步乘2加X的操作,其中AddNumber是需要加的数值。
代码我想不用讲解了,如果弄懂了上面的过程,看代码就像看小说一样容易。
同样在这个过程中,采用{3,2,1}的方式表示123是有优势的,最高位在后面,进位的时候只要push_back就行了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值