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

总体原则:

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

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;
    }
}

结语:

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

  

  

  

时间: 2024-10-17 02:50:13

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

(转)大数运算(4)——大数乘法

转自:http://blog.csdn.net/lisp1995/article/details/52316466 首先说一下乘法计算的算法:同样是模拟人工计算时的方法. 从低位向高位乘,在竖式计算中,我们是将乘数第一位与被乘数的每一位相乘,记录结果之后,用第二位相乘,记录结果并且左移一位,以此类推,直到计算完最后一位,再将各项结果相加,得出最后结果. 计算的过程基本上和小学生列竖式做乘法相同.为编程方便,并不急于处理进位,而将进位问题留待最后统一处理. 我们以125*53为例来说明计算过程:

bash脚本编程之字符串操作

字符串操作之一:测试变量存在性 ${varname:-word} 如果varname存在且非null,返回其值,否则返回word ${varname:=word} 如果varname存在且非null,返回其值,否则将其设置为word,然后返回其值 ${varname:?message} 如果varname存在且非null,返回其值,否则打印varname:后跟message,并退出当前命令或脚本 ${varname:+word} 如果varname存在且非null,返回word,否则返回null

大数运算之字符串模拟

相信大家被特别大的两个数据做运算折磨过.当两个操作数或者运算结果超过类型的表示范围后会有意想不到的错误,这时候我们的电脑还不如我们高中用过的科学计算器,这是作为一个程序员所不能忍受的.所以我们得找到其他的方式来计算.这就是我们今天要讨论的字符串模拟大数运算. 我们的运算一般使用int类型来算的,那么首先我们先复习一下各种int类型的数据表示范围: unsigned int 0-4294967295    int   -2147483648-2147483647  unsigned long 0-

4.三元运算/集合类型/字符串操作

三元运算:进制:元组集合语法:{}主要作用:集合的操作符集合的使用方法集合的增删改查字符串操作方法:字符编码穿插一个数据类型----bytes小结(抄袭自alex)文件操作基本操作关键参数: 三元运算: 可以将条件语句看成简化结构: 结果标识 = a / b '/'是针对a的一个判断比较,成立则输出a,使结果标识赋值为a b也需要是一个值,当'/'这个判断不成立的时候,输出. 但是b也同时可以是另外一个嵌套的三元运算,因为三元运算的结果一定是一个值 >>> a = 3 >>&

数式计算(递归解法)

1 /** 2 *注:1.有一个bug(以及未知什么bug) 我已知的是: ([email protected])(ps:@为运算符),这种带括号的表达式不能单独的出现,否则异常退出,,但是只要([email protected])@n 3 4 带括号的表达式出现任意+,-,*,/的运算都能进行正常运算(前面 [email protected]([email protected])也不可以)...不知道为什么()表达式后面没有操作时会异常退出. 5 *不知道如何解决.希望感兴趣的人帮帮忙! 6

C语言字符串操作总结大全(超详细)

1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度字符串 strlen(p) 取字符串长度 strcmp(p, p1) 比较字符串 strcasecmp忽略大小写比较字符串strncmp(p, p1, n) 比较指定长度字符串 strchr(p, c) 在字符串中查找指定字符 strrchr(p, c) 在字符串中反向查找 strstr(p, p1

面试之C语言字符串操作总结大全(转载)

趁着十一就好好补补数据结构吧,通信这个不软不硬的专业,现在还是得好好学学补习补习,,你这个非211的本科生!虽然拿到了一个offer,但是觉得时间还有,得继续拼一拼,希望不辜负! 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, p1) 附加字符串 strncat(p, p1, n) 附加指定长度字符串 strlen(p) 取字符串长度 strcmp(p, p1) 比较字符串 strcasecmp忽略大小写比较字符

c++概念字符串操作

转载自:http://www.jb51.net/article/37410.htm 一.char_traits 字符特征类 1)意义:包装特定串元素的通用行为界面,以便容器实现时依据特征信息而执行特定行为 2)定义了通用类型名 typedef _Elem char_type; typedef int int_type; typedef streampos pos_type; typedef streamoff off_type; typedef mbstate_t state_type; 其中

(三)Oracle字符串操作

1. Oracle字符串操作 1.1. 字符串函数 1.1.1. CONCAT和"||" CONCAT是字符串连接函数,语法是:CONCAT(char1, char2) 用于返回两个字符串连接后的结果,两个参数char1.char2是要连接的两个字符串.concat只能有两个参数,所以如果连接三个字符串时,需要两个concat函数.比如连接emp表中的name列和salary列,中间用":"隔开: SELECT CONCAT(CONCAT(name, ' : '),