當前位置:首頁 » 操作系統 » 分治演算法的應用

分治演算法的應用

發布時間: 2023-08-19 11:02:38

① 分治演算法時間復雜度

一:分治演算法和遞歸
1.簡述遞歸

我們要講到分治演算法,我覺得有必要說一下遞歸,他們就像一對孿生兄弟,經常同時應用在演算法設計中,並由此產生許多高效的演算法。
直接或間接的調用自身的演算法稱為遞歸演算法。用函數自身給出定義的函數稱為遞歸函數。

int fibonacci(int n){
if (n <= 1) return 1;
return fibonacci(n-1)+fibonacci(n-2);
}
先簡單看一下經典的遞歸例子,博主會找個時間系統詳細的總結一下關於遞歸的內容。

2.簡述分治

分治法的設計思想是:

分–將問題分解為規模更小的子問題;
治–將這些規模更小的子問題逐個擊破;
合–將已解決的子問題合並,最終得出「母」問題的解;
一個先自頂向下,再自底向上的過程。

凡治眾如治寡,分數是也。—孫子兵法

3.分治法與遞歸的聯系

由分治法產生的子問題往往是原問題的較小模式,這就為使用遞歸技術提供了方便。在這種情況下,反復應用分治手段,可以使子問題與原問題類型一致而其規模卻不斷縮小,最終使子問題縮小到很容易直接求出其解。這自然導致遞歸過程的產生。

二:分治法的適用條件
分治法所能解決的問題一般具有以下幾個特徵:

1) 該問題的規模縮小到一定的程度就可以容易地解決
2) 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質。
3) 利用該問題分解出的子問題的解可以合並為該問題的解;
4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題。

第一條特徵是絕大多數問題都可以滿足的,因為問題的復雜性一般是隨著問題規模的增加而增加;

第二條特徵是應用分治法的前提它也是大多數問題可以滿足的,此特徵反映了遞歸思想的應用;、

第三條是關鍵,能否利用分治法完全取決於問題是否具有第三條特徵,如果具備了第一條和第二條特徵,而不具備第三條特徵,則可以考慮用貪心法或動態規劃法。

第四條特徵涉及到分治法的效率,如果各子問題是不獨立的則分治法要做許多不必要的工作,重復地解公共的子問題,此時雖然可用分治法,但一般用動態規劃法較好

三:分治法的基本步驟
分解問題:將原問題分解為若干個規模較小,相互獨立,與原問題形式相同的子問題;(自頂向下)
這里涉及到一個平衡子問題的思想:人們從大量實踐中發現,在用分治法設計演算法時,最好使子問題的規模大致相同。即將一個問題分成大小相等的k個子問題的處理方法是行之有效的。這種使子問題規模大致相等的做法是出自一種平衡子問題的思想,它幾乎總是比子問題規模不等的做法要好。

解決問題:如果問題規模較小而容易被解決則直接解,否則遞歸地解各個子問題,以得到小問題的解。
合並結果:將各個子問題的解合並為原問題的解:(自底向上)。
它的一般演算法設計模式如下:
divide-and-conquer(P){
if ( | P | <= n0) adhoc(P); //(2)解決問題:遞歸到小問題,則解決小規模的問題(自頂向下)
divide P into smaller subinstances P1,P2,...,Pk;//(1)分解問題
for (i=1,i<=k,i++)
yi=divide-and-conquer(Pi); //利用遞歸的解各子問題
return merge(y1,...,yk); //將各子問題的解合並為原問題的解(自底向上)
}
四:分治法的復雜性分析
從分治法的一般設計模式可以看出,用他設計出的程序一般是遞歸演算法。因此分治法的計算效率通常可以用遞歸方程來進行分析。
一個分治法將規模為n的問題分成k個規模為n/m的子問題去解。設分解閥值(表示當問題P規模不超過n0時,問題已容易解出,不必再繼續分解)n0=1,且adhoc解規模為1的問題耗費1個單位時間。再設將原問題分解為k個子問題以及用merge將k個子問題的解合並為原問題的解需用f(n)個單位時間。用T(n)表示該分治法解規模為|P|=n的問題所需的計算時間,則有:

通常可以用展開遞歸式的方法來解這類遞歸方程,反復帶入求解得

② 分治、貪心五大演算法

1、分治
分治(即分而治喊孫之),把一個復雜的問題分成多鄭激鏈個相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合並。
適用場景:二分搜索、歸並排序、快速排序、大整數乘法、第K小元素、最近點對、快速傅里葉變換等。

2、動態規劃
動態規劃法也是把問題一層一層地分解為規模逐漸減小的同類型的子問題。動態規劃通常用來求最優化問題。此類問題可以有很多可行解,我們求出的是一個最優解,可能存在多個最優解。(最優子結構、公共子問題)
與分治法的區別是:分治的子問題是相互獨立的,動態規劃最好解決有公共子問題的,子問題相關性很大。
使用場景:矩陣連乘、鋼條切割、最長公共子序列、最優二叉搜索樹、流水作業調度、0/1背包問題等。

維特比演算法是動態規劃在HMM中的應用,維特比演算法用於解決HMM的預測或者叫解碼問題。
viterbi有最優解是因為HMM每一步是條件獨立的!既然後面的概率和前面的沒關系,那前面選最大的概率就行了。
而beam search時後面的概率依賴於前面所有的詞,相當於n-gram是滿的,viterbi的n-gram是2

背包問題:
https://blog.csdn.net/wind__chaser/article/details/89457771
https://blog.csdn.net/qq_38410730/article/details/81667885

3、貪心
通過局部最優選擇達鉛吵到全局最優選擇。貪心演算法不一定總產生最優解,貪心演算法是否產生優化解,需嚴格證明貪心演算法產生最優解的條件:(最優子結構、貪心選擇性)
貪心選擇性:當一個問題的全局最優解可以通過局部最優解得到,稱這個問題具有貪心選擇性。
適用場景:活動選擇問題、哈夫曼編碼問題、最小生成樹問題、單源最短路徑問題等。

貪心演算法:softmax之後取最大概率。與之對應的是,Beam Search演算法
http://www.360doc.com/content/18/0618/09/17563728_763230413.shtml
https://blog.csdn.net/qq_16234613/article/details/83012046
https://www.hu.com/question/54356960

分治和動態規劃的區別:
動態規劃也是一種分治思想(比如其狀態轉移方程就是一種分治),但與分治演算法不同的是,分治演算法是把原問題分解為若干個子問題,
自頂向下求解子問題,合並子問題的解,從而得到原問題的解。動態規劃也是把原始問題分解為若干個子問題,然後自底向上,
先求解最小的子問題,把結果存在表格中,在求解大的子問題時,直接從表格中查詢小的子問題的解,避免重復計算,從而提高演算法效率。

動態規劃和分治法有些相像,都是把一個問題分成了很多子問題來求解,但是不同的是動態規劃會記憶之前解決的子問題的結果,
避免了重復計算。判斷一個問題是否能用動態規劃求解,要看它是否能劃分成合適的子問題,然後寫出遞推關系式。
動態規劃得到的解一定是最優解。

③ 程序員都應該精通的六種演算法,你會了嗎

對於一名優秀的程序員來說,面對一個項目的需求的時候,一定會在腦海里浮現出最適合解決這個問題的方法是什麼,選對了演算法,就會起到事半功倍的效果,反之,則可能會使程序運行效率低下,還容易出bug。因此,熟悉掌握常用的演算法,是對於一個優秀程序員最基本的要求。


那麼,常用的演算法都有哪些呢?一般來講,在我們日常工作中涉及到的演算法,通常分為以下幾個類型:分治、貪心、迭代、枚舉、回溯、動態規劃。下面我們來一一介紹這幾種演算法。


一、分治演算法


分治演算法,顧名思義,是將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。


分治演算法一般分為三個部分:分解問題、解決問題、合並解。

分治演算法適用於那些問題的規模縮小到一定程度就可以解決、並且各子問題之間相互獨立,求出來的解可以合並為該問題的解的情況。


典型例子比如求解一個無序數組中的最大值,即可以採用分治演算法,示例如下:


def pidAndConquer(arr,leftIndex,rightIndex):

if(rightIndex==leftIndex+1 || rightIndex==leftIndex){

return Math.max(arr[leftIndex],arr[rightIndex]);

}

int mid=(leftIndex+rightIndex)/2;

int leftMax=pidAndConquer(arr,leftIndex,mid);

int rightMax=pidAndConquer(arr,mid,rightIndex);

return Math.max(leftMax,rightMax);


二、貪心演算法


貪心演算法是指在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。


貪心演算法的基本思路是把問題分成若干個子問題,然後對每個子問題求解,得到子問題的局部最優解,最後再把子問題的最優解合並成原問題的一個解。這里要注意一點就是貪心演算法得到的不一定是全局最優解。這一缺陷導致了貪心演算法的適用范圍較少,更大的用途在於平衡演算法效率和最終結果應用,類似於:反正就走這么多步,肯定給你一個值,至於是不是最優的,那我就管不了了。就好像去菜市場買幾樣菜,可以經過反復比價之後再買,或者是看到有賣的不管三七二十一先買了,總之最終結果是菜能買回來,但搞不好多花了幾塊錢。


典型例子比如部分背包問題:有n個物體,第i個物體的重量為Wi,價值為Vi,在總重量不超過C的情況下讓總價值盡量高。每一個物體可以只取走一部分,價值和重量按比例計算。

貪心策略就是,每次都先拿性價比高的,判斷不超過C。


三、迭代演算法


迭代法也稱輾轉法,是一種不斷用變數的舊值遞推新值的過程。迭代演算法是用計算機解決問題的一種基本方法,它利用計算機運算速度快、適合做重復性操作的特點,讓計算機對一組指令(或一定步驟)進行重復執行,在每次執行這組指令(或這些步驟)時,都從變數的原值推出它的一個新值。最終得到問題的結果。


迭代演算法適用於那些每步輸入參數變數一定,前值可以作為下一步輸入參數的問題。


典型例子比如說,用迭代演算法計算斐波那契數列。


四、枚舉演算法


枚舉演算法是我們在日常中使用到的最多的一個演算法,它的核心思想就是:枚舉所有的可能。枚舉法的本質就是從所有候選答案中去搜索正確地解。

枚舉演算法適用於候選答案數量一定的情況。


典型例子包括雞錢問題,有公雞5,母雞3,三小雞1,求m錢n雞的所有可能解。可以採用一個三重循環將所有情況枚舉出來。代碼如下:



五、回溯演算法


回溯演算法是一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就「回溯」返回,嘗試別的路徑。

許多復雜的,規模較大的問題都可以使用回溯法,有「通用解題方法」的美稱。


典型例子是8皇後演算法。在8 8格的國際象棋上擺放八個皇後,使其不能互相攻擊,即任意兩個皇後都不能處於同一行、同一列或同一斜線上,問一共有多少種擺法。


回溯法是求解皇後問題最經典的方法。演算法的思想在於如果一個皇後選定了位置,那麼下一個皇後的位置便被限制住了,下一個皇後需要一直找直到找到安全位置,如果沒有找到,那麼便要回溯到上一個皇後,那麼上一個皇後的位置就要改變,這樣一直遞歸直到所有的情況都被舉出。


六、動態規劃演算法


動態規劃過程是:每次決策依賴於當前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最優化決策解決問題的過程就稱為動態規劃。


動態規劃演算法適用於當某階段狀態給定以後,在這階段以後的過程的發展不受這段以前各段狀態的影響,即無後效性的問題。


典型例子比如說背包問題,給定背包容量及物品重量和價值,要求背包裝的物品價值最大。


④ 分治演算法的應用實例

下面通過實例加以說明: 給你一個裝有1 6個硬幣的袋子。1 6個硬幣中有一個是偽造的,並且那個偽造的硬幣比真的硬幣要輕一些。你的任務是找出這個偽造的硬幣。為了幫助你完成這一任務,將提供一台可用來比較兩組硬幣重量的儀器,利用這台儀器,可以知道兩組硬幣的重量是否相同。比較硬幣1與硬幣2的重量。假如硬幣1比硬幣2輕,則硬幣1是偽造的;假如硬幣2比硬幣1輕,則硬幣2是偽造的。這樣就完成了任務。假如兩硬幣重量相等,則比較硬幣3和硬幣4。同樣,假如有一個硬幣輕一些,則尋找偽幣的任務完成。假如兩硬幣重量相等,則繼續比較硬幣5和硬幣6。按照這種方式,可以最多通過8次比較來判斷偽幣的存在並找出這一偽幣。
另外一種方法就是利用分而治之方法。假如把1 6硬幣的例子看成一個大的問題。第一步,把這一問題分成兩個小問題。隨機選擇8個硬幣作為第一組稱為A組,剩下的8個硬幣作為第二組稱為B組。這樣,就把1 6個硬幣的問題分成兩個8硬幣的問題來解決。第二步,判斷A和B組中是否有偽幣。可以利用儀器來比較A組硬幣和B組硬幣的重量。假如兩組硬幣重量相等,則可以判斷偽幣不存在。假如兩組硬幣重量不相等,則存在偽幣,並且可以判斷它位於較輕的那一組硬幣中。最後,在第三步中,用第二步的結果得出原先1 6個硬幣問題的答案。若僅僅判斷硬幣是否存在,則第三步非常簡單。無論A組還是B組中有偽幣,都可以推斷這1 6個硬幣中存在偽幣。因此,僅僅通過一次重量的比較,就可以判斷偽幣是否存在。
假設需要識別出這一偽幣。把兩個或三個硬幣的情況作為不可再分的小問題。注意如果只有一個硬幣,那麼不能判斷出它是否就是偽幣。在一個小問題中,通過將一個硬幣分別與其他兩個硬幣比較,最多比較兩次就可以找到偽幣。這樣,1 6硬幣的問題就被分為兩個8硬幣(A組和B組)的問題。通過比較這兩組硬幣的重量,可以判斷偽幣是否存在。如果沒有偽幣,則演算法終止。否則,繼續劃分這兩組硬幣來尋找偽幣。假設B是輕的那一組,因此再把它分成兩組,每組有4個硬幣。稱其中一組為B1,另一組為B2。比較這兩組,肯定有一組輕一些。如果B1輕,則偽幣在B1中,再將B1又分成兩組,每組有兩個硬幣,稱其中一組為B1a,另一組為B1b。比較這兩組,可以得到一個較輕的組。由於這個組只有兩個硬幣,因此不必再細分。比較組中兩個硬幣的重量,可以立即知道哪一個硬幣輕一些。較輕的硬幣就是所要找的偽幣。 在n個元素中找出最大元素和最小元素。我們可以把這n個元素放在一個數組中,用直接比較法求出。演算法如下:
void maxmin1(int A[],int n,int *max,int *min)
{ int i;
*min=*max=A[0];
for(i=0;i <= n;i++)
{ if(A[i]> *max) *max= A[i];
if(A[i] < *min) *min= A[i];
}
}
上面這個演算法需比較2(n-1)次。能否找到更好的演算法呢?我們用分治策略來討論。
把n個元素分成兩組:
A1={A[1],...,A[int(n/2)]}和A2={A[INT(N/2)+1],...,A[N]}
分別求這兩組的最大值和最小值,然後分別將這兩組的最大值和最小值相比較,求出全部元素的最大值和最小值。如果A1和A2中的元素多於兩個,則再用上述方法各分為兩個子集。直至子集中元素至多兩個元素為止。
例如有下面一組元素:-13,13,9,-5,7,23,0,15。用分治策略比較的演算法如下:
void maxmin2(int A[],int i,int j,int *max,int *min)
/*A存放輸入的數據,i,j存放數據的范圍,初值為0,n-1,*max,*min 存放最大和最小值*/
{ int mid,max1,max2,min1,min2;
if (j==i) {最大和最小值為同一個數;return;}
if (j-1==i) {將兩個數直接比較,求得最大會最小值;return;}
mid=(i+j)/2;
求i~mid之間的最大最小值分別為max1,min1;
求mid+1~j之間的最大最小值分別為max2,min2;
比較max1和max2,大的就是最大值;
比較min1和min2,小的就是最小值;
} 題目:在一個(2^k)*(2^k)個方格組成的棋盤上,有一個特殊方格與其他方格不同,稱為特殊方格,稱這樣的棋盤為一個特殊棋盤。我們要求對棋盤的其餘部分用L型方塊填滿(註:L型方塊由3個單元格組成。即圍棋中比較忌諱的愚形三角,方向隨意),且任何兩個L型方塊不能重疊覆蓋。L型方塊的形態如下:
題目的解法使用分治法,即子問題和整體問題具有相同的形式。我們對棋盤做一個分割,切割一次後的棋盤如圖1所示,我們可以看到棋盤被切成4個一樣大小的子棋盤,特殊方塊必定位於四個子棋盤中的一個。假設如圖1所示,特殊方格位於右上角,我們把一個L型方塊(灰色填充)放到圖中位置。這樣對於每個子棋盤又各有一個「特殊方塊」,我們對每個子棋盤繼續這樣分割,直到子棋盤的大小為1為止。
用到的L型方塊需要(4^k-1)/3 個,演算法的時間是O(4^k),是漸進最優解法。
本題目的C語言的完整代碼如下(TC2.0下調試),運行時,先輸入k的大小,(1<=k<=6),然後分別輸入特殊方格所在的位置(x,y), 0<=x,y<=(2^k-1)。 #include<stdio.h>//#include<conio.h>//#include<math.h>inttitle=1;intboard[64][64];voidchessBoard(inttr,inttc,intdr,intdc,intsize){ints,t;if(size==1)return;t=title++;s=size/2;if(dr<tr+s&&dc<tc+s)chessBoard(tr,tc,dr,dc,s);else{board[tr+s-1][tc+s-1]=t;chessBoard(tr,tc,tr+s-1,tc+s-1,s);}if(dr<tr+s&&dc>=tc+s)chessBoard(tr,tc+s,dr,dc,s);else{board[tr+s-1][tc+s]=t;chessBoard(tr,tc+s,tr+s-1,tc+s,s);}if(dr>=tr+s&&dc<tc+s)chessBoard(tr+s,tc,dr,dc,s);else{board[tr+s][tc+s-1]=t;chessBoard(tr+s,tc,tr+s,tc+s-1,s);}if(dr>=tr+s&&dc>=tc+s)chessBoard(tr+s,tc+s,dr,dc,s);else{board[tr+s][tc+s]=t;chessBoard(tr+s,tc+s,tr+s,tc+s,s);}}voidmain(){intdr=0,dc=0,s=1,i=0,j=0;printf(printinthesizeofchess: );scanf(%d,&s);printf(printinspecalpointx,y: );scanf(%d%d,&dr,&dc);if(dr<s&&dc<s){chessBoard(0,0,dr,dc,s);for(i=0;i<s;i++){for(j=0;j<s;j++){printf(%4d,board[i][j]);}printf( );}}elseprintf(thewrongspecalpoint!! );getch();}

⑤ 背包問題用分治演算法怎麼解決

分治演算法的基本思想是將一個規模為N的問題分解為K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。

我的想法是,比如背包承重是m,物品t[]分別是t1,t2,...tn,
演算法pakage(m,t[],p[]),最簡單的演算法思路就是把物品ti(1<=i<=n)放入背包,然後問題就變成在背包m-ti的承重下,盛放物品newt[](newt[]=t[]-ti),用遞歸方法可以很簡單的描述,最後p[]就是得到的結果.但是這么做演算法復雜度非常高,所以必須優化。只是給你提供一個思路,希望有用
pakage(m,t[],p[]){
if(m==0||t.length()==0){
return;
}
if(null!=t){
for(int i=0;i<t.length;i++){
//把t[i]放入背包
//newt[]=t-t[i];
package(m-t[i],newt[],p[]);
}
}
}

⑥ 分治演算法是什麼呢

分治演算法的基本思想是將一個規模為N的問題分解為K個規模較小的子問題,這些子問題相互獨立且與原問題性質相同。求出子問題的解,就可得到原問題的解。即一種分目標完成程序演算法,簡單問題可用二分法完成。

解題步驟

分治法解題的一般步驟:

(1)分解,將要解決的問題劃分成若干規模較小的同類問題;

(2)求解,當子問題劃分得足夠小時,用較簡單的方法解決;

(3)合並,按原問題的要求,將子問題的解逐層合並構成原問題的解。

⑦ 分治演算法幾個經典例子

分治法,字面意思是「分而治之」,就是把一個復雜的1問題分成兩個或多個相同或相似的子問題,再把子問題分成更小的子問題直到最後子問題可以簡單地直接求解,原問題的解即子問題的解的合並,這個思想是很多高效演算法的基礎。

圖二

大整數乘法

Strassen矩陣乘法

棋盤覆蓋

合並排序

快速排序

線性時間選擇

最接近點對問題

循環賽日程表

漢諾塔

熱點內容
sqlwhereor效率 發布:2025-03-10 09:57:06 瀏覽:103
12306java 發布:2025-03-10 09:52:39 瀏覽:602
php鍵值 發布:2025-03-10 09:51:14 瀏覽:629
FTP對吧 發布:2025-03-10 09:50:28 瀏覽:94
單機游戲怎麼解壓安裝 發布:2025-03-10 09:49:41 瀏覽:672
我的世界所有伺服器都能刷tnt么 發布:2025-03-10 09:25:41 瀏覽:288
相冊加密oppo 發布:2025-03-10 09:06:45 瀏覽:615
計程車網上學習登錄密碼多少 發布:2025-03-10 09:06:43 瀏覽:670
保險公司工資怎麼演算法 發布:2025-03-10 09:03:50 瀏覽:144
yy模擬器源碼 發布:2025-03-10 09:00:47 瀏覽:770