1. 程式人生 > >Stanford Algorithms(一): 大數相乘(c++版)

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

tree present multipl append algo 數字 就會 int rod

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

代碼在這裏:大數相乘

總結:以前很少考慮過兩個數是怎麽相乘的,寫在程序裏,也許只是一個符號而已,不知道這中間,發生了這麽多的故事.現在我們時常面臨著的,不是缺乏工具,而是工具封裝的太好,程序員都喜歡偷懶,但是要想獲得真正的提高,那個盒子,是遲早要打開看看的.我覺得這是數據結構和算法課,交給我的很重要的東西,這種底層的思維習慣,很重要.

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