rust_algorithms/searching/advanced.rs
1//! Qo'shimcha qidiruv algoritmlari: jump, interpolation, exponential, ternary.
2
3/// Jump search — tartiblangan massivda √n qadamlab "sakrab" qidirish.
4///
5/// **G'oya:** har `√n` qadamda bir marta qaraymiz; `target` dan katta blok topilsa,
6/// shu blok ichida linear search qilamiz.
7///
8/// **Nega kerak?** Disk/lentaga o'xshash, "orqaga qaytish qimmat" bo'lgan
9/// muhitlarda binary searchdan ko'ra kamroq sakrash qiladi.
10///
11/// - **Time:** O(√n), **Space:** O(1). Shart: tartiblangan bo'lishi.
12///
13/// # Misol
14/// ```
15/// use rust_algorithms::searching::jump_search;
16///
17/// let v: Vec<i32> = (0..100).collect();
18/// assert_eq!(jump_search(&v, &73), Some(73));
19/// assert_eq!(jump_search(&v, &1000), None);
20/// ```
21pub fn jump_search<T: Ord>(list: &[T], target: &T) -> Option<usize> {
22 let n = list.len();
23 if n == 0 {
24 return None;
25 }
26 let step = (n as f64).sqrt().ceil() as usize;
27 let step = step.max(1);
28
29 // 1) target bo'lishi mumkin bo'lgan blokni topamiz
30 let mut block_start = 0usize;
31 while block_start < n && list[(block_start + step - 1).min(n - 1)] < *target {
32 block_start += step;
33 }
34 if block_start >= n {
35 return None;
36 }
37
38 // 2) blok ichida linear search
39 let block_end = (block_start + step).min(n);
40 (block_start..block_end).find(|&i| list[i] == *target)
41}
42
43/// Interpolation search — qiymatlar **tekis taqsimlangan** bo'lsa binary searchdan tez.
44///
45/// **G'oya:** o'rtaga emas, qiymatga qarab "taxmin qilingan" joyga sakraymiz —
46/// lug'atdan "Zokir" so'zini oxiridan qidirganingizdek.
47///
48/// - **Time:** o'rtacha O(log log n), eng yomon holatda O(n) (masalan, 1,2,4,8,…,2^k).
49/// - **Space:** O(1). Shart: tartiblangan `i64` slice.
50///
51/// # Misol
52/// ```
53/// use rust_algorithms::searching::interpolation_search;
54///
55/// let v: Vec<i64> = (0..1000).map(|x| x * 2).collect();
56/// assert_eq!(interpolation_search(&v, 400), Some(200));
57/// assert_eq!(interpolation_search(&v, 401), None);
58/// ```
59pub fn interpolation_search(list: &[i64], target: i64) -> Option<usize> {
60 if list.is_empty() {
61 return None;
62 }
63 let (mut lo, mut hi) = (0usize, list.len() - 1);
64
65 while lo <= hi && target >= list[lo] && target <= list[hi] {
66 if list[hi] == list[lo] {
67 return if list[lo] == target { Some(lo) } else { None };
68 }
69 // chiziqli interpolyatsiya: qiymat oralig'idagi nisbatni indekslarga ko'chiramiz
70 let span = (list[hi] - list[lo]) as i128;
71 let offset = ((target - list[lo]) as i128 * (hi - lo) as i128) / span;
72 let pos = lo + offset as usize;
73
74 match list[pos].cmp(&target) {
75 std::cmp::Ordering::Equal => return Some(pos),
76 std::cmp::Ordering::Less => lo = pos + 1,
77 std::cmp::Ordering::Greater => {
78 if pos == 0 {
79 return None;
80 }
81 hi = pos - 1;
82 }
83 }
84 }
85 None
86}
87
88/// Exponential search — avval oraliqni 1, 2, 4, 8… deb kengaytirib topadi,
89/// so'ng shu oraliqda binary search qiladi.
90///
91/// **Qachon kerak:** massiv juda katta (yoki uzunligi noma'lum) va qidirilayotgan
92/// element **boshiga yaqin** bo'lsa. `i` — javob indeksi bo'lsa, narxi O(log i).
93///
94/// - **Time:** O(log i), **Space:** O(1). Shart: tartiblangan.
95///
96/// # Misol
97/// ```
98/// use rust_algorithms::searching::exponential_search;
99///
100/// let v: Vec<i32> = (0..1_000_000).collect();
101/// assert_eq!(exponential_search(&v, &5), Some(5)); // deyarli bir zumda
102/// ```
103pub fn exponential_search<T: Ord>(list: &[T], target: &T) -> Option<usize> {
104 let n = list.len();
105 if n == 0 {
106 return None;
107 }
108 if list[0] == *target {
109 return Some(0);
110 }
111
112 let mut bound = 1usize;
113 while bound < n && list[bound] < *target {
114 bound *= 2;
115 }
116 let lo = bound / 2;
117 let hi = (bound + 1).min(n);
118
119 // [lo, hi) oralig'ida binary search
120 super::binary_search(&list[lo..hi], target).map(|i| i + lo)
121}
122
123/// Ternary search — **unimodal** (avval o'sib, keyin kamayadigan) funksiya maksimumi.
124///
125/// **G'oya:** oraliqni har qadamda uchga bo'lib, maksimum bo'lishi mumkin bo'lmagan
126/// uchdan birini tashlab yuboramiz.
127///
128/// `eps` — kerakli aniqlik. Qaytadi: maksimum bo'lgan `x`.
129///
130/// - **Time:** O(log((hi-lo)/eps)), **Space:** O(1).
131///
132/// # Misol
133/// ```
134/// use rust_algorithms::searching::ternary_search_max;
135///
136/// // f(x) = -(x-3)^2 + 10 → maksimum x = 3 da
137/// let x = ternary_search_max(-10.0, 10.0, 1e-9, |x| -(x - 3.0) * (x - 3.0) + 10.0);
138/// assert!((x - 3.0).abs() < 1e-5);
139/// ```
140pub fn ternary_search_max<F: Fn(f64) -> f64>(lo: f64, hi: f64, eps: f64, f: F) -> f64 {
141 let (mut lo, mut hi) = (lo, hi);
142 while hi - lo > eps {
143 let m1 = lo + (hi - lo) / 3.0;
144 let m2 = hi - (hi - lo) / 3.0;
145 if f(m1) < f(m2) {
146 lo = m1;
147 } else {
148 hi = m2;
149 }
150 }
151 (lo + hi) / 2.0
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn jump_hamma_elementni_topadi() {
160 let v: Vec<i32> = (0..200).map(|x| x * 5).collect();
161 for (i, x) in v.iter().enumerate() {
162 assert_eq!(jump_search(&v, x), Some(i), "x = {x}");
163 }
164 assert_eq!(jump_search(&v, &3), None);
165 assert_eq!(jump_search::<i32>(&[], &1), None);
166 }
167
168 #[test]
169 fn interpolation_hamma_elementni_topadi() {
170 let v: Vec<i64> = (0..500).map(|x| x * 3 + 7).collect();
171 for (i, &x) in v.iter().enumerate() {
172 assert_eq!(interpolation_search(&v, x), Some(i), "x = {x}");
173 }
174 assert_eq!(interpolation_search(&v, 8), None);
175 assert_eq!(interpolation_search(&[], 1), None);
176 assert_eq!(interpolation_search(&[5, 5, 5], 5), Some(0));
177 assert_eq!(interpolation_search(&[5, 5, 5], 4), None);
178 }
179
180 #[test]
181 fn exponential_hamma_elementni_topadi() {
182 let v: Vec<i32> = (0..1000).map(|x| x * 2).collect();
183 for (i, x) in v.iter().enumerate() {
184 assert_eq!(exponential_search(&v, x), Some(i), "x = {x}");
185 }
186 assert_eq!(exponential_search(&v, &1), None);
187 assert_eq!(exponential_search(&v, &10_000), None);
188 assert_eq!(exponential_search::<i32>(&[], &0), None);
189 }
190
191 #[test]
192 fn ternary_maksimumni_topadi() {
193 let x = ternary_search_max(0.0, 100.0, 1e-9, |x| -(x - 42.0).powi(2));
194 assert!((x - 42.0).abs() < 1e-4);
195 }
196}