1. 程式人生 > >遞迴分治-大整數乘法

遞迴分治-大整數乘法

最近在學演算法,想著不能只是學,要深刻領悟,需要記錄,需要寫程式碼,需要分析……所以就誕生了這篇部落格。

問題描述:

設X和Y都是n位整數,計算它們的乘積XY。可以使用傳統的數學計算方法,但是這樣做計算步驟太多,效率較低。如果將每個一位數的乘法或加法看做一步運算,則這種方法需要進行O(n^2)步運算才能求出乘積XY。

方法一

基本計算步驟如下,如:56*78
第一步:6*8;
第二步:5*8;
第三步:6*7;
第四步:5*7;
第五步:以上4步的和加起來,得到最後的結果。
將以上的例子進行抽象,即可以這樣表示:
將n位十進位制(還包括其他進位制,如二進位制、八進位制、十六進位制等)整數X和Y都分為2段,每段的長為n/2位。即:
X

=A10n2+B
Y=C10n2+D
這樣XY的乘積就是
XY=(A10n2+B)C10n2+D=AC10n+AD+BC10n2+BD
由以上的等式我們可以看出,需要4次乘法,分別是:AC,AD,BC,BD,以及3次加法,還有3次移位操作。所有這些加法和移位公用O(n)步運算。設T(n)是2個n位整數相乘所需的運算總數,則有:

T(n) =

{O(1),4T(n2)+O(n),n =1n>1
然後,我們進行計算,得到該演算法的時間複雜度為O(n^2),具體過程如下:
這裡寫圖片描述
實現程式碼如下:
#include<stdio.h>
#include<math.h>
#define MAX_LENGTH 10 void sqperateNum(int num, int seArray_num[]); //資料儲存到陣列中 int main() { int i=0, j=0; int num1, num2; int sum = 0; int seArray_num1[MAX_LENGTH] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; //初始化陣列 int seArray_num2[MAX_LENGTH] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
}; printf("Please input two numbers:\n"); scanf("%d%d", &num1, &num2); sqperateNum(num1, seArray_num1); sqperateNum(num2, seArray_num2); while (seArray_num1[i]!=-1) { while (seArray_num2[j] != -1) { sum += seArray_num1[i] * seArray_num2[j] * pow(10.0, (i + j)); j++; } i++; j = 0; } printf("%d*%d=%d\n", num1, num2, sum); return 0; } //資料儲存到陣列中 void sqperateNum(int num, int seArray_num[]) { int k = 0; do { seArray_num[k] = num % 10; num /= 10; k++; } while (num%10!=0 ||(num/10 !=0)); }

方法二

然後我們對於該問題進行優化,要想改進該演算法的複雜性,必須減少乘法的次數。我們對以上的表示式進行一個優化,得到如下的表示式:
XY=A10n2+BC10n2+D=AC10n+AD+BC10n2+BD=AC10n+((AB)(DC)+AC+BD)10n2+BD
通過以上表達式變形,我們只需進行3次n/2位整數的乘法,6次加減法操作和2次移位。由此可得如下的表示式:
T(n) =

{O(1),3T(n2)+O(n),n =1n>1
再次進行計算,得到該演算法的時間複雜度為O(nlog3),相比於O(n2)來說,這是一個很大的改進。
這裡寫圖片描述
程式碼如下,這個程式碼是在別人的基礎上改了一下,具體如下:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
int IntegerMultiply(int X, int Y, int N)
{
    int x = X;
    int y = Y;
    if ((0 == x) || (0 == y))
        return 0;
    if (1 == N)
    {
        return x*y;
    }
    else
    {
        int XL = x / (int)pow(10., (int)N / 2);
        int XR = x - XL * (int)pow(10., N / 2);
        int YL = y / (int)pow(10., (int)N / 2);
        int YR = y - YL * (int)pow(10., N / 2);

        int XLYL = IntegerMultiply(XL, YL, N / 2);
        int XRYR = IntegerMultiply(XR, YR, N / 2);
        int XLYRXRYL = IntegerMultiply(XL - XR, YR - YL, N / 2) + XLYL + XRYR;
        return (XLYL * (int)pow(10., N) + XLYRXRYL * (int)pow(10., N / 2) + XRYR);
    }
}
int main()
{
    int x = 1234, y = 9876; 
    int value = 0;
    value = IntegerMultiply(x, y, 4);
    printf("%ld*%ld=%ld", x,y,value);
    return 0;
}

綜上所述,主要是掌握對演算法複雜度的推導,以及實現相應的演算法。在這裡,不得不感嘆數學家的厲害,只是一個小小的公式變換,就將複雜度大大降低了。請大數學家們收下我的膝蓋,哈哈~

相關推薦

分治-整數乘法

最近在學演算法,想著不能只是學,要深刻領悟,需要記錄,需要寫程式碼,需要分析……所以就誕生了這篇部落格。 問題描述: 設X和Y都是n位整數,計算它們的乘積XY。可以使用傳統的數學計算方法,但是這樣做計算步驟太多,效率較低。如果將每個一位數的乘法或加法看做

分治-整數乘法

請設計一個有效的演算法,可以進行兩個n位大整數的乘法運算 小學的方法:O(n2)            效率太低 X= Y= X = a 2n/2+ b     Y = c 2n/2+ d XY

分治演算法解決整數乘法問題

整數相乘:小整數相乘在演算法時間分析中可以認為是常數時間,但是對於大整數,時間需要考慮。兩個N位數的整數X和Y相乘,常規方法花費時間是,因為X的每一位都要和Y的每一位相乘,是兩層迴圈。 分治演算法解決整數相乘問題: 例如,X是12345678,Y是87654321,將X和Y都拆成兩半,得到

整數乘法中的分治思想(TOOM-COOK的一種使用方法)

演算法分析與設計學習中,接觸到一道大整數乘法問題,分享出來,原題目如下: 演算法分析在用分治法求兩個n位大整數u和v的乘積時,將u和v都分割為長度為n/3的3段。證明可以用5次n/3位整數的乘法求得uv的值。按此思想設計大整數乘積的分治方法,並分析演算法的計算

分治演算法】整數乘法

大整數的乘法 在計算機中,長整形(long int)變數的範圍不能超過10位數。即便用雙精度(double)變數,也僅能保證16位有效數字 的精度。在某些需要更高精度的乘法運算場合,需要

2980 整數乘法

沒有 程序代碼 pre != return 註意 string str1 改變 題目來源:http://bailian.openjudge.cn/practice/2980/描述求兩個不超過200位的非負整數的積。輸入有兩行,每行是一個不超過200位的非負整數,沒有多余的前

light oj 1024 - Eid 整數乘法

imp borde mean ron [] public ans test case cas In a strange planet there are n races. They are completely different as well as their food

Multiply Strings整數乘法

height image 分析 特殊情況 pac 什麽 .com 總結 pos [抄題]: 以字符串的形式給定兩個非負整數 num1 和 num2,返回 num1 和 num2 的乘積。 [暴力解法]: 時間分析: 空間分析: [思維問題]: 還要找到結果中第一位不等於

caioj1450:【快速傅裏葉變換】整數乘法

rose clas scan name 代碼 printf 答案 r+ 傅裏葉變換 【傳送門:caioj1450】 簡要題意:   給出兩個超級大的整數,求出a*b 題解:   Rose_max出的一道FFT例題,卡掉高精度 = =   只要把a和b的每一

整數乘法

space ret algorithm 進行 cout while 當前 ostream cstring #include<cmath> #include<cstdio> #include<cstring> #include&l

關於整數乘法和加法的一些整理

例如,有兩個大整數,a和b,其中a、b位數都是大於10的。我們知道,在做OJ的題時,碰到大整數的乘法,不能直接用a*b得到結果,那樣是不對的,所以需要用陣列來儲存。 首先,很簡單,定義兩個陣列(足夠長),s1和s2,分別用來儲存大整數a和b,因為我們需要用來相乘,而乘法是從最後一位開始

演算法與設計經典題:整數乘法(教材2-4)

給定兩個整數u和v,他們分別有m和n為數字,且m≤n,用通常的乘法求uv的值需要O(mn)時間,可以將u和v均看作是有n位數字的大整數,用本章介紹的分治法,在O(n^(log3))時間內計算uv的值,當m<<n時,此法效率不高。設計演算法在O(nlog2/3)時間計算uv的值 在O(

CodeForces - 768B Code For 1 —— 分治

題意: 把一個數n拆分,奇數拆成n/2,1,n/2,偶數拆成n/2,0,n/2,1和0不可拆,問拆分後從l位置到r位置1的個數 思路: 分治遞迴拆分即可...想的太多... n最大是2^50,最多遞迴呼叫也才是高為50的二叉樹 #include <iostream>

分治法 整數乘法

學習演算法的時候,其中一道經典就是大整數乘法咯,感覺有點難理解,於是寫一篇部落格加深下理解。 大數相乘不可以直接得到答案,肯定會超出數的範圍,而解決大數相乘的辦法就是分治法:將大問題變成小問題,再變成簡單問題,最後進行合併。 例如:        

【演算法】整數乘法

大整數乘法 問題描述 求兩個不超過200位的非負整數的積。 輸入形式 有兩行,每行是一個不超過200位的非負整數,沒有多餘的前導0。 輸出形式 一行,即相乘後的結果。結果裡不能有多餘的前導0,即如果結果是342,那麼就不能輸出為0342。 樣例輸入 123456789

演算法_(正整數的冪次方表示)

遞迴 一、概念 函式呼叫自身。 注意:遞迴程式可能更加簡潔,但是不一定節省時間。 二、案例 案例網址:http://cxsjsxmooc.openjudge.cn/2018t2fallw2/2/ 總時間限制: 1000ms 記憶體限制: 65536kB描述 任何一個正整數都可以用2的冪次方表示。例如:    

借陣列實現整數乘法

思想: 用字串來控制輸入,陣列來儲存,陣列的低位存整數的低位,高位來儲存高位, 和:計算的過程基本上和小學生列豎式做加法相同。 差:跟和差不多 乘:計算的過程基本上和列豎式做乘法相同。為程式設計方便,並不急於處理進位,而將進位問題留待最後統一處理 除:基本的思想

C語實現格子乘法--整數乘法

之前看過有博主發過python版的,看了一看覺得這個方法好玩,小時候老師教的時候又總聽不懂,就想試試看能不能實現起來。 具體的這篇部落格也寫的很清楚了,在這就具體說一說我這個演算法的思路好了。 1.讓使用者輸入兩個大整數以及它們的長度。 2.建立一個二維陣列

Java實現整數乘法

請設計一個有效的演算法,可以進行兩個n位大整數的乘法運算 1 最暴力的方法:O(n^2) 2 我們採用分而治之的思想 將X和Y按如下方法分成兩部分 那麼 X = A*10^(n/2) + B Y = C*10^(n/2) + D X*

分治法實現整數乘法【C++語言】

如果實現傳統演算法中兩個n位整數相乘,第一個整數中的n個數字都要分別乘以第二個整數的n個數字,這樣就一共要做n*n次乘法。看上去設計一個乘法次數少於n*n的演算法是不可能的,但事實證明並非如此,可以使用分治的思想計算兩個大整數的相乘。 首先從僅有兩位數字的兩個數12和34考慮,12 = 1 *