算法 算法分析
本書是基於算法導論第一節課的總結。主要總結算法分析中的時間複雜度,並通過排序算法中的插入排序和歸併排序來分析。文章中的代碼由go語言實現
算法的時間複雜度的計算
分析算法運行的快慢,主要有以下幾個方面決定:輸入數據的大小(data size),輸入數據的好壞,在什麼樣的平臺上運行。一個好的程序或者軟件,需要對輸入的對象有一定的研究。比如排序算法,在size<30的情況下插入排序是最爲理想的排序算法,當size>100之後,歸併排序的速度就非常的具有優勢了。因此在當前高級語言的成熟的排序算法中,對於一個大數據量文本的排序,在裏面處理小數據量片段的時候,會使用插入排序來實現。
考慮到以上幾個方面的差異,在算法研究中,有一個很重要的思想,漸近分析(asymptotic analysis) 因爲對於 和,即使後者運行在超級計算機上,前者運行在普通計算機上,當n大到一定程度之後,會比快。這就是漸近分析的作用,忽略了機器常數,又考慮了算法運行時間的增長。
插入排序
對於一個,將其進行排序,
先看排序問題的僞代碼:
for j = 1 to n-1
do key = a[j]
i <- j-1
while i > 0 && a[i] > key
do a[i+1] <- a[i]
i <- i-1
a[i+1] = key
下面是使用go語言實現的排序算法:
func InsertSort(a []int) {
length := len(a)
if length < 2{
return
}
for j := 1; j < length; j++{
key := a[j]
i := j-1
for i >= 0 && a[i] > key{
a[i+1] = a[i]
i--
}
a[i+1] = key
}
}
像這樣的代碼,我們可以得到排除掉時間常數,每次循環需要最多需要遍歷的次數爲j,所以因此其時間複雜度爲
歸併排序
同樣對於上面的代碼,使用歸併排序的僞代碼爲:
if n = 1 done
other recursirely sorting two part and merging A[0...n/2-1] and A[n/2...n-1]
devide the left and right part to smaller part, until n=1
下面是使用go語言實現的歸併排序算法:
func mergeSort(a []int, left, right int) {
if left == right{
return
}
mid := (left+right)/2
mergeSort(a, left, mid)
mergeSort(a, mid+1, right)
var temp []int
temp = append(temp, a[left:right+1]...)
tempMid := (len(temp)-1)/2
tempRight := len(temp)-1
i, j:= 0, tempMid+1
for i <= tempMid && j <= tempRight{
if temp[i] > temp[j]{
a[left] = temp[j];left++;j++
}else{
a[left] = temp[i];left++;i++
}
}
if j > tempRight{
a[left] = temp[i];left++;i++
}else if(i > tempMid) {
a[left] = temp[j];left++;j++
}
}
func MergeSort(a []int){
length := len(a)
if length <= 1 {return}
mergeSort(a, 0, length-1)
}
我們來考慮將兩個有序數組合爲一個有序數組的過程,是需要變量這兩個數組的所有元素的,因此在遞歸樹的每一層所需要的時間損耗爲 遞歸樹有多少層呢 ? 因此算法的時間複雜度爲 這個算法是比上面的插入排序在大數據量的情況下,是具有非常大的優勢的。
兩種算法的綜合
需要聲明的是,成熟的排序算法,會使用到快速排序等其他的排序算法。本文只是將歸併和插入結合起來,也就是在數據量小於一定值之後,使用插入排序:
const thresholdValue int = 8
func mergeSort(a []int, left, right int) {
if left == right{
return
}
if right - left > thresholdValue{
InsertSort(a[left:right+1])
}
.........
}
藉助於go語言test的支持,我們可以更爲直接的得到程序運行的情況
package main
import "testing"
func BenchmarkMergeSort(b *testing.B) {
testData := make([]int, 100000)
for i := 0; i < 100000; i++{
testData[i] = rand.Intn(100000)
}
for i:= 0; i < b.N ;i++ {
MergeSort(testData)
}
}
func BenchmarkInsertSort(b *testing.B) {
testData := make([]int, 100000)
for i := 0; i < 100000; i++{
testData[i] = rand.Intn(100000)
}
for i:= 0; i < b.N ;i++ {
InsertSort(testData)
}
}
//運行結果
BenchmarkMergeSort-4 132 9127102 ns/op 14312409 B/op 99999 allocs/op
PASS
ok github.com/exerciseOfGolang/sorttest 1.590s
BenchmarkInsertSort-4 1 2040541200 ns/op 803208 B/op 3 allocs/op
PASS
ok github.com/exerciseOfGolang/sorttest 1.673s