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位数也可以.

代码在这里:大数相乘

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

转载于:https://www.cnblogs.com/lucifer25/p/7966431.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值