二分查找的一個bug隱藏40年

做爲一個程序員,寫bug不是什麼丟臉的事。可是須要吃一塹長一智,不要在一樣的地方再犯錯誤就行。程序員

前言

二分查找,做爲程序員再熟悉不過了。它是由John Mauchly在1946年第一次參加Moore School Lectures(電子計算機原理及技術課程)提出的。John本身在1986年發現他的二分查找算法中存在一個bug。算法

那麼,這究竟是個怎樣的bug呢?你們能夠先本身實現一個二分查找算法,看你會不會踩坑markdown

二分查找

這裏貼一段go版本的二分查找算法ui

// 二分查找,找第一個目標數字的下標,沒有則返回-1
func binarySearch(arr []int64, target int64) int {
	if len(arr) == 0 {
		return -1
	}

	begin, end := 0, len(arr)-1
	for begin <= end {
		mid := (begin + end) / 2
		if arr[mid] > target {
			end = mid - 1
		} else if arr[mid] < target {
			begin = mid + 1
		} else {
			return mid
		}
	}

	return -1
}
複製代碼

問題分析

乍一看好像沒有問題,那問題究竟出在哪裏呢?spa

這裏涉及到計算機組成原理的知識。每一個操做系統能表示的整形位數是有限的,超過這個位數的數字計算機是表示不出來的,說到這裏,你再回頭看看呢?是否是發現了問題?操作系統

沒錯,問題就出在 mid := (begin + end) / 2,這裏mid大小就容易超過計算機的限制。code

go語言int64最大能表示的數字是 (i<<63 - 1)orm

func main() {
	i1 := 1<<63-1
	i2 := 1
	mid := (i1+i2) / 2
	fmt.Println("i1=", i1)
	fmt.Println("i2=", i2)
	fmt.Println("mid=", mid)
}

結果:
i1= 9223372036854775807
i2= 1
mid= -4611686018427387904
複製代碼

解決方法

既然加法不行,那就改爲減法get

func main() {
	i1 := 1<<63-1
	i2 := 1
	mid := i2 + (i1-i2) / 2
	fmt.Println("i1=", i1)
	fmt.Println("i2=", i2)
	fmt.Println("mid=", mid)
}

結果:
i1= 9223372036854775807
i2= 1
mid= 4611686018427387904
複製代碼

擴展

其實這不算是算法的bug,算法自己沒有問題,只是不一樣的計算機上不一樣的程序實現會產生bug。這種加法溢出的問題只要是兩個數字相加都由可能出現溢出問題,因此你們在寫程序的時候要注意,吃一塹長一智,才能不斷地提高本身string

相關文章
相關標籤/搜索