github.com/primecitizens/pcz/std@v0.2.1/algo/sort/search.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright 2023 The Prime Citizens 3 // 4 // Copyright 2022 The Go Authors. All rights reserved. 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file. 7 8 package sort 9 10 // Search uses binary search to find and return the smallest index i 11 // in [0, n) at which f(i) is true, assuming that on the range [0, n), 12 // f(i) == true implies f(i+1) == true. That is, Search requires that 13 // f is false for some (possibly empty) prefix of the input range [0, n) 14 // and then true for the (possibly empty) remainder; Search returns 15 // the first true index. If there is no such index, Search returns n. 16 // (Note that the "not found" return value is not -1 as in, for instance, 17 // strings.Index.) 18 // Search calls f(i) only for i in the range [0, n). 19 // 20 // A common use of Search is to find the index i for a value x in 21 // a sorted, indexable data structure such as an array or slice. 22 // In this case, the argument f, typically a closure, captures the value 23 // to be searched for, and how the data structure is indexed and 24 // ordered. 25 // 26 // For instance, given a slice data sorted in ascending order, 27 // the call Search(len(data), func(i int) bool { return data[i] >= 23 }) 28 // returns the smallest index i such that data[i] >= 23. If the caller 29 // wants to find whether 23 is in the slice, it must test data[i] == 23 30 // separately. 31 // 32 // Searching data sorted in descending order would use the <= 33 // operator instead of the >= operator. 34 // 35 // To complete the example above, the following code tries to find the value 36 // x in an integer slice data sorted in ascending order: 37 // 38 // x := 23 39 // i := sort.Search(len(data), func(i int) bool { return data[i] >= x }) 40 // if i < len(data) && data[i] == x { 41 // // x is present at data[i] 42 // } else { 43 // // x is not present in data, 44 // // but i is the index where it would be inserted. 45 // } 46 // 47 // As a more whimsical example, this program guesses your number: 48 // 49 // func GuessingGame() { 50 // var s string 51 // fmt.Printf("Pick an integer from 0 to 100.\n") 52 // answer := sort.Search(100, func(i int) bool { 53 // fmt.Printf("Is your number <= %d? ", i) 54 // fmt.Scanf("%s", &s) 55 // return s != "" && s[0] == 'y' 56 // }) 57 // fmt.Printf("Your number is %d.\n", answer) 58 // } 59 func Search(n int, cmp func(int) bool) int { 60 // Define f(-1) == false and f(n) == true. 61 // Invariant: f(i-1) == false, f(j) == true. 62 i, j := 0, n 63 for i < j { 64 h := int(uint(i+j) >> 1) // avoid overflow when computing h 65 // i ≤ h < j 66 if !cmp(h) { 67 i = h + 1 // preserves f(i-1) == false 68 } else { 69 j = h // preserves f(j) == true 70 } 71 } 72 // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. 73 return i 74 } 75 76 // SearchEx is Search but accepts an extra arg. 77 func SearchEx[T any](n int, arg T, cmp func(int, T) bool) int { 78 // Define f(-1) == false and f(n) == true. 79 // Invariant: f(i-1) == false, f(j) == true. 80 i, j := 0, n 81 for i < j { 82 h := int(uint(i+j) >> 1) // avoid overflow when computing h 83 // i ≤ h < j 84 if !cmp(h, arg) { 85 i = h + 1 // preserves f(i-1) == false 86 } else { 87 j = h // preserves f(j) == true 88 } 89 } 90 // i == j, f(i-1) == false, and f(j) (= f(i)) == true => answer is i. 91 return i 92 } 93 94 // Find uses binary search to find and return the smallest index i in [0, n) 95 // at which cmp(i) <= 0. If there is no such index i, Find returns i = n. 96 // The found result is true if i < n and cmp(i) == 0. 97 // Find calls cmp(i) only for i in the range [0, n). 98 // 99 // To permit binary search, Find requires that cmp(i) > 0 for a leading 100 // prefix of the range, cmp(i) == 0 in the middle, and cmp(i) < 0 for 101 // the final suffix of the range. (Each subrange could be empty.) 102 // The usual way to establish this condition is to interpret cmp(i) 103 // as a comparison of a desired target value t against entry i in an 104 // underlying indexed data structure x, returning <0, 0, and >0 105 // when t < x[i], t == x[i], and t > x[i], respectively. 106 // 107 // For example, to look for a particular string in a sorted, random-access 108 // list of strings: 109 // 110 // i, found := sort.Find(x.Len(), func(i int) int { 111 // return strings.Compare(target, x.At(i)) 112 // }) 113 // if found { 114 // fmt.Printf("found %s at entry %d\n", target, i) 115 // } else { 116 // fmt.Printf("%s not found, would insert at %d", target, i) 117 // } 118 func Find(n int, cmp func(int) int) (i int, found bool) { 119 // The invariants here are similar to the ones in Search. 120 // Define cmp(-1) > 0 and cmp(n) <= 0 121 // Invariant: cmp(i-1) > 0, cmp(j) <= 0 122 i, j := 0, n 123 for i < j { 124 h := int(uint(i+j) >> 1) // avoid overflow when computing h 125 // i ≤ h < j 126 if cmp(h) > 0 { 127 i = h + 1 // preserves cmp(i-1) > 0 128 } else { 129 j = h // preserves cmp(j) <= 0 130 } 131 } 132 // i == j, cmp(i-1) > 0 and cmp(j) <= 0 133 return i, i < n && cmp(i) == 0 134 } 135 136 // FindEx is Find but accepts an extra arg. 137 func FindEx[T any](n int, arg T, cmp func(int, T) int) (i int, found bool) { 138 // The invariants here are similar to the ones in Search. 139 // Define cmp(-1) > 0 and cmp(n) <= 0 140 // Invariant: cmp(i-1) > 0, cmp(j) <= 0 141 i, j := 0, n 142 for i < j { 143 h := int(uint(i+j) >> 1) // avoid overflow when computing h 144 // i ≤ h < j 145 if cmp(h, arg) > 0 { 146 i = h + 1 // preserves cmp(i-1) > 0 147 } else { 148 j = h // preserves cmp(j) <= 0 149 } 150 } 151 // i == j, cmp(i-1) > 0 and cmp(j) <= 0 152 return i, i < n && cmp(i, arg) == 0 153 }