演算法導論_第四章_分治策略
演算法導論_第四章_分治策略
分治的三個步驟:
分解:將問題劃分為一些子問題,子問題的形式與原問題一樣,只是規模更小。
解決:遞迴的求解出子問題。如果子問題足夠小,則停止遞迴,直接求解
合併:將子問題的解組合成原問題的解。
最大子陣列問題:
給定陣列A,尋找A的和最大的非空連續子陣列。
可以利用暴力求解,其為Ω(n^2)
這裡利用分治法解決,其時間複雜度為Θ(n*lg(n)):
既然是分治,即肯定要把陣列分開,其有三種情況:
1.完全位於左邊的陣列
2.完全位於右邊的陣列
3.跨越了中點
下面給出具體程式碼,已經加上詳細註釋:
/*************************************************************************
> File Name: FIND_MAXIMUM_SUBARRAY.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年06月24日 星期五 08時53分20秒
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
void FIND_MAX_CROSSING_SUBARRAY(int A[],int low,int mid ,int high);
void FIND_MAXIMUM_SUBARRAY(int A[],int low,int high);
int _low,_high,_sum;
int main(int argc,char *argv[])
{
int A[1000]={13,-3,-25,20,-3,-16,-23,18,20,-7,12,-5,-22,15,-4,7};
FIND_MAXIMUM_SUBARRAY(A,0,15);
printf("%d %d %d",_low,_high,_sum);
return 0;
}
/***********************************
* 函式功能:得到最大陣列
* 引數:
* 1.陣列
* 2.陣列最左邊座標
* 3.陣列最右邊座標
* *********************************/
void FIND_MAXIMUM_SUBARRAY(int A[],int low,int high)
{
int left_low,left_high,right_low,right_high;
int left_sum,right_sum;
int cross_low,cross_high,cross_sum;
if(low==high)
{
_low=low;
_high=high;
_sum=A[low];
return;
}
int mid=(low+high)/2;//分割點
FIND_MAXIMUM_SUBARRAY(A,low,mid);//找到第一種情況的最大陣列
left_low =_low;
left_high=_high;
left_sum =_sum;
FIND_MAXIMUM_SUBARRAY(A,mid+1,high);//找到第二種情況的最大陣列
right_sum=_sum;
right_high=_high;
right_low=_low;
FIND_MAX_CROSSING_SUBARRAY(A,low,mid,high);//找到第三種情況的最大陣列
cross_low=_low;
cross_high=_high;
cross_sum=_sum;
//比較三種情況,哪種情況較優返回哪種情況
if(left_sum>=right_sum&&left_sum>=cross_sum)
{
_low=left_low;
_high=left_high;
_sum=left_sum;
}
else if(right_sum>=left_sum&&right_sum>=cross_sum)
{
_low =right_low;
_high=right_high;
_sum =right_sum;
}
else
{
_low =cross_low;
_high=cross_high;
_sum =cross_sum;
}
return ;
}
/**********************************
* 函式功能:尋找跨越中點的最大子陣列
* 引數:
* 1.陣列
* 2.陣列最左邊
* 3.陣列中點
* 4.陣列最右邊
* *****************************/
void FIND_MAX_CROSSING_SUBARRAY(int A[],int low,int mid ,int high)
{
int left_sum=-INF;//左邊的最大陣列,從中點開始
int right_sum=-INF;//右邊的最大陣列,從中點開始
int sum=0;
int max_left,max_right;//最大陣列左邊座標,最大陣列右邊座標
for(int i=mid;i>=low;i--)//左邊最大陣列求解
{
sum+=A[i];
if(sum>left_sum)
{
left_sum=sum;
max_left=i;
}
}
sum=0;
for(int i=mid+1;i<=high;i++)//右邊最大陣列求解
{
sum+=A[i];
if(sum>right_sum)
{
right_sum=sum;
max_right=i;
}
}
//利用全域性變數,代替return
_low=max_left;
_high=max_right;
_sum=right_sum+left_sum;
return ;
}
我們來分析一下其時間複雜度:
可以看出,其把一個原問題分解為兩個子問題,所以2T(n/2)
又其中的尋找 跨越中點的最大子陣列的時間複雜度為Θ(n^2)
所以T(n)=2*T(n/2)+Θ(n^2)
所以根據上幾節的知識,其時間複雜度為Θ(n*lg(n))
下面再來介紹一個例子:
矩陣乘法的Strassen演算法
對於一個矩陣乘法其可以同過一下一個簡單方法實現:
/*************************************************************************
> File Name: Strassen.cpp
> Author:chudongfang
> Mail:1149669942@qq.com
> Created Time: 2016年06月27日 星期一 09時55分03秒
************************************************************************/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include <algorithm>
#define INF 0x3f3f3f3f
using namespace std;
typedef long long ll;
void SQUARE_MATRIX_MULTIPLY(int A[][100],int B[][100],int C[][100],int n);
int main(int argc,char *argv[])
{
int A[100][100];
int B[100][100];
int C[100][100];
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&A[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&B[i][j]);
SQUARE_MATRIX_MULTIPLY(A,B,C,n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
printf("%4d",C[i][j]);
printf("\n");
}
return 0;
}
void SQUARE_MATRIX_MULTIPLY(int A[][100],int B[][100],int C[][100],int n)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
C[i][j]=0;
for(int k=1;k<=n;k++)
{
C[i][j]+=A[i][k]+B[k][j];
}
}
}
}
其時間複雜度為
Θ(n^3);
下面採用分治方法計算:
由於該陣列分治操作相對複雜,這裡先給出虛擬碼,以後有時間再給出相應程式碼:
跟據程式碼可以看出其遞迴式為:
T(n)=8*(n/2)+Θ(n^2)
則根據主方法計算出其時間複雜度為Θ(n^3);
所以其分治並不能降低其時間複雜度
下面介紹一個可以降低其時間複雜度的方法
Strassen方法
其演算法核心思想是利用空間換換時間,利用加減法減少遞迴次數,使其遞迴次數由8次降低到了7次。減少一次的代價
可能是額外的幾次n/2*n/2矩陣的加法,但只是常數次。
下面給出其虛擬碼:
其根據加減法進行一些轉換,最後的結果還是不變的。
這裡給出書上的解釋:
求解遞迴式
這裡重點為主方法,其運用最廣
1.代入法求解遞迴式
思想:
(1)猜測解的形式
(2)用數學歸納法,求出解中的常數,並證明解是正確的。
在這裡,先跟據情況猜測出解的大概情況,然後代入,根據前面的對漸進符號的定義最終求出常數符合條件,則其等
式成立。
2.用遞迴樹求解遞迴式
在遞迴樹中,每個節點表示一個單一的子問題的代價,子問題對應某次遞迴函式呼叫。我們將樹中每層代價求和,得
到每層代價,然後將所有層的代價求和,得到所有層次的遞迴呼叫的總代價。
3.利用主方法求解遞迴式
主方法為如下形式的遞迴式提供了一種“菜譜”式的求解方法
T(n)=a*T(n/b)+f(n)
其中a>=1和b>1是常數,f(n)是漸進正函式。
主定理
令a>=1和b>1是常數,f(n)是一個函式,T(n)是定義在非負整數上的遞迴式:
T(n)=a*T(n/b)+f(n)
其中我們將n/b解釋為向上取整或向下取整,那麼T(n)有如下漸進界:
1.若對某個常數e>0有f(n)=O(n^ log(b) a-e)則,T(n)=Θ(n^log(b)a)
2.若f(n)=Θ(n^log(b)a)則T(n)=Θ(n^log(b)a*lg(n))
3.若對某個e>0有f(n)=Ω(n^(log(b)a-e))其對某個常數c<1和所有足夠大的n有a*f(n/b)
<=c*f(n),則T(n)=Θ(f(n))
簡明一點,就是拿f(n)和函式n^(log(b)a)比較,
如果n^(log(b)a)更大,就滿足情況1其解為:T(n)=Θ(n^log(b)a)
若函式f(n)更大,如情況3則解為:T(n)=Θ(f(n))
如果兩個函式大小相當就為情況2,其解為:
T(n)=Θ(n^log(b)a*lg(n))
以上比價都必須滿足多項式大於或小於
這裡舉幾個例子:
a=9 b=3;
f(n)=n
應用於情況1,所以其為Θ(n^2)
a=1 b=3/2 f(n)=1
應用於情況2所以其為Θ(lgn)
a=3 b=4 f(n)=n*lg(n)
應用於情況3所以其為Θ(n*lg(n))
a=2 b=2 f(n)= Θ(n)
應用於情況2所以其為Θ(n*lg(n))
a=8 b=2 f(n)= Θ(n^2)
應用於情況1所以其為Θ(n^3)
a=7 b=2 f(n)= Θ(n^2)
應用於情況1所以其為Θ(n^lg(7))
總結一下,其用法,比較f(n)和n^(log(b)a),
如果不相同,則取其較大的那個,
如果相同 ,則取log(b)a再乘上lg(n)
下面還有對主定理的證明內容,由於我們先只是會運用主定理,這裡先不證,留到以後
進階時在進行分析。
相關文章
- 利用分治策略解題
- 《演算法導論》演算法
- 分治演算法演算法
- 演算法導論-堆排序演算法排序
- 演算法導論-快速排序演算法排序
- 計算機演算法設計與分析——遞迴與分治策略(一)計算機演算法遞迴
- 演算法導論-第6章演算法
- 演算法導論FFT程式碼演算法FFT
- 演算法導論第三十一(31)章數論演算法演算法
- 分治演算法-骨牌鋪方格演算法
- Python演算法:分治法Python演算法
- [演算法] 一些分治演算法
- 演算法學習-CDQ分治演算法
- 學演算法要讀《演算法導論》嗎?演算法
- 《演算法導論》學習筆記演算法筆記
- 【演算法導論】24.1 Bellman-Ford 演算法演算法
- 分治演算法-眾數問題演算法
- 【演算法】分治四步走演算法
- 遞迴 & 分治演算法深度理解遞迴演算法
- 史蒂芬斯與演算法導論之思演算法
- 當prompt策略遇上分治演算法,南加大、微軟讓大模型煉成「火眼金睛」演算法微軟大模型
- 讀人工智慧全傳03分治策略人工智慧
- 遞迴與分治演算法練習遞迴演算法
- 從分治演算法到 Hadoop MapReduce演算法Hadoop
- 演算法第二篇之分治演算法
- 演算法設計與分析中的幾個核心演算法策略:動態規劃、貪心演算法、回溯演算法和分治演算法演算法動態規劃
- 演算法導論第二章思考題演算法
- 演算法導論第二章筆記演算法筆記
- 演算法導論第二章練習演算法
- 演算法導論 3.2-7 共軛數演算法
- 演算法導論_第七章_快速排序演算法排序
- 演算法導論學習之五:快速排序演算法排序
- 樹分治 - 點分治
- 演算法導論C語言實現: 演算法基礎演算法C語言
- 五大常用演算法之一:分治演算法演算法
- 演算法導論第三章練習演算法
- 演算法導論_第六章_堆排序演算法排序
- 演算法導論學習之六:歸併排序演算法排序