Мы все знаем, что бинарный поиск — один из самых быстрых способов поиска цели в отсортированном списке. И есть два способа реализовать это либо с помощью итеративного подхода, либо с помощью рекурсивного подхода. В этой истории мы рассмотрим рекурсивный подход, и одна из проблем использования рекурсивной функции заключается в том, что она требует больше времени ( O (2 ^ N) ), поскольку каждый вызов функции вызывает себя дважды, если только он не был рекурсирован n раз.
Тогда почему мы не можем сделать его немного быстрее, просто разбив операторы return в рекурсивных вызовах > 🤔.
Ну вот…!!
реализация бинарного поиска с использованием подпрограмм go
func implementBinarySearchUsingRoutines(arr []int, target int) { result := make(chan int) go binarySearchUsingGoRoutines(arr, target, 0, len(arr)-1, result) // it will block the code until search function sends some response index := <-result if index != -1 { fmt.Println(target, " target found at", index) } else { fmt.Println(target, " target not found") } } func binarySearchUsingGoRoutines(arr []int, target, low, high int, result chan int) { if low > high { result <- -1 return } mid := (low + high) / 2 if arr[mid] == target { result <- mid return } if arr[mid] > target { go binarySearchUsingGoRoutines(arr, target, low, mid-1, result) } else { go binarySearchUsingGoRoutines(arr, target, mid+1, high, result) } }
вместо того, чтобы возвращать функцию, мы можем запустить ту же функцию в отдельной процедуре go, чтобы повысить ее производительность 🙂.
Теперь мы сравним временную и пространственную сложность рекурсивной и рутинной реализации.
package main import ( "fmt" "math/rand" "runtime" "time" ) func main() { arr, target := generateArrayAndTarget() // set max usage cores as 1 runtime.GOMAXPROCS(1) start := time.Now() fmt.Println("starting implementation of Binary Search recursively ") implementBinarySearchRecursively(arr, target) printMemUsage() fmt.Println("time taken in nano seconds:-",time.Since(start).Nanoseconds()) // run garbage collector to free up unused memory runtime.GC() start = time.Now() fmt.Println("starting implementation of Binary Search Using Routines ") implementBinarySearchUsingRoutines(arr, target) printMemUsage() fmt.Println("time taken in nano seconds:-",time.Since(start).Nanoseconds()) } func implementBinarySearchRecursively(arr []int, target int) { index := binarySearch(arr, target, 0, len(arr)-1) if index != -1 { fmt.Println(target, " target found at", index) } else { fmt.Println(target, " target not found") } } func implementBinarySearchUsingRoutines(arr []int, target int) { result := make(chan int) go binarySearchUsingGoRoutines(arr, target, 0, len(arr)-1, result) // it will block the code until search function sends some response index := <-result if index != -1 { fmt.Println(target, " target found at", index) } else { fmt.Println(target, " target not found") } } func binarySearchUsingGoRoutines(arr []int, target, low, high int, result chan int) { if low > high { result <- -1 return } mid := (low + high) / 2 if arr[mid] == target { result <- mid return } if arr[mid] > target { go binarySearchUsingGoRoutines(arr, target, low, mid-1, result) } else { go binarySearchUsingGoRoutines(arr, target, mid+1, high, result) } } func binarySearch(arr []int, target, low, high int) int { if low > high { return -1 } mid := (low + high) / 2 if arr[mid] == target { return mid } if arr[mid] > target { return binarySearch(arr, target, low, mid-1) } else { return binarySearch(arr, target, mid+1, high) } } func generateArrayAndTarget() ([]int, int) { arr := make([]int, 999999999) for i := 1; i < len(arr); i++ { arr[i-1] = i } return arr, arr[rand.Intn(len(arr)-1)] } func printMemUsage() { var stats runtime.MemStats runtime.ReadMemStats(&stats) fmt.Printf("Alloc = %v MiB", stats.Alloc/1024/1024) fmt.Printf("\tTotalAlloc = %v MiB", stats.TotalAlloc/1024/1024) fmt.Printf("\tSys = %v MiB", stats.Sys/1024/1024) fmt.Printf("\tNumGC = %v", stats.NumGC) fmt.Printf("\t no of go routines = %v\n", runtime.NumGoroutine()) } output:- iteration-1:- starting implementation of Binary Search recursively 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 1 no of go routines = 1 time taken in nano seconds:-12889400 starting implementation of Binary Search Using Routines 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 2 no of go routines = 1 time taken in nano seconds:-8522300 -------------------------------- iteration-2:- starting implementation of Binary Search recursively 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 1 no of go routines = 1 time taken in nano seconds:-7123500 starting implementation of Binary Search Using Routines 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 2 no of go routines = 1 time taken in nano seconds:-1851600 -------------------------------- iteration-3:- starting implementation of Binary Search recursively 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 1 no of go routines = 1 time taken in nano seconds:-10586800 starting implementation of Binary Search Using Routines 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 2 no of go routines = 1 time taken in nano seconds:-4202600 -------------------------------- iteration-4:- starting implementation of Binary Search recursively 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 1 no of go routines = 1 time taken in nano seconds:-13572600 starting implementation of Binary Search Using Routines 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 2 no of go routines = 1 time taken in nano seconds:-8423100 -------------------------------- iteration-5:- starting implementation of Binary Search recursively 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 1 no of go routines = 1 time taken in nano seconds:-11283800 starting implementation of Binary Search Using Routines 298498084 target found at 298498083 Alloc = 7629 MiB TotalAlloc = 7629 MiB Sys = 7886 MiB NumGC = 2 no of go routines = 1 time taken in nano seconds:- 7131700
Из вывода видно, что обе реализации занимают одинаковый объем памяти, но затраченное время отличается. Также очевидно, что использование подпрограмм go может улучшить выполнение рекурсивных функций.
Тестирование BenchMark с временем тестирования 10 секунд
note:- target key is generated randomly may not be the same for both the functions go test -bench=. -benchtime=10s iternation -1:- goos: windows goarch: amd64 pkg: medium/binarysearch/usingroutines cpu: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz BenchmarkBinarySearchUsingGoRoutines/input_size_9-8 8009953 2398 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_99-8 1823480 5705 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_999-8 842208 14788 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_9999-8 182142 64211 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_99999-8 25999 466619 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_999999-8 3604 3625492 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_9999999-8 591 19323843 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_99999999-8 58 180493174 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_999999999-8 1 23644980900 ns/op PASS ok medium/binarysearch/usingroutines 147.730s iternation -2:- goos: windows goarch: amd64 pkg: medium/binarysearch/usingroutines cpu: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz BenchmarkBinarySearchUsingGoRoutines/input_size_9-8 9758373 1786 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_99-8 2235060 5464 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_999-8 869326 14623 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_9999-8 180708 66059 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_99999-8 25923 464167 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_999999-8 3112 3486702 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_9999999-8 592 19711070 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_99999999-8 57 184378581 ns/op BenchmarkBinarySearchUsingGoRoutines/input_size_999999999-8 1 23065051500 ns/op PASS ok medium/binarysearch/usingroutines 143.025s ------------------------------------------------- go test -bench=. -benchtime=10s iternation -1:- goos: windows goarch: amd64 pkg: medium/binarysearch/recursive cpu: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz BenchmarkBinarySearch/input_size_9-8 100000000 147.5 ns/op BenchmarkBinarySearch/input_size_99-8 15203101 844.0 ns/op BenchmarkBinarySearch/input_size_999-8 2299185 5452 ns/op BenchmarkBinarySearch/input_size_9999-8 255415 49134 ns/op BenchmarkBinarySearch/input_size_99999-8 29052 406890 ns/op BenchmarkBinarySearch/input_size_999999-8 3422 3558830 ns/op BenchmarkBinarySearch/input_size_9999999-8 652 19478196 ns/op BenchmarkBinarySearch/input_size_99999999-8 66 177987880 ns/op BenchmarkBinarySearch/input_size_999999999-8 1 24534260400 ns/op PASS ok medium/binarysearch/recursive 154.862s iternation -2:- goos: windows goarch: amd64 pkg: medium/binarysearch/recursive cpu: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz BenchmarkBinarySearch/input_size_9-8 112362855 208.8 ns/op BenchmarkBinarySearch/input_size_99-8 13626289 847.0 ns/op BenchmarkBinarySearch/input_size_999-8 2133458 5720 ns/op BenchmarkBinarySearch/input_size_9999-8 245186 48752 ns/op BenchmarkBinarySearch/input_size_99999-8 30397 400923 ns/op BenchmarkBinarySearch/input_size_999999-8 3423 3333734 ns/op BenchmarkBinarySearch/input_size_9999999-8 578 20049595 ns/op BenchmarkBinarySearch/input_size_99999999-8 58 184094891 ns/op BenchmarkBinarySearch/input_size_999999999-8 1 24155393800 ns/op PASS ok medium/binarysearch/recursive 156.357s