大数运算——字符串操作结合“竖式计算“思想的实现

总体原则:

     字符串转整形数组,然后按照“竖式计算”的思想,按位(对于数组来说,就是对应位置的元素)进行运算,同时处理进位、退位。最后将整形数组转换为字符串输出。

Ps:1、字符串转整形,本文采取逆序存储的方式,即将字符串的低位(大数的高位)放置到整形数组的高位。

  2、本文提供的四个四则运算方法,所有的输入值(大数)必须为正整数。

一、加法

  加法运算遵循从低位到高位运算的法则。将字符串转换为整形数组后,两数组对应元素相加,结果存储至结果数组的相应元素位置。同时对相加后的元素进行整除和取余运算(整除结果即进位,取余结果即本位置最终加和结果),将结果存至相应位置。

注意:考虑到高位可能存在进位的情况,用于保存结果的数组大小应比两数最大长度大1。

///////////////////////////////////////////////////////////////////////////////
//
// NAME:  Add
//
// DESCRIPTION:  An operation for two large numbers adding
//
// PARAMETERS:  stNumA/strNumB    - the input numbers
//              strResult         - the result of addtion
//              lenRes            - the length of 'result'
//
// RETURN:        int            - indicates the operation success or failure
//                              0 failure    
//                              1 success                                        
///////////////////////////////////////////////////////////////////////////////

int Add(char* strNumA, char* strNumB, char* strResult, int lenRes)
{
    int lenA = strlen(strNumA);
    int lenB = strlen(strNumB);

    //Check user's input
    if((lenA <= 0 || lenA > MAX_LENGTH) || (lenB <= 0 || lenB > MAX_LENGTH))
        return 0;
    int len = lenA;
    while(len--)
    {
        if(strNumA[len] > 57 || strNumA[len] < 48)
            return 0;
    }
    len = lenB;
    while(len--)
    {
        if(strNumB[len] > 57 || strNumB[len] < 48)
            return 0;    
    }

    int arNumA[MAX_LENGTH] = {0};
    int arNumB[MAX_LENGTH] = {0};
    int arRes[MAX_LENGTH] = {0};

    //Convert string to array
    for(int i = 0; i < lenA; i++)
        arNumA[i] = strNumA[lenA-i-1] - '0';
    for(int i = 0; i < lenB; i++)
        arNumB[i] = strNumB[lenB-i-1] - '0';

    while(arNumA[lenA-1] == 0 && lenA > 1)
        lenA--;

    while(arNumB[lenB-1] == 0 && lenB > 1)
        lenB--;

    //Get the max length of input number
    int lenMax = lenA > lenB ? lenA:lenB;
    lenMax++;

    //Calculate the numbers digit by digit
    for(int i = 0; i < lenMax; i++)
    {
        arRes[i] += arNumA[i] + arNumB[i];
        //Deal with carries
        arRes[i+1] += arRes[i]/10;
        arRes[i] %= 10;
    }

    //Re-calculate the length of result
    while(arRes[lenMax-1] == 0 && lenMax > 1)
        lenMax--;
    //Convert array to string
    if(lenMax+1 > lenRes)
        return 0;
    for(int i = 0; i < lenMax; i++)
    {
        strResult[i] = arRes[lenMax-i-1] + '0';
    }
    strResult[lenMax] = '\0';

    return 1;
}

 

二、减法

  减法运算遵循从低位到高位运算的法则。将字符串转换为整形数组后,两数组对应元素相减,结果存储至结果数组的相应元素位置。同时对相减后的结果进行判断,如果大于0,将结果存至相应位置;如果小于0,结果加10后存入相应位置,同时高位元素置1,表示存在借位。这样高位相减的时候,多减1,相当于借了10。

注意:两数相减之前,需判断大小,要用大的数减去小的数,即减数与被减数交换。如果存在交换,在输出的时候,结果前面需要加”-“号。

///////////////////////////////////////////////////////////////////////////////
//
// NAME:  Sub
//
// DESCRIPTION:  An operation for two large numbers subtracting
//
// PARAMETERS:  stNumA/strNumB    - the input numbers
//              strResult         - the result of subtraction
//              lenRes            - the length of 'result'
//
// RETURN:        int            - indicates the operation success or failure
//                              0 failure    
//                              1 success                                        
///////////////////////////////////////////////////////////////////////////////
int Sub(char* strNumA, char* strNumB, char* strResult, int lenRes)
{
    int lenA = strlen(strNumA);
    int lenB = strlen(strNumB);
    //Check user's input
    if((lenA <= 0 || lenA > MAX_LENGTH) || (lenB <= 0 || lenB > MAX_LENGTH))
        return 0;
    
    int len = lenA;
    while(len--)
    {
        if(strNumA[len] > 57 || strNumA[len] < 48)
            return 0;
    }
    len = lenB;
    while(len--)
    {
        if(strNumB[len] > 57 || strNumB[len] < 48)
            return 0;    
    }

    int arNumA[MAX_LENGTH] = {0};
    int arNumB[MAX_LENGTH] = {0};
    int arRes[MAX_LENGTH] = {0};

    //Convert string to carries
    for(int i = 0; i < lenA; i++)
        arNumA[i] = strNumA[lenA-i-1] - '0';
    for(int i = 0; i < lenB; i++)
        arNumB[i] = strNumB[lenB-i-1] - '0';

    while(arNumA[lenA-1] == 0 && lenA > 1)
        lenA--;

    while(arNumB[lenB-1] == 0 && lenB > 1)
        lenB--;

    int ret = CompareNum(arNumA, lenA, arNumB, lenB);
    int sign = 0;
    
    //Calculate the numbers digit by digit. Three situation should be concerned: A>B;A<B;A=B
    switch(ret)
    {
    case 1:
        sign = 1;
        for(int i = 0; i < lenA; i++)
        {
            int    diff = arNumA[i] - arNumB[i] - arRes[i];
            arRes[i] = diff;
            if(diff >= 0)
                arRes[i] = diff;
            else
            {
                arRes[i] += 10;
                arRes[i+1] = 1;
            }
        }
        break;
    case -1:
        sign = 0;
        for(int i = 0; i < lenB; i++)
        {
            int    diff = arNumB[i] - arNumA[i] - arRes[i];
            
            arRes[i] = diff;
            if(diff < 0)
            {
                arRes[i] += 10;
                arRes[i+1] = 1;
            }
        }
        break;
    case 0:
        sign = 1;
        break;

    }

    int lenMax = lenA>lenB?lenA:lenB;
    //Re-calculate the length of result
    while(arRes[lenMax-1] == 0 && lenMax > 1)
        lenMax--;

    if(lenMax + 2 > lenRes)
        return 0;

    //Convert array to string
    if(sign == 0)
    {
        strResult[0] = '-';
        for(int i = 1; i < lenMax+1; i++)
        {
            strResult[i] = arRes[lenMax-i] + '0';
        }
        strResult[lenMax+1] = '\0';
    }
    else
    {
        for(int i = 0; i < lenMax; i++)
        {
            strResult[i] = arRes[lenMax-i-1] + '0';
        }
        strResult[lenMax] = '\0';
    }

    return 1;
}

///////////////////////////////////////////////////////////////////////////////
//
// NAME:  CompareNum
//
// DESCRIPTION: Compare the two input numbers
//
// PARAMETERS:  arNumA/arNumB    - the input numbers
//                lenA/lenB        - the length of numbers
//
// RETURN:        int            - indicates the result of comparison
//                              1 A > B
//                              -1 A > B
//                              0 A = B
///////////////////////////////////////////////////////////////////////////////

int CompareNum(int* arNumA, int lenA, int* arNumB, int lenB)
{
    if(lenA > lenB)
        return 1;
    else if(lenA < lenB)
        return -1;
    else
    {
        while(lenA > 0)
        {
            if(arNumA[lenA-1] == arNumB[lenA-1])
                lenA--;
            else if(arNumA[lenA-1] > arNumB[lenA-1])
                return 1;
            else
                return -1;
        }
        return 0;
    }
}


三、乘法

  乘法的运算逻辑与”竖式计算“一致,即一个数的每一位分别乘以另一个数的每一位,然后将计算结果按照一定的规律加和,乘法的难点就是在于这个加和的规律。通过观察和总结可发现,A数的第i个元素与B数的第j个元素所乘得的结果存放在结果数组的第i+j个元素中。按照这个规律,乘法的运算可以总结为:按位相乘,结果存入结果数组的对应元素(其中存在累加,所以存入时要使用+=运算),同时与大数加法的逻辑一样,处理进位问题。

///////////////////////////////////////////////////////////////////////////////
//
// NAME:  Mul
//
// DESCRIPTION:  An operation for two large numbers multiplying
//
// PARAMETERS:  strNumA/strNumB    - the input numbers
//                strResult        - the result of multiply
//                lenRes            - the length of 'result'
//
// RETURN:        int            - indicates the operation success or failure
//                              0 failure    
//                              1 success                                        
///////////////////////////////////////////////////////////////////////////////
int Mul(char* strNumA, char* strNumB, char* strResult, int lenRes)
{
    int lenA = strlen(strNumA);
    int lenB = strlen(strNumB);
    //Check user's input
    if(lenA < 0 || lenA > MAX_LENGTH || lenB < 0 || lenB > MAX_LENGTH)
        return 0;

    int len = lenA;
    while(len--)
    {
        if(strNumA[len] > 57 || strNumA[len] <48)
            return 0;
    }
    len = lenB;
    while(len--)
    {
        if(strNumB[len] > 57 || strNumB[len] <48)
            return 0;    
    }

    int arNumA[MAX_LENGTH] = {0};
    int arNumB[MAX_LENGTH] = {0};
    int arRes[MAX_LENGTH*2] = {0};

    //Convert string to array
    for(int i = 0; i < lenA; i++)
        arNumA[i] = strNumA[lenA-1-i] - '0';
    for(int i = 0; i < lenB; i++)
        arNumB[i] = strNumB[lenB-1-i] - '0';

    while(arNumA[lenA-1] == 0 && lenA > 1)
        lenA--;

    while(arNumB[lenB-1] == 0 && lenB > 1)
        lenB--;

    //Calculate digit by digit
    for(int i = 0; i < lenA; i++)
        for(int j = 0; j < lenB; j++)
        {
            arRes[i+j] += arNumA[i] * arNumB[j];
            //Deal with carries
            arRes[i+j+1] += arRes[i+j]/10;
            arRes[i+j] %= 10;
        }
    int lenMax = lenA + lenB;
    //Re-calculate the length of result
    while(arRes[lenMax-1] == 0 && lenMax > 1)
        lenMax--;

    if(lenMax+1 > lenRes)
        return 0;

    //Convert array to string

    for(int i = 0; i < lenMax; i++)
    {
        strResult[i] = arRes[lenMax-i-1] + '0';
    }
    strResult[lenMax] = '\0';

    return 1;
}


四、除法

  除法运算相较前面三种运算,实现起来稍稍麻烦一些,也和”竖式计算“的思想有一些小的出入。大体说来,除法的运算的思想就是用被除数去减除数,减的次数即为商,剩下不够减的,即为余数。但是对于大数运算来说,单纯的循环减法,计算次数,是不符合大数运算要求的(次数,即商,也可能是大数),所以需要一些特殊的处理。具体处理方法为:对于除数大于或等于被除数的情况,结果很容易得出,不啰嗦;对于除数小于被除数的情况,首先将除数扩大n倍,使其与被除数长度一致,然后循环做减法,减的次数×扩大的倍数N,为结果数组第N个元素的值。接下来除数扩大N-1倍,用之前剩下的余数循环做减法,减的次数×扩大的倍数N-1,为结果数组第N-1个元素的值。按此循环,的到最终的商,余下的值则为余数。

举例:258/4 --> 258 - 400 --> 0 余 258 --> 258 - 40 --> 6 余 18 --> 18 - 4 --> 4 余 2

   按照之前的对应原则将商组合,的到结果:商64;余2

///////////////////////////////////////////////////////////////////////////////
//
// NAME:  Div
//
// DESCRIPTION:  An operation for two large numbers dividing
//
// PARAMETERS:  strNumA/strNumB            - the input numbers
//                strResult/strRemainder    - the result of division
//                lenRes/lenRem            - the length of 'result'
//
// RETURN:        int            - indicates the operation success or failure
//                              0 failure    
//                              1 success                                        
///////////////////////////////////////////////////////////////////////////////

int Div(char* strNumA, char* stNumB, char* strResult, int lenRes, char* strRemainder, int lenRem)
{    
    int lenA = strlen(strNumA);
    int lenB = strlen(stNumB);
    //Check user's input
    if(lenA < 0 || lenA > MAX_LENGTH || lenB < 0 || lenB > MAX_LENGTH)
        return -1;

    int len = lenA;
    while(len--)
    {
        if(strNumA[len] > 57 || strNumA[len] <48)
            return -1;
    }
    len = lenB;
    while(len--)
    {
        if(stNumB[len] > 57 || stNumB[len] <48)
            return -1;    
    }


    int arNumA[MAX_LENGTH] = {0};
    int arNumB[MAX_LENGTH] = {0};
    int arRes[MAX_LENGTH] = {0};
    int arRemainder[MAX_LENGTH] = {0};

    //Convert string to array
    for(int i = 0; i < lenA; i++)
        arNumA[i] = strNumA[lenA-1-i] - '0';
    for(int i = 0; i < lenB; i++)
        arNumB[i] = stNumB[lenB-1-i] - '0';

    while(arNumA[lenA-1] == 0 && lenA > 1)
        lenA--;

    while(arNumB[lenB-1] == 0 && lenB > 1)
        lenB--;

    int qlen = lenA;
    int rlen = lenB;
    int ret = CompareNum(arNumA, lenA, arNumB, lenB);
    int times = lenA - lenB;

    switch(ret)
    {
    case 1:
        if(times > 0)
        {
            int i = 0;
            for(i= lenA - 1; i >= times; --i)
                arNumB[i]=arNumB[i-times];            //Move to high position
            for(;i >= 0; --i)
                arNumB[i] = 0;                        //Cover 0 to low positon
            lenB = lenA;    
        }

        for(int i = 0; i <= times; i++)
        {
            while(SubForDiv(arNumA, arNumB + i, lenA, lenB))
            {
                arRes[times-i]++;
            }            
        }
        memcpy(arRemainder, arNumA, rlen* sizeof(int));
        break;
    case -1:
        qlen = 1;
        rlen = lenB;
        memcpy(arRemainder, arNumB, rlen* sizeof(int));
        break;
    case 0:
        qlen = 1;
        rlen =1;
        arRes[0]=1;
        break;
    }

    //Re-calculate the length of result
    while(arRes[qlen-1] == 0 && qlen > 1)
        qlen--;
    while(arRemainder[rlen-1] == 0 && rlen > 1)
        rlen--;

    if(qlen+1 > lenRes || rlen+1 > lenRem)
        return -1;

    //Convert array to string
    for(int i = 0; i < qlen; i++)
        strResult[i] = arRes[qlen-i-1] + '0'; 
    strResult[qlen] = '\0';

    for(int i = 0; i < rlen; i++)
        strRemainder[i] = arRemainder[rlen-i-1] + '0';
    strRemainder[rlen] = '\0';

    return 1;
    
}

///////////////////////////////////////////////////////////////////////////////
//
// NAME:  SubForDiv
//
// DESCRIPTION:  An operation for two large numbers subtracting,the result assign to arNumA.
//
// PARAMETERS:  arNumA/arNumB            - the input numbers
//                lenA/lenB                - the length of the two numbers
//
// RETURN:        int            - indicates the operation success or failure
//                              0 failure    
//                              1 success                                        
///////////////////////////////////////////////////////////////////////////////
int SubForDiv(int* arNumA, int* arNumB, int lenA, int lenB)
{

    while(arNumA[lenA-1] == 0 && lenA > 1)
        lenA--;

    while(arNumB[lenB-1] == 0 && lenB > 1)
        lenB--;

    int ret = CompareNum(arNumA, lenA, arNumB, lenB);
    int res = 0;
    int arRes[MAX_LENGTH] = {0};
    //Calculate the numbers digit by digit. Three situation should be concerned: A>B;A<B;A=B
    switch(ret)
    {
    case 1:
        for(int i = 0; i < lenA; i++)
        {
            int    diff = arNumA[i] - arNumB[i] - arRes[i];
            arRes[i] = diff;
            if(diff >= 0)
                arRes[i] = diff;
            else
            {
                arRes[i] += 10;
                arRes[i+1] = 1;
            }
        }
        memcpy(arNumA, arRes, lenA*sizeof(int));
        res = 1;
        break;
    case -1:
        res = 0;
        break;
    case 0:
        memset(arNumA, 0, lenA * sizeof(int));
        res = 1;
        break;
    }
    return res;
}

///////////////////////////////////////////////////////////////////////////////
//
// NAME:  CompareNum
//
// DESCRIPTION: Compare the two input numbers
//
// PARAMETERS:  arNumA/arNumB    - the input numbers
//                lenA/lenB        - the length of numbers
//
// RETURN:        int            - indicates the result of comparison
//                              1 A > B
//                              -1 A > B
//                              0 A = B
///////////////////////////////////////////////////////////////////////////////

int CompareNum(int* arNumA, int lenA, int* arNumB, int lenB)
{
    if(lenA > lenB)
        return 1;
    else if(lenA < lenB)
        return -1;
    else
    {
        while(lenA > 0)
        {
            if(arNumA[lenA-1] == arNumB[lenA-1])
                lenA--;
            else if(arNumA[lenA-1] > arNumB[lenA-1])
                return 1;
            else
                return -1;
        }
        return 0;
    }
}

 

结语:

  以上大数的四则运算是我看过的一些其他人的实现,然后根据自己的理解总结的。写的看上去比较冗长,是因为我加了一些输入验证的判断。此外有些代码可以提炼出函数,但是为了看起来方便,逻辑连贯,我没有将他们单独提出来。所有函数我都经过简单的测试,至少没有语法错误~。如果大家有什么更好的建议或者发现什么问题,欢迎随时联系,一起交流,共同进步~

  

 

  


  

posted on 2015-03-30 16:46  MoZhao  阅读(511)  评论(0编辑  收藏  举报

导航