01算法-時間複雜度分析

算法 算法分析

本書是基於算法導論第一節課的總結。主要總結算法分析中的時間複雜度,並通過排序算法中的插入排序和歸併排序來分析。文章中的代碼由go語言實現

算法的時間複雜度的計算

分析算法運行的快慢,主要有以下幾個方面決定:輸入數據的大小(data size),輸入數據的好壞,在什麼樣的平臺上運行。一個好的程序或者軟件,需要對輸入的對象有一定的研究。比如排序算法,在size<30的情況下插入排序是最爲理想的排序算法,當size>100之後,歸併排序的速度就非常的具有優勢了。因此在當前高級語言的成熟的排序算法中,對於一個大數據量文本的排序,在裏面處理小數據量片段的時候,會使用插入排序來實現。
考慮到以上幾個方面的差異,在算法研究中,有一個很重要的思想,漸近分析(asymptotic analysis) 因爲對於 θ(n)\theta(n)θ(n2)\theta(n^2),即使後者運行在超級計算機上,前者運行在普通計算機上,當n大到一定程度之後,θ(n)\theta(n)會比θ(n2)\theta(n^2)快。這就是漸近分析的作用,忽略了機器常數,又考慮了算法運行時間的增長。

插入排序

對於一個(a0,a1...an1)(a_0, a_1...a_{n-1}),將其進行排序,
先看排序問題的僞代碼:

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,所以T(n)=1+2+....n1T(n)=1+2+....n-1因此其時間複雜度爲θ(n2)\theta(n^2)

歸併排序

同樣對於上面的代碼,使用歸併排序的僞代碼爲:

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)
}

我們來考慮將兩個有序數組合爲一個有序數組的過程,是需要變量這兩個數組的所有元素的,因此在遞歸樹的每一層所需要的時間損耗爲θ(n)\theta(n) 遞歸樹有多少層呢 ? lg(n)\lg(n) 因此算法的時間複雜度爲θ(nlg(n))\theta(n*\lg(n)) 這個算法是比上面的插入排序在大數據量的情況下,是具有非常大的優勢的。

兩種算法的綜合

需要聲明的是,成熟的排序算法,會使用到快速排序等其他的排序算法。本文只是將歸併和插入結合起來,也就是在數據量小於一定值之後,使用插入排序:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章