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

代码在这里:大数相乘

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

时间: 2024-10-10 06:25:39

Stanford Algorithms(一): 大数相乘(c++版)的相关文章

java版大数相乘

在搞ACM的时候遇到大数相乘的问题,在网上找了一下,看到了一个c++版本的 http://blog.csdn.net/jianzhibeihang/article/details/4948267 用java搞了一个版本 这里说一下思路 将数字已字符串形式接收,转换成int[]整型数组,然后num1[],num2[]依次相乘,结果保存到result[]中 其他注意的在注释中有说明 1 package com.gxf.test; 2 3 import java.util.Scanner; 4 5 p

大数相乘算法C++版

#include <iostream> #include <cstring> using namespace std; #define null 0 #define MAXN 51 //大数相乘 char *big_cheng(char line1[], char line2[]) { short s1[MAXN], s2[MAXN], s[MAXN];//s1,s2:两个乘数:s:乘积的结果 int i, j, k, c; int len1, len2, len; len1 =

1051:A &#215; B problem 大数相乘

给你两个整数,请你计算A × B. 输入 数据的第一行是整数T(1 ≤ T ≤ 20),代表测试数据的组数.接着有T组数据,每组数据只有一行,包括两个非负整数A和B.但A和B非常大,Redraiment能保证这些数用long来保存一定会溢出.但A和B的位数最大不会超过100位. 输出 对应每组测试数据,你都要输出两行:第一行为:"Case #:", # 代表这是第几组测试数据.第二行是一个等式:"A * B = Sum", Sum 代表 A × B 的结果.你要注意

大数相乘

1.算法思路 将大数当做字符串进行处理,也就是将大数用十进制字符数组进行表示,然后模拟人们手工进行“竖式计算”的过程得到乘法的结果. 乘积是逐位相乘,也就是a[i] * b[j],结果加入到积C的第i+j位,即 最后处理进位即可 例如:A =17 = 1*10 + 7 = (7,1)最后是十进制的幂表示法,幂次是从低位到高位,以下同. B=25 = 2*10 + 5 = (5, 2); C = A * B = (7 * 5, 1 * 5 + 2 * 7, 1 * 2) = (35, 19, 2)

两大数相乘 -- javascript 实现

(function(){ var multiply = function(n1,n2){ var nstr1 = n1.toString(); var nstr2 = n2.toString(); var carry = 0; var ret = ""; var di = 0; var dj = 0; var dig = 0.1; for(var i = nstr1.length - 1 ;i >= 0;i--) { var tmp = ""; var di

ACM大赛题 【C++大数相乘】

题     目: 大数相乘,我们常常会遇见几百位长的数字之间相乘的编程题,例如在算法中,ACM大赛题中,下面就是大数相乘的详细过程. 思      路: 模拟相乘法 举例说明: 123*789=97047 原理展示: 计算机计算: . C++源代码: 结果展示:

UVA 10106 Product (大数相乘)

Product The Problem The problem is to multiply two integers X, Y. (0<=X,Y<10250) The Input The input will consist of a set of pairs of lines. Each line in pair contains one multiplyer. The Output For each input pair of lines the output line should c

大数相乘&mdash;&mdash;模拟乘法的运算规则

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<string.h> int main() { void add(int a[], int length1, int b[], int length2); char str1[255], str2[255]; int num1[255] = { 0 }, num2[255] = { 0 }; int product[255] = { 0 }, temp[255] =

海豚实习-实现两个大数相乘

问题描述:定义大数d为一个数组,表示为:d=a[k-1]*10^(k-1)+a[k-2]*10^(k-2)+...+a[1]*10+a[0](k为数组长度),实现一个函数,返回两个大数相乘的结果 /** d1=a[k-1]*10^(k-1)+a[k-2]*10^(k-2)+...+a[1]*10+a[0] d2=b[l-1]*10^(l-1)+b[l-2]*10^(l-2)+...+b[1]*10+b[0] d1*d2 包含k*l项,这只是没有合并的 每一项可以表示为:d*10^x,然后将(x,