• Stanford Algorithms(一): 大数相乘(c++版)


    Stanford Algorithms(一): 大数相乘(c++版)

    刚不就在中国大学Mooc上参加了陈越老师的数据结构的课程,收获很大.觉得趁热打铁,也把算法的部分也给一块学了吧,就在Coursera上注册了一个斯坦福大学的算法课,课程的量很重,估计要学一个学期吧,慢慢的学,稳扎稳打.

    课程里推荐了很多书,我找了一本, 书名就叫Algorithms,作者是S.Dasgupta教授,简单翻看了一下,觉得写的挺不错,就姑且把这本书当做教材了.

    还是那句话,贵精不贵多,一门学深入了,收获就会很大,总之:

    不要做一个浮躁的人.

    第一节课就出了一个很有意思的题:两个数相乘.

    也许会有人问,这有什么难,程序里直接就能算,但是题目出的是两个64个数字相乘,这就有意思了.

    这个计算两个数字相乘的算法,视频里介绍过了,就是分治的思想,递归的调用,把原本的O(n^2)的问题变成了O(logn),效率无以提升很多.

    我到网上一查,发现这是个很出名的,叫做大数相乘的算法问题,参考了一些文章,但感觉写的都不是很详细.

    我是用C++实现的.先看看思路吧:

    1.数字太多,int肯定不行,要用string

    2.具体的算法已经有了,实现的困难,在于实现两个string数字之间的加,减,以及乘法.

    要用的函数大概是这些:

    string multiply(string x, string y);
    string simplyMultiply(string x, string y);
    int string2int(string x);
    string int2string(int x);
    string add(string x, string y);
    string Minus(string x, string y);
    string addZero(string x, int zeroNum);
    string addPreZero(string x, int zeroNum);
    string reverseString(string s);
    int Max(int x, int y);
    

    其中有三个函数比较简单:

    int Max(int x, int y){
        /*
         * Description: find max number
         * Input: Two integers
         * Output: Return max between x and y
         */
        return x > y ? x : y;
    }
    
    int string2int(string x){
        /*
         * Description: Change string to int
         * Input: A string 
         * Output: Return a integer represents origin string
         */
        int n = x.length();
        int s = 0;
        for(int i = 0; i < n; ++i){
            s = 10 * s + x[i] - '0';
        }
        return s;
    }
    
    string int2string(int x){
        /*
         * Description: Change int to string
         * Input: An integers 
         * Output: Return a string represents origin integers
         */
        string result;
        stringstream stream;
        stream << x;
        stream >> result;
        return result;
    }
    

    这里借助了stringstream,可以轻松实现类型之间的转换,当然是涉及到string的.

    两个string的加和减,考虑平时手算的方式,是尾对齐的,因此用程序实现的话,先把它们倒转,变成头对齐,就方便计算了.

    string simplyMultiply(string x, string y){
        /*
         * Description: multiply two string, whose length = 1
         * Input: Two string
         * Output: Return product
         */
        if(x.empty() | y.empty()){
            return int2string(0);
        }else{
            int result = string2int(x) * string2int(y);
            return int2string(result);
        }
    }
    
    string reverseString(string s){
        /*
         * Description: Reverse the string
         * Input: A string
         * Output: Return a reversed string
         */
        string result;
        for(auto temp = s.end() - 1; temp >= s.begin(); --temp){
            result.push_back(*temp);
        }
        return result;
    }
    
    

    还有两个额外的操作,就是在string前面和后面添加0,在前面添加0是为了让两个string的位数相等,因为这个算法处理的是两个等长string,因此要补位,不然会出问题;后面加0,是要用到与10^n相乘这种情况.

    string addZero(string x, int zeroNum){
        /*
         * Description: Add zero between a string, simulate x * 10^n
         * Input: A string, a integer represents zero's number after it
         * Output: Return a string, which is added n's 0
         */
        string temp(zeroNum, '0');
        x.append(temp);
        return x;
    }
    
    string addPreZero(string x, int zeroNum){
        /*
         * Description: Add zero before a string to fill in empty place
         * Input: A string, a integer represents zero's number
         * Output: Return a string, which is added n's 0 before it
         */
        string temp(zeroNum, '0');
        temp.append(x);
        return temp;
    }
    

    比较精彩的是模拟两个string加减的操作.有了前面几个方法做铺垫,实现起来就不困难了.其中,

    Add操作模仿的是到10进1

    Minus操作模仿的是减时不够高位来补

    细节一定要注意,否则bug很难看出来.

    string add(string x, string y){
        /*
         * Description: Add two string
         * Input: Two strings
         * Output: Return their sum
         */
        int i, more = 0, tempSum = 0;
        x = reverseString(x);
        y = reverseString(y);
        int maxSize = Max(x.size(), y.size());
        string s(maxSize + 1, '0');
        for(i = 0; i < x.size() && i < y.size(); ++i){
            tempSum = x[i] - '0' + y[i] - '0' + more;
            s[i] = tempSum % 10 + '0';
            more = tempSum / 10;
        }
        if(i != y.size()){
            for(; i < y.size(); ++i){
                tempSum = y[i] - '0' + more;
                s[i] = tempSum % 10 + '0';
                more = tempSum / 10;
            }
        }else if(i != x.size()){
            for(; i < x.size(); ++i){
                tempSum = x[i] - '0' + more;
                s[i] = tempSum % 10 + '0';
                more = tempSum / 10;
            }
        }
        if(more != 0){
            s[i] += more;
        }else{
            s.pop_back();
        }
        s = reverseString(s);
        return s;
    }
    
    string Minus(string x, string y){
        /*
         * Description: Minus between strings
         * Input: Two strings
         * Output: Return their difference
         */
        int i;
        x = reverseString(x);
        y = reverseString(y);
        string s(x.size(), '0');
        for(i = 0; i < y.size(); ++i){
            if(x[i] < y[i]){
                x[i] += 10;
                x[i + 1] -= 1;
            }
            s[i] = x[i] - y[i] + '0';
        }
        for(; i < x.size(); ++i){
            s[i] = x[i];
        }
        for(i = x.size() - 1; i > 0; --i){
            if(s[i] == '0'){
                s.pop_back();
            }else{
                break;
            }
        }
        s = reverseString(s);
        return s;
    }
    

    有了前面的这些,multi()写起来就很简单了,这里要注意的是数字位数为奇数时的处理.

    string multiply(string x, string y){
        /*Description: Multiply between two strings
         *Input: Two strings, represents two positive integers
         *Output: Return product of x and y
        */
    
        int xSize = x.length();
        int ySize = y.length();
        int n = Max(xSize, ySize);
        if(n == xSize){
            y = addPreZero(y, n - ySize);
        }else{
            x = addPreZero(x, n - xSize);
        }
        if(n == 1){
            return simplyMultiply(x, y);
        }
    
        string xLeft = x.substr(0, n / 2);
        string xRight = x.substr(n / 2);
        string yLeft = y.substr(0, n / 2);
        string yRight = y.substr(n / 2);
    
        string p1 = multiply(xLeft, yLeft);
        string p2 = multiply(xRight, yRight);
        string p3 = multiply(add(xLeft, xRight), add(yLeft, yRight));
        string p4 = Minus(Minus(p3, p1), p2);
    
        string result = add(add(addZero(p1, 2 * (n - n / 2)),
                                addZero(p4, n - n / 2)), p2);
        return result;
    }
    

    现在,可以尽情的相乘了,两个64位数也可以.

    代码在这里:大数相乘

    总结:以前很少考虑过两个数是怎么相乘的,写在程序里,也许只是一个符号而已,不知道这中间,发生了这么多的故事.现在我们时常面临着的,不是缺乏工具,而是工具封装的太好,程序员都喜欢偷懒,但是要想获得真正的提高,那个盒子,是迟早要打开看看的.我觉得这是数据结构和算法课,交给我的很重要的东西,这种底层的思维习惯,很重要.

    却道,此心安处是吾乡
  • 相关阅读:
    牛客小白月赛3 I 排名【结构体排序/较复杂/细节】
    牛客小白月赛3 F 异或【区间交集】
    牛客小白月赛3 B 躲藏【动态规划/字符串出现cwbc子序列多少次】
    陕西师范大学第七届程序设计竞赛网络同步赛 I 排队排队排队【数组任一位可以移动到队头,最少移动几次增序/数组指针操作】
    陕西师范大学第七届程序设计竞赛网络同步赛 J 黑猫的小老弟【数论/法拉数列/欧拉函数】
    陕西师范大学第七届程序设计竞赛网络同步赛D ZQ的睡前故事【约瑟夫环1-N数到第k个出队,输出出队顺序/ STL模拟】
    陕西师范大学第七届程序设计竞赛网络同步赛 C iko和她的糖【贪心/ STL-优先队列/ 从1-N每个点有能量补充,每段有消耗,选三个点剩下最多能量】
    陕西师范大学第七届程序设计竞赛网络同步赛 F WWX的礼物【数学/k进制下x^n的位数/log】
    NYOJ 71 乘船问题【贪心】
    hdu 5273 Dylans loves sequence
  • 原文地址:https://www.cnblogs.com/lucifer25/p/7966431.html
Copyright © 2020-2023  润新知