1. 請問遞歸演算法的時間復雜度如何計算呢
遞歸演算法的時間復雜度在演算法中,當一個演算法中包含遞歸調用時,其時間復雜度的分析會轉化為一個遞歸方程求解,常用以下四種方法:
代入法的基本步驟是先推測遞歸方程的顯式解,然後用數學歸納法來驗證該解是否合理。
2.遞歸程序設計是程序設計中常用的一種方法,它可以解決所有有遞歸屬性的問題,並且是行之有效的.
3.但對於遞歸程序運行的效率比較低,無論是時間還是空間都比非遞歸程序更費,若在程序中消除遞歸調用,則其運行時間可大為節省.
2. 冒泡排序法和快速排序比較的演算法
打你屁股,這么簡單的問題都不認真研究一下。
冒泡排序是最慢的排序,時間復雜度是 O(n^2)。
快速排序是最快的排序。關於快速排序,我推薦你看看《代碼之美》第二章:我編寫過的最漂亮的代碼。作者所說的最漂亮,就是指效率最高的。
--------------------------------摘自《代碼之美》---------------
當我撰寫關於分治(divide-and-conquer)演算法的論文時,我發現C.A.R. Hoare的Quicksort演算法(「Quicksort」,Computer Journal 5)無疑是各種Quicksort演算法的鼻祖。這是一種解決基本問題的漂亮演算法,可以用優雅的代碼實現。我很喜歡這個演算法,但我總是無法弄明白演算法中最內層的循環。我曾經花兩天的時間來調試一個使用了這個循環的復雜程序,並且幾年以來,當我需要完成類似的任務時,我會很小心地復制這段代碼。雖然這段代碼能夠解決我所遇到的問題,但我卻並沒有真正地理解它。
我後來從Nico Lomuto那裡學到了一種優雅的劃分(partitioning)模式,並且最終編寫出了我能夠理解,甚至能夠證明的Quicksort演算法。William Strunk Jr.針對英語所提出的「良好的寫作風格即為簡練」這條經驗同樣適用於代碼的編寫,因此我遵循了他的建議,「省略不必要的字詞」(來自《The Elements of Style》一書)。我最終將大約40行左右的代碼縮減為十幾行的代碼。因此,如果要回答「你曾編寫過的最漂亮代碼是什麼?」這個問題,那麼我的答案就是:在我編寫的《Programming Pearls, Second Edition》(Addison-Wesley)一書中給出的Quichsort演算法。在示例2-1中給出了用C語言編寫的Quicksort函數。我們在接下來的章節中將進一步地研究和改善這個函數。
【示例】 2-1 Quicksort函數
void quicksort(int l, int u)
{ int i, m;
if (l >= u) return; 10
swap(l, randint(l, u));
m = l;
for (i = l+1; i <= u; i++)
if (x[i] < x[l])
swap(++m, i);
swap(l, m);
quicksort(l, m-1);
quicksort(m+1, u);
}
如果函數的調用形式是quicksort(0, n-1),那麼這段代碼將對一個全局數組x[n]進行排序。函數的兩個參數分別是將要進行排序的子數組的下標:l是較低的下標,而u是較高的下標。函數調用swap(i,j)將會交換x[i]與x[j]這兩個元素。第一次交換操作將會按照均勻分布的方式在l和u之間隨機地選擇一個劃分元素。
在《Programming Pearls》一書中包含了對Quicksort演算法的詳細推導以及正確性證明。在本章的剩餘內容中,我將假設讀者熟悉在《Programming Pearls》中所給出的Quicksort演算法以及在大多數初級演算法教科書中所給出的Quicksort演算法。
如果你把問題改為「在你編寫那些廣為應用的代碼中,哪一段代碼是最漂亮的?」我的答案還是Quicksort演算法。在我和M. D. McIlroy一起編寫的一篇文章("Engineering a sort function," Software-Practice and Experience, Vol. 23, No. 11)中指出了在原來Unix qsort函數中的一個嚴重的性能問題。隨後,我們開始用C語言編寫一個新排序函數庫,並且考慮了許多不同的演算法,包括合並排序(Merge Sort)和堆排序(Heap Sort)等演算法。在比較了Quicksort的幾種實現方案後,我們著手創建自己的Quicksort演算法。在這篇文章中描述了我們如何設計出一個比這個演算法的其他實現要更為清晰,速度更快以及更為健壯的新函數——部分原因是由於這個函數的代碼更為短小。Gordon Bell的名言被證明是正確的:「在計算機系統中,那些最廉價,速度最快以及最為可靠的組件是不存在的。」現在,這個函數已經被使用了10多年的時間,並且沒有出現任何故障。
考慮到通過縮減代碼量所得到的好處,我最後以第三種方式來問自己在本章之初提出的問題。「你沒有編寫過的最漂亮代碼是什麼?」。我如何使用非常少的代碼來實現大量的功能?答案還是和Quicksort有關,特別是對這個演算法的性能分析。我將在下一節給出詳細介紹。
2.2 事倍功半
Quicksort是一種優雅的演算法,這一點有助於對這個演算法進行細致的分析。大約在1980年左右,我與Tony Hoare曾經討論過Quicksort演算法的歷史。他告訴我,當他最初開發出Quicksort時,他認為這種演算法太簡單了,不值得發表,而且直到能夠分析出這種演算法的預期運行時間之後,他才寫出了經典的「Quicksoft」論文。
我們很容易看出,在最壞的情況下,Quicksort可能需要n2的時間來對數組元素進行排序。而在最優的情況下,它將選擇中值作為劃分元素,因此只需nlgn次的比較就可以完成對數組的排序。那麼,對於n個不同值的隨機數組來說,這個演算法平均將進行多少次比較?
Hoare對於這個問題的分析非常漂亮,但不幸的是,其中所使用的數學知識超出了大多數程序員的理解范圍。當我為本科生講授Quicksort演算法時,許多學生即使在費了很大的努力之後,還是無法理解其中的證明過程,這令我非常沮喪。下面,我們將從Hoare的程序開
11
始討論,並且最後將給出一個與他的證明很接近的分析。
我們的任務是對示例2-1中的Quicksort代碼進行修改,以分析在對元素值均不相同的數組進行排序時平均需要進行多少次比較。我們還將努力通過最短的代碼、最短運行時間以及最小存儲空間來得到最深的理解。
為了確定平均比較的次數,我們首先對程序進行修改以統計次數。因此,在內部循環進行比較之前,我們將增加變數comps的值(參見示例2-2)。
【示例2-2】 修改Quicksort的內部循環以統計比較次數。
for (i = l+1; i <= u; i++) {
comps++;
if (x[i] < x[l])
swap(++m, i);
}
如果用一個值n來運行程序,我們將會看到在程序的運行過程中總共進行了多少次比較。如果重復用n來運行程序,並且用統計的方法來分析結果,我們將得到Quicksort在對n個元素進行排序時平均使用了1.4 nlgn次的比較。
在理解程序的行為上,這是一種不錯的方法。通過十三行的代碼和一些實驗可以反應出許多問題。這里,我們引用作家Blaise Pascal和T. S. Eliot的話,「如果我有更多的時間,那麼我給你寫的信就會更短。」現在,我們有充足的時間,因此就讓我們來對代碼進行修改,並且努力編寫出更短(同時更好)的程序。
我們要做的事情就是提高這個演算法的速度,並且盡量增加統計的精確度以及對程序的理解。由於內部循環總是會執行u-l次比較,因此我們可以通過在循環外部增加一個簡單的操作來統計比較次數,這就可以使程序運行得更快一些。在示例2-3的Quicksort演算法中給出了這個修改。
【示例2-3】 Quicksort的內部循環,將遞增操作移到循環的外部
comps += u-l;
for (i = l+1; i <= u; i++)
if (x[i] < x[l])
swap(++m, i);
這個程序會對一個數組進行排序,同時統計比較的次數。不過,如果我們的目標只是統計比較的次數,那麼就不需要對數組進行實際地排序。在示例2-4中去掉了對元素進行排序的「實際操作」,而只是保留了程序中各種函數調用的「框架」。
【示例2-4】將Quicksort演算法的框架縮減為只進行統計
void quickcount(int l, int u)
{ int m;
if (l >= u) return;
m = randint(l, u);
comps += u-l;
quickcount(l, m-1);
quickcount(m+1, u);
}
12
這個程序能夠實現我們的需求,因為Quichsort在選擇劃分元素時採用的是「隨機」方式,並且我們假設所有的元素都是不相等的。現在,這個新程序的運行時間與n成正比,並且相對於示例2-3需要的存儲空間與n成正比來說,現在所需的存儲空間縮減為遞歸堆棧的大小,即存儲空間的平均大小與lgn成正比。
雖然在實際的程序中,數組的下標(l和u)是非常重要的,但在這個框架版本中並不重要。因此,我們可以用一個表示子數組大小的整數(n)來替代這兩個下標(參見示例2-5)
【示例2-5】 在Quicksort代碼框架中使用一個表示子數組大小的參數
void qc(int n)
{ int m;
if (n <= 1) return;
m = randint(1, n);
comps += n-1;
qc(m-1);
qc(n-m);
}
現在,我們可以很自然地把這個過程整理為一個統計比較次數的函數,這個函數將返回在隨機Quicksort演算法中的比較次數。在示例2-6中給出了這個函數。
【示例2-6】 將Quicksort框架實現為一個函數
int cc(int n)
{ int m;
if (n <= 1) return 0;
m = randint(1, n);
return n-1 + cc(m-1) + cc(n-m);
}
在示例2-4、示例2-5和示例2-6中解決的都是相同的基本問題,並且所需的都是相同的運行時間和存儲空間。在後面的每個示例都對這些函數的形式進行了改進,從而比這些函數更為清晰和簡潔。
在定義發明家的矛盾(inventor's paradox)(How To Solve It, Princeton University Press)時,George Póllya指出「計劃越宏大,成功的可能性就越大。」現在,我們就來研究在分析Quicksort時的矛盾。到目前為止,我們遇到的問題是,「當Quicksort對大小為n的數組進行一次排序時,需要進行多少次比較?」我們現在將對這個問題進行擴展,「對於大小為n的隨機數組來說,Quichsort演算法平均需要進行多少次的比較?」我們通過對示例2-6進行擴展以引出示例2-7。
【示例2-7】 偽碼:Quicksort的平均比較次數
float c(int n)
if (n <= 1) return 0
sum = 0
for (m = 1; m <= n; m++)
sum += n-1 + c(m-1) + c(n-m)
return sum/n
如果在輸入的數組中最多隻有一個元素,那麼Quichsort將不會進行比較,如示例2-6
13
中所示。對於更大的n,這段代碼將考慮每個劃分值m(從第一個元素到最後一個,每個都是等可能的)並且確定在這個元素的位置上進行劃分的運行開銷。然後,這段代碼將統計這些開銷的總和(這樣就遞歸地解決了一個大小為m-1的問題和一個大小為n-m的問題),然後將總和除以n得到平均值並返回這個結果。
如果我們能夠計算這個數值,那麼將使我們實驗的功能更加強大。我們現在無需對一個n值運行多次來估計平均值,而只需一個簡單的實驗便可以得到真實的平均值。不幸的是,實現這個功能是要付出代價的:這個程序的運行時間正比於3n(如果是自行參考(self-referential)的,那麼用本章中給出的技術來分析運行時間將是一個很有趣的練習)。
示例2-7中的代碼需要一定的時間開銷,因為它重復計算了中間結果。當在程序中出現這種情況時,我們通常會使用動態編程來存儲中間結果,從而避免重復計算。因此,我們將定義一個表t[N+1],其中在t[n]中存儲c[n],並且按照升序來計算它的值。我們將用N來表示n的最大值,也就是進行排序的數組的大小。在示例2-8中給出了修改後的代碼。
【示例2-8】 在Quicksort中使用動態編程來計算
t[0] = 0
for (n = 1; n <= N; n++)
sum = 0
for (i = 1; i <= n; i++)
sum += n-1 + t[i-1] + t[n-i]
t[n] = sum/n
這個程序只對示例2-7進行了細微的修改,即用t[n]來替換c(n)。它的運行時間將正比於N2,並且所需的存儲空間正比於N。這個程序的優點之一就是:在程序執行結束時,數組t中將包含數組中從元素0到元素N的真實平均值(而不是樣本均值的估計)。我們可以對這些值進行分析,從而生成在Quichsort演算法中統計比較次數的計算公式。
我們現在來對程序做進一步的簡化。第一步就是把n-1移到循環的外面,如示例2-9所示。
【示例2-9】 在Quicksort中把代碼移到循環外面來計算
t[0] = 0
for (n = 1; n <= N; n++)
sum = 0
for (i = 1; i <= n; i++)
sum += t[i-1] + t[n-i]
t[n] = n-1 + sum/n
現在將利用對稱性來對循環做進一步的調整。例如,當n為4時,內部循環計算總和為:
t[0]+t[3] + t[1]+t[2] + t[2]+t[1] + t[3]+t[0]
在上面這些組對中,第一個元素增加而第二個元素減少。因此,我們可以把總和改寫為:
2 * (t[0] + t[1] + t[2] + t[3])
我們可以利用這種對稱性來得到示例2-10中的Quicksort。
【示例2-10】 在Quichsort中利用了對稱性來計算
t[0] = 0
14
for (n = 1; n <= N; n++)
sum = 0
for (i = 0; i < n; i++)
sum += 2 * t[i]
t[n] = n-1 + sum/n
然而,在這段代碼的運行時間中同樣存在著浪費,因為它重復地計算了相同的總和。此時,我們不是把前面所有的元素加在一起,而是在循環外部初始化總和並且加上下一個元素,如示例2-11所示。
【示例2-11】 在Quicksort中刪除了內部循環來計算
sum = 0; t[0] = 0
for (n = 1; n <= N; n++)
sum += 2*t[n-1]
t[n] = n-1 + sum/n
這個小程序確實很有用。程序的運行時間與N成正比,對於每個從1到N的整數,程序將生成一張Quicksort的估計運行時間表。
我們可以很容易地把示例2-11用表格來實現,其中的值可以立即用於進一步的分析。在2-1給出了最初的結果行。
表2-1 示例2-11中實現的表格輸出
N Sum t[n]
0 0 0
1 0 0
2 0 1
3 2 2.667
4 7.333 4.833
5 17 7.4
6 31.8 10.3
7 52.4 13.486
8 79.371 16.921
這張表中的第一行數字是用代碼中的三個常量來進行初始化的。下一行(輸出的第三行)的數值是通過以下公式來計算的:
A3 = A2+1 B3 = B2 + 2*C2 C3 = A2-1 + B3/A3
把這些(相應的)公式記錄下來就使得這張表格變得完整了。這張表格是「我曾經編寫的最漂亮代碼」的很好的證據,即使用少量的代碼完成大量的工作。
但是,如果我們不需要所有的值,那麼情況將會是什麼樣?如果我們更希望通過這種來方式分析一部分數值(例如,在20到232之間所有2的指數值)呢?雖然在示例2-11中構建了完整的表格t,但它只需要使用表格中的最新值。因此,我們可以用變數t的定長空間來替代table t[]的線性空間,如示例2-12所示。
【示例2-12】 Quicksoft 計算——最終版本
sum = 0; t = 0
15
for (n = 1; n <= N; n++)
sum += 2*t
t = n-1 + sum/n
然後,我們可以插入一行代碼來測試n的適應性,並且在必要時輸出這些結果。
這個程序是我們漫長學習旅途的終點。通過本章所採用的方式,我們可以證明Alan Perlis的經驗是正確的:「簡單性並不是在復雜性之前,而是在復雜性之後」 ("Epigrams on Programming," Sigplan Notices, Vol. 17, Issue 9)。
3. 求一個數據挖掘的演算法
試論貝葉斯分類、決策樹分類分類挖掘演算法的優勢與劣勢,以及解決維度效應的策略
引言 數據分類是指按照分析對象的屬性、特徵,建立不同的組類來描述事物。數據分類是數據挖掘的主要內容之一,主要是通過分析訓練數據樣本,產生關於類別的精確描述。這種類別通常由分類規則組成,可以用來對未來的數據進行分類和預測。分類技術解決問題的關鍵是構造分類器。 一.數據分類 數據分類一般是兩個步驟的過程: 第1步:建立一個模型,描述給定的數據類集或概念集(簡稱訓練集)。通過分析由屬性描述的資料庫元組來構造模型。每個元組屬於一個預定義的類,由類標號屬性確定。用於建立模型的元組集稱為訓練數據集,其中每個元組稱為訓練樣本。由於給出了類標號屬性,因此該步驟又稱為有指導的學習。如果訓練樣本的類標號是未知的,則稱為無指導的學習(聚類)。學習模型可用分類規則、決策樹和數學公式的形式給出。 第2步:使用模型對數據進行分類。包括評估模型的分類准確性以及對類標號未知的元組按模型進行分類。 常用的分類規則挖掘方法 分類規則挖掘有著廣泛的應用前景。對於分類規則的挖掘通常有以下幾種方法,不同的方法適用於不同特點的數據: 1.貝葉斯方法 2.決策樹方法 3.人工神經網路方法 4.約略集方法 5.遺傳演算法 分類方法的評估標准: 准確率:模型正確預測新數據類標號的能力。速度:產生和使用模型花費的時間。健壯性:有雜訊數據或空缺值數據時模型正確分類或預測的能力。伸縮性:對於給定的大量數據,有效地構造模型的能力。可解釋性:學習模型提供的理解和觀察的層次。 影響一個分類器錯誤率的因素 (1) 訓練集的記錄數量。生成器要利用訓練集進行學習,因而訓練集越大,分類器也就越可靠。然而,訓練集越大,生成器構造分類器的時間也就越長。錯誤率改善情況隨訓練集規模的增大而降低。 (2) 屬性的數目。更多的屬性數目對於生成器而言意味著要計算更多的組合,使得生成器難度增大,需要的時間也更長。有時隨機的關系會將生成器引入歧途,結果可能構造出不夠准確的分類器(這在技術上被稱為過分擬合)。因此,如果我們通過常識可以確認某個屬性與目標無關,則將它從訓練集中移走。 (3) 屬性中的信息。有時生成器不能從屬性中獲取足夠的信息來正確、低錯誤率地預測標簽(如試圖根據某人眼睛的顏色來決定他的收入)。加入其他的屬性(如職業、每周工作小時數和年齡),可以降低錯誤率。 (4) 待預測記錄的分布。如果待預測記錄來自不同於訓練集中記錄的分布,那麼錯誤率有可能很高。比如如果你從包含家用轎車數據的訓練集中構造出分類器,那麼試圖用它來對包含許多運動用車輛的記錄進行分類可能沒多大用途,因為數據屬性值的分布可能是有很大差別的。 評估方法 有兩種方法可以用於對分類器的錯誤率進行評估,它們都假定待預測記錄和訓練集取自同樣的樣本分布。 (1) 保留方法(Holdout):記錄集中的一部分(通常是2/3)作為訓練集,保留剩餘的部分用作測試集。生成器使用2/3 的數據來構造分類器,然後使用這個分類器來對測試集進行分類,得出的錯誤率就是評估錯誤率。雖然這種方法速度快,但由於僅使用2/3 的數據來構造分類器,因此它沒有充分利用所有的數據來進行學習。如果使用所有的數據,那麼可能構造出更精確的分類器。 (2) 交叉糾錯方法(Cross validation):數據集被分成k 個沒有交叉數據的子集,所有子集的大小大致相同。生成器訓練和測試共k 次;每一次,生成器使用去除一個子集的剩餘數據作為訓練集,然後在被去除的子集上進行測試。把所有得到的錯誤率的平均值作為評估錯誤率。交叉糾錯法可以被重復多次(t),對於一個t 次k 分的交叉糾錯法,k *t 個分類器被構造並被評估,這意味著交叉糾錯法的時間是分類器構造時間的k *t 倍。增加重復的次數意味著運行時間的增長和錯誤率評估的改善。我們可以對k 的值進行調整,將它減少到3 或5,這樣可以縮短運行時間。然而,減小訓練集有可能使評估產生更大的偏差。通常Holdout 評估方法被用在最初試驗性的場合,或者多於5000 條記錄的數據集;交叉糾錯法被用於建立最終的分類器,或者很小的數據集。 二.貝葉斯分類 貝葉斯分類方法是一種具有最小錯誤率的概率分類方法,可以用數學公式的精確方法表示出來,並且可以用很多種概率理論來解決。 設(Ω,Θ,P)為概率空間,Ai∈Θ(i=1,2,…,n)為Ω的一個有窮剖分,且P(Ai)>0 (i=1,2,…,n),則對任意B∈Θ且P(B)>0,有 P(Ai|B)= (i=1,2,…,n) 上式稱為貝葉斯公式。貝葉斯定理為我們提供了一個計算假設h的後驗概率的方法 P(h|D)= 分類有規則分類和非規則分類,貝葉斯分類是非規則分類,它通過訓練集訓練而歸納出分類器,並利用分類器對沒有分類的數據進行分類。 貝葉斯分類的特點貝葉斯分類具有如下特點: (1) 貝葉斯分類並不把一個對象絕對地指派給某一類,而是通過計算得出屬於某一類的概率,具有最大概率的類便是該對象所屬的類; (2) 一般情況下在貝葉斯分類中所有的屬性都潛在地起作用,即並不是一個或幾個屬性決定分類,而是所有的屬性都參與分類; (3) 貝葉斯分類對象的屬性可以是離散的、連續的,也可以是混合的。 貝葉斯定理給出了最小化誤差的最優解決方法,可用於分類和預測。理論上,它看起來很完美,但在實際中,它並不能直接利用,它需要知道證據的確切分布概率,而實際上我們並不能確切的給出證據的分布概率。因此我們在很多分類方法中都會作出某種假設以逼近貝葉斯定理的要求。 三.決策樹分類 決策樹(Decision Tree)又稱為判定樹,是運用於分類的一種樹結構。其中的每個內部結點(internal node)代表對某個屬性的一次測試,每條邊代表一個測試結果,葉結點(leaf)代表某個類(class)或者類的分布(class distribution),最上面的結點是根結點。決策樹分為分類樹和回歸樹兩種,分類樹對離散變數做決策樹,回歸樹對連續變數做決策樹。 構造決策樹是採用自上而下的遞歸構造方法。決策樹構造的結果是一棵二叉或多叉樹,它的輸入是一組帶有類別標記的訓練數據。二叉樹的內部結點(非葉結點)一般表示為一個邏輯判斷,如形式為(a = b)的邏輯判斷,其中a 是屬性,b是該屬性的某個屬性值;樹的邊是邏輯判斷的分支結果。多叉樹(ID3)的內部結點是屬性,邊是該屬性的所有取值,有幾個屬性值,就有幾條邊。樹的葉結點都是類別標記。 使用決策樹進行分類分為兩步: 第1步:利用訓練集建立並精化一棵決策樹,建立決策樹模型。這個過程實際上是一個從數據中獲取知識,進行機器學習的過程。 第2步:利用生成完畢的決策樹對輸入數據進行分類。對輸入的記錄,從根結點依次測試記錄的屬性值,直到到達某個葉結點,從而找到該記錄所在的類。 問題的關鍵是建立一棵決策樹。這個過程通常分為兩個階段: (1) 建樹(Tree Building):決策樹建樹演算法見下,可以看得出,這是一個遞歸的過程,最終將得到一棵樹。 (2) 剪枝(Tree Pruning):剪枝是目的是降低由於訓練集存在雜訊而產生的起伏。 決策樹方法的評價。 優點 與其他分類演算法相比決策樹有如下優點: (1) 速度快:計算量相對較小,且容易轉化成分類規則。只要沿著樹根向下一直走到葉,沿途的分裂條件就能夠唯一確定一條分類的謂詞。 (2) 准確性高:挖掘出的分類規則准確性高,便於理解,決策樹可以清晰的顯示哪些欄位比較重要。 缺點 一般決策樹的劣勢: (1) 缺乏伸縮性:由於進行深度優先搜索,所以演算法受內存大小限制,難於處理大訓練集。一個例子:在Irvine機器學習知識庫中,最大可以允許的數據集僅僅為700KB,2000條記錄。而現代的數據倉庫動輒存儲幾個G-Bytes的海量數據。用以前的方法是顯然不行的。 (2) 為了處理大數據集或連續量的種種改進演算法(離散化、取樣)不僅增加了分類演算法的額外開銷,而且降低了分類的准確性,對連續性的欄位比較難預測,當類別太多時,錯誤可能就會增加的比較快,對有時間順序的數據,需要很多預處理的工作。 但是,所用的基於分類挖掘的決策樹演算法沒有考慮雜訊問題,生成的決策樹很完美,這只不過是理論上的,在實際應用過程中,大量的現實世界中的數據都不是以的意願來定的,可能某些欄位上缺值(missing values);可能數據不準確含有雜訊或者是錯誤的;可能是缺少必須的數據造成了數據的不完整。 另外決策樹技術本身也存在一些不足的地方,例如當類別很多的時候,它的錯誤就可能出現甚至很多。而且它對連續性的欄位比較難作出准確的預測。而且一般演算法在分類的時候,只是根據一個屬性來分類的。 在有雜訊的情況下,完全擬合將導致過分擬合(overfitting),即對訓練數據的完全擬合反而不具有很好的預測性能。剪枝是一種克服雜訊的技術,同時它也能使樹得到簡化而變得更容易理解。另外,決策樹技術也可能產
4. C語言什麼是遞歸方法
編程裡面估計最讓人摸不著頭腦的基本演算法就是遞歸了。很多時候我們看明白一個復雜的遞歸都有點費時間,尤其對模型所描述的問題概念不清的時候,想要自己設計一個遞歸那麼就更是有難度了。今天我也花費了半個小時來搞明白二叉樹的平衡性的遞歸模型,首先我不明白什麼叫做平衡性,所以花費的時候大部分實在試探理解平衡性的含義。在搞明白的時候,我突然想到假如讓我來設計,在我知道平衡性的前提下,我是否可以建立如此簡潔的遞歸模型。為此,我遇到的問題是我們到底在什麼情況下適用遞歸模型,並且遞歸模型如何建立。
數學都不差的我們,第一反應就是遞歸在數學上的模型是什麼。畢竟我們對於問題進行數學建模比起代碼建模拿手多了。 (當然如果對於問題很清楚的人也可以直接簡歷遞歸模型了,運用數模做中介的是針對對於那些問題還不是很清楚的人)
自己觀察遞歸,我們會發現,遞歸的數學模型其實就是歸納法,這個在高中的數列裡面是最常用的了。回憶一下歸納法。
歸納法適用於想解決一個問題轉化為解決他的子問題,而他的子問題又變成子問題的子問題,而且我們發現這些問題其實都是一個模型,也就是說存在相同的邏輯歸納處理項。當然有一個是例外的,也就是遞歸結束的哪一個處理方法不適用於我們的歸納處理項,當然也不能適用,否則我們就無窮遞歸了。這里又引出了一個歸納終結點以及直接求解的表達式。如果運用列表來形容歸納法就是:
步進表達式:問題蛻變成子問題的表達式
結束條件:什麼時候可以不再是用步進表達式
直接求解表達式:在結束條件下能夠直接計算返回值的表達式
邏輯歸納項:適用於一切非適用於結束條件的子問題的處理,當然上面的步進表達式其實就是包含在這裡面了。
這樣其實就結束了,遞歸也就出來了。
遞歸演算法的一般形式:
voidfunc(mode)
{
if(endCondition)
{
constExpression//基本項
}
else
{
accumrateExpreesion/歸納項
mode=expression//步進表達式
func(mode)//調用本身,遞歸
}
}
最典型的就是N!演算法,這個最具有說服力。理解了遞歸的思想以及使用場景,基本就能自己設計了,當然要想和其他演算法結合起來使用,還需要不斷實踐與總結了。
例如:返回一個二叉樹的深度:
intdepth(Treet){
if(!t)return0;
else{
inta=depth(t.right);
intb=depth(t.left);
return(a>b)?(a+1):(b+1);
}
}
判斷一個二叉樹是否平衡:
intisB(Treet){
if(!t)return0;
intleft=isB(t.left);
intright=isB(t.right);
if(left>=0&&right>=0&&left-right<=1||left-right>=-1)
return(left<right)?(right+1):(left+1);
elsereturn-1;
}
上面這兩個遞歸的難易程度就不一樣了,第一個關於深度的遞歸估計只要了解遞歸思想的都可以短時間設計出來,但第二個估計就要長點時間了。純遞歸問題的難易主要糾結於一些條件表達式的構造以及初值的設置(上面的為直接表達式值的設定)。
最後需要補充的是,很多不理解遞歸的人,總認為遞歸完全沒必要,用循環就可以實現,其實這是一種很膚淺的理解。因為遞歸之所以在程序中能風靡並不是因為他的循環,大家都知道遞歸分兩步,遞和歸,那麼可以知道遞歸對於空間性能來說,簡直就是造孽,這對於追求時空完美的人來說,簡直無法接接受,如果遞歸僅僅是循環,估計現在我們就看不到遞歸了。遞歸之所以現在還存在是因為遞歸可以產生無限循環體,也就是說有可能產生100層也可能10000層for循環。例如對於一個字元串進行全排列,字元串長度不定,那麼如果你用循環來實現,你會發現你根本寫不出來,這個時候就要調用遞歸,而且在遞歸模型裡面還可以使用分支遞歸,例如for循環與遞歸嵌套,或者這節枚舉幾個遞歸步進表達式,每一個形成一個遞歸。
5. 請問如何做信道估計中的MMSE估計
MMSE估計就是最小均方誤差估計,通過求得一個合適的信道沖擊響應(CIR),使得通過CIR計算出的接收數據與實際數據的誤差的均方和最小。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
我上個月剛做過基於塊狀導頻信息的LTE物理層上行信道的頻域信道估計以及信道均衡。
部分演算法如下(以下是基於單載波的)
假設循環前綴已經消除了實踐彌散信道帶來的符號間干擾,保證了子載波之間的正交性。並且信道為慢衰落信道,在一個OFDM符號內,可以認為保持不變。
均衡器接收到的信號可以表示為
y(t)=x(t)*h(t)+n(t)
y(t)為均衡器接收到的信號,h(t)為系統等效的沖擊響應,x(t)為原始的輸入信號,n(t)為系統中的雜訊。
信道估計的任務就是在已知發送參考信息的情況下,對接受到的參考信息進行分析,選擇合適的演算法得到參考信息的信道沖擊響應,即h(t),而數據信息的信道沖擊響應則可以通過插值得到。
1) 最小二乘估計(LS)
該演算法的目的是
有正交性原理,則可得LS估計
該估計為無偏估計,每估計一個新到衰落系數只需一次乘法,缺點是受雜訊影響較大。
2) 線性最小均方誤差估計(MMSE)
LMMSE估計屬於統計估計,需要對信道的二階統計量進行估計,利用信道相關性可以置信道雜訊提高估計性能。以最小均方誤差(MMSE)為准則,如下式:
為了降低計算的復雜度,一般將 用它的期望值 代替,信道性能不會產生明顯惡化,則上式可變為
其中 為一個僅與調試的星座的大小有關的值, 為平均信噪比。
該演算法的復雜度較高,隨著X的改變, 須不斷更新。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
不知道你的是物理模型和數據結構是什麼樣的,頻域估計還是時域估計,基於導頻信息還是盲信道估計?
____________________________________________
有點悲劇,Word裡面的公式我不知道怎麼插進來
6. 遞歸演算法和非遞歸演算法在分析時間復雜度和空間復雜度上為什麼不同
在演算法分析中,當一個演算法中包含遞歸調用時,其時間復雜度的分析會轉化為一個遞歸方程求解。實際上,這個問題是數學上求解漸近階的問題,而遞歸方程的形式多種多樣,其求解方法也是不一而足,比較常用的有以下四種方法:
(1)代入法(Substitution Method)
代入法的基本步驟是先推測遞歸方程的顯式解,然後用數學歸納法來驗證該解是否合理。
(2)迭代法(Iteration Method)
迭代法的基本步驟是迭代地展開遞歸方程的右端,使之成為一個非遞歸的和式,然後通過對和式的估計來達到對方程左端即方程的解的估計。
(3)套用公式法(Master Method)
這個方法針對形如「T(n) = aT(n/b) + f(n)」的遞歸方程。這種遞歸方程是分治法的時間復雜性所滿足的遞歸關系,即一個規模為n的問題被分成規模均為n/b的a個子問題,遞歸地求解這a個子問題,然後通過對這a個子間題的解的綜合,得到原問題的解。
(4)差分方程法(Difference Formula Method)
可以將某些遞歸方程看成差分方程,通過解差分方程的方法來解遞歸方程,然後對解作出漸近階估計。
下面就以上方法給出一些例子說明。
一、代入法
大整數乘法計算時間的遞歸方程為:T(n) = 4T(n/2) + O(n),其中T(1) = O(1),我們猜測一個解T(n) = O(n2 ),根據符號O的定義,對n>n0,有T(n) < cn2 - eO(2n)(注意,這里減去O(2n),因其是低階項,不會影響到n足夠大時的漸近性),把這個解代入遞歸方程,得到:
T(n) = 4T(n/2) + O(n)
≤ 4c(n/2)2 - eO(2n/2)) + O(n)
= cn2 - eO(n) + O(n)
≤ cn2
其中,c為正常數,e取1,上式符合 T(n)≤cn2 的定義,則可認為O(n2 )是T(n)的一個解,再用數學歸納法加以證明。
二、迭代法
某演算法的計算時間為:T(n) = 3T(n/4) + O(n),其中T(1) = O(1),迭代兩次可將右端展開為:
T(n) = 3T(n/4) + O(n)
= O(n) + 3( O(n/4) + 3T(n/42 ) )
= O(n) + 3( O(n/4) + 3( O(n/42 ) + 3T(n/43 ) ) )
從上式可以看出,這是一個遞歸方程,我們可以寫出迭代i次後的方程:
T(n) = O(n) + 3( O(n/4) + 3( O(n/42 ) + ... + 3( n/4i + 3T(n/4i+1 ) ) ) )
當n/4i+1 =1時,T(n/4i+1 )=1,則
T(n) = n + (3/4) + (32 /42 )n + ... + (3i /4i )n + (3i+1 )T(1)
< 4n + 3i+1
而由n/4i+1 =1可知,i<log4 n,從而
3i+1 ≤ 3log4 n+1 = 3log3 n*log4 3 +1 = 3nlog4 3
代入得:
T(n) < 4n + 3nlog4 3,即T(n) = O(n)。
三、套用公式法
這個方法為估計形如:
T(n) = aT(n/b) + f(n)
其中,a≥1和b≥1,均為常數,f(n)是一個確定的正函數。在f(n)的三類情況下,我們有T(n)的漸近估計式:
1.若對於某常數ε>0,有f(n) = O(nlogb a-ε ),則T(n) = O(nlogb a )
2.若f(n) = O(nlogb a ),則T(n) = O(nlogb a *logn)
3.若f(n) = O(nlogb a+ε ),且對於某常數c>1和所有充分大的正整數n,有af(n/b)≤cf(n),則T(n)=O(f(n))。
設T(n) = 4T(n/2) + n,則a = 4,b = 2,f(n) = n,計算得出nlogb a = nlog2 4 = n2 ,而f(n) = n = O(n2-ε ),此時ε= 1,根據第1種情況,我們得到T(n) = O(n2 )。
這里涉及的三類情況,都是拿f(n)與nlogb a 作比較,而遞歸方程解的漸近階由這兩個函數中的較大者決定。在第一類情況下,函數nlogb a 較大,則T(n)=O(nlogb a );在第三類情況下,函數f(n)較大,則T(n)=O(f (n));在第二類情況下,兩個函數一樣大,則T(n)=O(nlogb a *logn),即以n的對數作為因子乘上f(n)與T(n)的同階。
但上述三類情況並沒有覆蓋所有可能的f(n)。在第一類情況和第二類情況之間有一個間隙:f(n)小於但不是多項式地小於nlogb a ,第二類與第三類之間也存在這種情況,此時公式法不適用。
7. 八皇後遞歸演算法的復雜性
期盤是8*8的么?
八皇後採用回溯的方法,其實就是一種效率比較低下的方法,採用深度優先遍歷的方法進行(dfs),如果想問更多歡迎在我blog留言,我天天上的.
下面說正事:
如果是棋盤的長度n=8的話應該是O(n^16),但事實上應該比這快很多,因為O(n^16)會成一個很小的系數,為什麼會這樣呢,比如第一個頂點要考慮8*8的情況,在確定第二個頂點的時候就是小於7*7的情況了,但是演算法時間復雜度只是處略的估計,不計較系數,而且n也不大.
要是還有疑慮可以在hi上給我留言.
8. 能詳細解釋一下值雜訊演算法嗎最好通俗易懂,越清晰越好。
摘要 柏林雜訊是一個非常強大演算法,經常用於程序生成隨機內容,在游戲和其他像電影等多媒體領域廣泛應用。演算法發明者Ken Perlin也因此演算法獲得奧斯卡科技成果獎(靠演算法拿奧斯卡也是沒誰了666)。本文將剖析他於2002年發表的改進版柏林雜訊演算法。在游戲開發領域,柏林雜訊可以用於生成波形,起伏不平的材質或者紋理。例如,它能用於程序生成地形(例如使用柏林雜訊來生成我的世界(Minecraft)里的地形),火焰燃燒特效,水和雲等等。柏林雜訊絕大部分應用在2維,3維層面上,但某種意義上也能拓展到4維。柏林雜訊在1維層面上可用於卷軸地形、模擬手繪線條等。
9. 雜訊估計的方法
介紹幾種常見的單通道雜訊估計演算法。雜訊估計主要基於以下三個現象。
(1)在音頻信號中,閉塞因閉合段頻譜能量趨於0或者接近雜訊水平。雜訊在頻譜上分布不均勻,不同的頻帶具有不同的SNR.對於任意類型雜訊,只要該頻帶無語音的概率很高或者SNR很低,則可以估計/更新該頻帶的雜訊譜,這類思想是遞歸平均雜訊估計演算法(the recursive-averaging type of noise-estimation algorithms)的支撐點。
(2)即使在語音活動的區域,帶噪語音信號在單個頻帶的功率通常會衰減到雜訊的功率水平,我們因此可以追蹤在短時窗內(0.4~1s)帶噪語音譜每個頻帶的最小值,實現各個頻帶雜訊的估計。該現象是最小值跟蹤演算法(the minima-tracking algorithms)的支撐點。
(3)每個頻帶能量的直方圖揭示了一個理論:出現頻次最高的值對應頻帶的雜訊水平。有時譜能量直方圖有兩種模式:1)低能量對應無聲段、語音的低能量段;2)高能量模式對應(noisy)語音的濁音段。低能量成分大於高能量成分。
因而總結出三類雜訊估計演算法
1、遞歸平均雜訊演算法
2、最小值跟蹤演算法
3、直方圖雜訊估計演算法
10. 雜訊方差估計怎麼計算
對於雜訊估計:
1.首先雜訊估計肯定是要用參考信號來估計的。參考信號就是說一種收發雙方都已知的滿足某種特殊特性的信號--比如良好的相關特性的信號;
2.然後接受信號的能量可以算出來吧;
3.再用LS演算法對接收信號做一個簡單的信道估計,再用這個信道估計值重構接收信號(參考信號已知,信道估計已知,把參考信號到接收端的過程走一遍,注意頻偏等影響);計算重構信號的能量;
4. 接收信號能量減去重構信號的能量就是雜訊了;
5. 雜訊就可以用於MMSE信道估計了...
當然這是一種最簡單的計算雜訊的方法,實際中對雜訊的處理比這個要復雜得多。