前提,在一個已經排序完成的A陣列中
Divide : 元素x和A陣列的中間元素進行比較
Conquer : 在其中一個子陣列中進行遞迴,如果x小於A陣列的中間元素,就在左子陣列進行遞迴。
Combine : 沒有
遞迴關係式:
發現這個遞迴關係式符合關係式,可以使用主定理(master method)求解,代入,得到,這個結果大於(主方法case 2),因此該遞迴式的解為
,得到解。
int binary_search(vector<int> &array, int min, int max, int target)
{
int mid = 0;
if (max > min)
{
mid = start + (end - start)/2
if(array[mid] == target)
{
return array[mid];
}
if (target < array[mid])
{
return binary_search(array, min, mid - 1, target);
}
else if(target > array[mid])
{
return binary_search(array, mid + 1, max, target);
}
}
return -1;
}
正常一般情況,要計算x的n次方,即為將x * x * x....進行n次,每一次的乘法需要一個常數時間c,總時間為cn,用進行表示。
我們引入分治法的想法,去解決這個問題,我們可以把這個問題進行分解
,當n為偶數時,如果我們得知的結果,只剩下需要計算,這需要一個常數時間,因此,複雜度就會變成原來的一半,將這個遞迴關係寫出
,如同二分搜尋法的結果,得出的解為,這個方法就稱為native recursive squaring。
定義
時間複雜度 : , 為黃金比例常數,
這是一個指數型的時間複雜度,而我們希望他可以式多項式的時間複雜度。
如果我們將費波那契數列的遞迴情況畫成一棵樹,會發現存在許多相同的子樹,也就是有許多情況不斷地重複發生,以至於浪費了許多時間,我們可以將計算過的結果放在一個陣列當中(動態規劃的想法),此持我們在計算時間就會是(我們只需要知道前n項相加即可)。
native recursive squaring(樸素平方遞迴)的時間複雜度為,如果我們將取整數到與其最接近的整數,我們會發現其結果就是第n位的費波那契數。
但是在真實的機器中,這個方法是不可行的,原因是因為我們必須使用浮點數表示和,直接取整可能會有錯誤發生。
我們可以利用費波那契數的特性,來使用平方遞迴
2*2的矩陣和2*2的矩陣進行乘法運算得出來的結果依然是2*2的矩陣,規模不會變大,我們使用分治法的出的時間複雜度依然會保持在
下面使用數學歸納法進行證明:
我們將上面進行結合,得到
根據數學歸納法,證明完畢。
Input: , ,
Output:
假設是一個2*2的矩陣乘法,矩陣C總共有4項需要計算,每一個項需要2步的計算
若有一個n*n的矩陣乘法。矩陣C需要項需要計算,每一項需要n步,整體複雜度為
int main(void)
{
int A[2][2] = {{2, 3}, {4, 5}};
int B[2][2] = {{6, 7}, {8, 9}};
int C[2][2] = {{0, 0}, {0, 0}};
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 2; j++)
{
for (int k = 0; k < 2; k++)
{
C[i][j] = C[i][j] + A[i][k] * B[k][j];
}
}
}
我們使用分治法降低時間複雜度,每個矩陣有數字(因為是方陣),上面分治法的想法都是將規模為n的問題分解成n/2的兩個子問題,我們希望一個n*n的矩陣可以轉化為兩個n/2*n/2規模的矩陣,我們可以使用分塊矩陣(block matrix)的想法
這個例子我們將4*4的矩陣看做4個2*2的矩陣,也就是將n*n的矩陣拆分成數個n/2*n/2的矩陣
我們可以得到以下的關係
我們將其視為A矩陣,將其視為B矩陣,將其視為C矩陣,我們要求的C矩陣的結果,就是將上面結果計算並組合之後就能夠得到,每一個字母我們可以想像成是一個n/2規模的矩陣,我們需要不斷的遞迴計算乘積,直到每一個字母是一個數字。
在上面這個例子中,我們可以得到我們需要進行8次的遞迴運算,也就是,以及4次的求和運算,將兩個矩陣相加所需要的時間複雜度是,矩陣加法的複雜度已經無法再減少了,目前矩陣乘法的總複雜度為:
,發現這個遞迴關係式符合關係式,因此使用主定理來解出這個遞迴式
代入得到,,屬於case 1.
因此結果為,也就是
這個想法的思路為,我們必須繞過這些乘法,也就是減少乘法發生的次數,上面這個例子中,我們需要進行8次,需要進行8次乘法運算並加總才能夠得到C,我們嘗試減少乘法發生的次數,以加快矩陣乘法的速度。在這個演算法中,我們可以讓乘法發生的次數降低到7次。
我們可以在的時間完成這一些操作,之後我們將這四項元素用上面的符號進行表示:
我們可以確認看看這一些結果,以u作為例子
得到,會發現跟我們上方正常進行矩陣乘法運算的結果是相同的
這個演算法使用分治法的思路如下
Divide : 將A矩陣和B矩陣進行拆分,然後求得一些乘積項,也就是計算出所有的P,這一步需要
Conquer : 遞迴的方式處理每一個,得到7個乘積
Combine : 結合每一項P的結果,得出,需要
整個演算法的遞迴關係式為,發現這個遞迴關係式符合關係式,因此使用主定理來解出這個遞迴式
代入得到,,屬於case 1.因此時間複雜度為,比好,但不是最好的演算法,目前最好的矩陣乘法演算法為Coppersmith-Winograd,複雜度為
參考資料: Introduction to algorithms 3rd