开发者

详解C/C++高精度算法的简单实现

开发者 https://www.devze.com 2022-12-16 10:33 出处:网络 作者: CodeOfCC
目录前言一、基本原理二、辅助方法1、字符串转高精度2、整型转高精度3、比较4、打印三、算法实现1、加法2、减法3、乘法4、除法四、使用示例1、加法2、减法3、乘法4、除法总结前言
目录
  • 前言
  • 一、基本原理
  • 二、辅助方法
    • 1、字符串转高精度
    • 2、整型转高精度
    • 3、比较
    • 4、打印
  • 三、算法实现
    • 1、加法
    • 2、减法
    • 3、乘法
    • 4、除法
  • 四、使用示例
    • 1、加法
    • 2、减法
    • 3、乘法
    • 4、除法
  • 总结

    前言

    由于上一章《C/C++ 高精度(加减乘除)算法实现》是基于工程项目考虑实现的,也做了一定的优化,实现过程较为复杂。不利于移植和使用,且比较难以理解,时间一长代码也容易忘记,所以重新编写了一个简化的版本,方便以后需要时拷贝使用。

    一、基本原理

    1、存储方式

    采用数字记录高精度数字,数组的第一个元素存储数据长度,比如记录数字为1024示例如下:

    详解C/C++高精度算法的简单实现

    2、计算方式

    采用模拟立竖式计算,比如加法的计算流程,如下图所示1024+9000:

    详解C/C++高精度算法的简单实现

    这里只给出加法的计算说明,其他的以此类推,减法与加法基本一致。乘法和除法略有不同,通过示例图表示也复杂,还不如通过代码去理解,本质的方法就是模拟笔算的立竖式计算。

    二、辅助方法

    1、字符串转高精度

    长度记录在数组第一个元素中

    /// <summary>
    /// 通过字符串初始化
    /// </summary>
    /// <param name="a">[in]高精度数组</param>
    /// <param name="value">[in]字符串首地址</param>
    static void loadStr(int* a,const char* value)
    {
        //记录长度
        a[0] 编程客栈= strlen(value);
        for (int i = 1; i <= a[0]; i++)
            a[i] = value[a[0] - i] - '0';
    }
    

    2、整型转高精度

    /// <summary>
    /// 通过无符号整型初始化
    /// </summary>
    /// <param name="a">[in]高精度数组</param>
    /// <param name="value">[in]整型值</param>
    static void loadInt(int* a, uint64_t value)
    {
        for (size_t i = 1; i < 8096; i++)
        {
            a[i] = value % 10;
            value /= 10;
            if (!value)
            {   
                //记录长度
                a[0] = i;
                return;
            }
        }
    }

    3、比较

    /// <summary>
    /// 比较两个高精度数的大小
    /// </summary>
    /// <param name="a">[in]第一个数</param>
    /// <param name="b">[in]第二个数</param>
    /// <returns>1是a>b,0是a==b,-1是a<b</returns>
    static int compare(int* a, int* b)
    {
        if (a[0] > b[0])return 1;
        if (a[0] < b[0])return -1;
        for (int i = a[0]; i > 0; i--)
            if (a[i] > b[i])return 1;
            else if (a[i] < b[i])return -1;
        return 0;
    }
    

    4、打印

    /// <summary>
    /// 打印输出结果
    /// </summary>
    static void print(int* a) {
        if (!a[0])
            printf("0");
        for (int i = a[0]; i > 0; i--)
            printf("%d", a[i]);
    }
    

    三、算法实现

    原理就不做具体介绍了,四种计算的核心都是模拟立竖式计算。

    1、加法

    为了保证代码相对简单,当b长度较小时可能会做一些多余的计算,不影响结果。

    /// <summary>
    /// 加法(累加)
    ///结果会保存在a中
    /// </summary>
    /// <param name="a">[in]被加数</param>
    /// <param name="b">[in]加数</param>
    static    void acc(int* a, int* b)
    {
        int len = a[0] > b[0] ? a[0] : b[0];
        memset(a + a[0] + 1, 0, (len - a[0] + 1) * sizeof(int));
        memset(b + b[0] + 1, 0, (len - b[0] + 1) * sizeof(int));
        for (int i = 1; i <= len; i++) {
            int temp = a[i] + b[i];
            a[i] = temp % 10;
            a[i + 1] += temp / 10;
        }
        if (a[len + 1])a[0]++;
    }

    2、减法

    /// <summary>
    /// 减法(累减)
    ///结果会保存在a中
    /// </summary>
    /// <param name="a">[in]被减数,被减数必须大于等于减数</param>
    /// <param name="b">[in]减数</param>
    static    void subc(int* a, int* b) {
        memset(b + b[0] + 1, 0, (a[0] - b[0]) * sizeof(int));
        for (int i = 1; i <= a[0]; i++)
        {
            int temp = a[i] - b[i];
            a[i] = temp;
            if (temp < 0)
            {
                //借位
                a[i + 1] -= 1;
                a[i] += 10;
            }
        }
        //记录长度
        for (int i = a[0]; i > 0; i--)
            if (a[i])
            {
                a[0] = i;
                return;
            }
        a[0] = 0;
    }

    3、乘法

    /// <summary>
    /// 乘法
    /// </summary>
    /// <param name="a">[in]被乘数</param>
    /// <param name="b">[in]乘数</param>
    /// <param name="c">[out]结果开发者_Go培训,数组长度必须大于等于aLen+bLen+1</param>
    static    void mul(int* a, int* b, int c[]) {
        c[a[0] + b[0]] = 0;
        memset(c, 0, sizeof(int) * (a[0] + b[0] + 1));
        for (int i = 1; i <= a[0]; i++)
        {
            int j;
            int d = 0;
            //被乘数的一位去乘以乘数的每一位
            for (j = 1; j <= b[0]; j++)
            {
                int temp = a[i] * b[j] + c[j + i - 1] + d;
                c[j + i - 1] = temp % 10;
                d = temp / 10;
            }
            if (d)
            {
                c[j + i - 1] = d;
            }
        }
        //记录长度
        for (int i = a[0] + b[0]; i > 0; i--)
            if (c[i])
            {
                c[0] = i;
                return;
            }
    }

    4、除法

    采用了升阶+减法实现

    /// <summary>
    /// 除法
    /// 依赖减法subc
    /// </summary>
    /// <param name="a">[in]被除数,被除数必须大于除数</param>
    /// <param name="b">[in]除数</param>
    /// <param name="c">[out]商,数组长度大于等于aLen-bLen+1</param>
    /// <param name="mod">[out]余数,数组长度大于等于aLen</param>>
    /// <param name="temp">[in]临时缓冲区,由外部提供以提高性能,数组长度大于等于aLen-bLen+1</param>
    static void divi(int* a, int* b, int* c, int* mod, int* temp) {
        //相差的阶数
        int digit = a[0] - b[0] + 1;
        memcpy(mod, a, (a[0] + 1) * sizeof(int));
        memset(c, 0, sizeof(int) * (digit + 1));
        memset(temp, 0, sizeof(int) * digit);
        androidwhile (digit)
        {
            //升阶        
            memcpy(temp + digit, b + 1, sizeof(int) * b[0]);
            temp[0] = b[0] + digit - 1;
            //减法
            while (compare(mod, temp) != -1)
            {
                subc(mod, temp);
                c[digit]++;
            }
            digit--;
        }
        //记录长度
        for (int i = a[0] - b[0] + 1; i > 0; i--)
            if (c[i])
            {
                c[0] = i;
                return;
            }
    }

    四、使用示例

    1、加法

    计算累加

    int main() {
        int64_t n;
        int num[1024];
        int num2[1024];
        std::cin >> n;OITJfr
        loadInt(num, 0);
        for (int64_t i = 1; i <= n; i++)
        {
            loadInt(num2, i);
            acc(num, num2);
        }
        print(num);
        return 0;
    }
    

    结果:

    详解C/C++高精度算法的简单实现

    2、减法

    两个任意n位数的减法,数字1大于数字2。

    intandroid main()
    {
        int a1[8096], a2[8096];
        std::string s1, s2;
        std::cin >> s1 >> s2;
        loadStr(a1, s1.c_str());
        loadStr(a2, s2.c_str());
        subc(a1, a2);
        print(a1);
        return 0;
    }

    结果:

    #数字1

    752425289999999999999652142141414141414146666676667677682324000001302461646520

    #数字2

    587891851201874512000000000154515100202121555555555555555555555545477910232111

    #计算结果

    164533438798125487999652141986899041212025111121112122126768444455824551414409

    3、乘法

    计算阶乘

    int main() {
        int64_t n;
        int num[8192];
        int num2[8192];
        int num3[8192];
        int* p1 = num;
        int* p2 = num3;
        std::cin >> n;
        loadInt(num, 1);
        for (int64_t i = 1; i <= n; i++)
        {
            loadInt(num2, i);
            mul(p1, num2, p2);
            int* temp = p1;
            p1 = p2;
            p2 = temp;
        }
        print(p1);
        return 0;
    }

    结果:

    #阶乘数

    1000

    #计算结果

    4023872600770937735437024339230039857193748642107146325437999104299385123986290205920442084869694048004799886101971960586316668729948085589013238296699445909974245040870737599188236277271887325197795059509952761208749754624970436014182780946464962910563938874378864873371191810458257836478499770124766328898359557354325131853239584630755574091142624174743493475534286465766116677973966688202912073791438537195882498081268678383745597317461360853795345242215865932019280908782973084313928444032812315586110369768013573042161687476096758713483120254785893207671691324484262361314125087802080002616831510273418279777047846358681701643650241536913982812648102130927612448963599287051149649754199093422215668325720808213331861168115536158365469840467089756029009505376164758477284218896796462449451607653534081989013854424879849599533191017233555566021394503997362807501378376153071277619268490343526252000158885351473316117021039681759215109077880193931781141945452572238655414610628921879602238389714760885062768629671466746975629112340824392081601537808898编程客栈93964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

    4、除法

    给定两个非负整数A,B,请你计算 A / B的商和余数。

    int main()
    {
        int a1[8096], a2[8096], c[8096], mod[8096], temp[8096];
        std::string s1, s2;
        std::cin >> s1 >> s2;
        loadStr(a1, s1.c_str());
        loadStr(a2, s2.c_str());
        divi(a1, a2, c, mod, temp);
        print(c);
        std::cout << std::endl;
        print(mod);
        return 0;
    }
    

    结果:

    #被除数

    12458848948151231366666666666666665454545123156415641561231561213648

    #除数

    88484851521548496564154848456486789

    #商

    140802055198308817458997123299946

    #余数

    25178368711335236611547594127800254

    总结

    以上就是今天要讲的内容,本文提供的是较为简化的实现,且每个方法基本是独立的,可单独拿来使用,用法也比较简单,由于采用数组第一个元素存储长度,接口就变得很简洁,使用起来也方便了很多。

    到此这篇关于详解C/C++高精度算法的简单实现的文章就介绍到这了,更多相关C/C++高精度算法内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号