rust_algorithms/other/recursion.rs
1//! Rekursiya — funksiyaning o'zini o'zi chaqirishi.
2//!
3//! ## Har bir rekursiv funksiyada 2 qism bo'lishi shart
4//!
5//! 1. **Baza holati (base case)** — rekursiya to'xtaydigan joy. Bo'lmasa → stack overflow;
6//! 2. **Rekursiv qadam** — masalani **kichikroq** ko'rinishga keltirish.
7//!
8//! ```text
9//! faktorial(4)
10//! → 4 × faktorial(3)
11//! → 3 × faktorial(2)
12//! → 2 × faktorial(1)
13//! → 1 × faktorial(0)
14//! → 1 ← baza
15//! = 4 × 3 × 2 × 1 × 1 = 24
16//! ```
17//!
18//! ## Rekursiya narxi
19//!
20//! Har bir chaqiruv **stack** da joy egallaydi. Rustda odatiy stack 8 MB —
21//! taxminan bir necha yuz ming chaqiruvga yetadi. Chuqurlik katta bo'lsa,
22//! iterativ variantga o'ting (Rust hozircha *tail call optimization* qilmaydi).
23
24/// Faktorial: `n! = n × (n-1) × … × 1`, `0! = 1`.
25///
26/// Overflow bo'lsa `None`.
27///
28/// # Misol
29/// ```
30/// use rust_algorithms::other::recursion::factorial;
31///
32/// assert_eq!(factorial(5), Some(120));
33/// assert_eq!(factorial(0), Some(1));
34/// assert_eq!(factorial(25), None);
35/// ```
36pub fn factorial(n: u32) -> Option<u64> {
37 if n == 0 {
38 return Some(1); // baza holati
39 }
40 factorial(n - 1)?.checked_mul(n as u64)
41}
42
43/// Sodda rekursiv Fibonachchi — **qanday qilmaslik** kerakligining namunasi.
44///
45/// Har bir chaqiruv ikkita yangi chaqiruv yaratadi → **O(2ⁿ)**.
46/// `fib_naive(40)` bir necha soniya, `fib_naive(60)` esa bir necha yil ishlaydi.
47///
48/// Yechim: memoizatsiya ([`crate::dp::fib_memo`]) yoki iteratsiya
49/// ([`crate::numbers::fibonacci`]).
50///
51/// # Misol
52/// ```
53/// use rust_algorithms::other::recursion::fib_naive;
54///
55/// assert_eq!(fib_naive(10), 55);
56/// // fib_naive(50) ni chaqirmang — juda uzoq davom etadi!
57/// ```
58pub fn fib_naive(n: u32) -> u64 {
59 if n < 2 {
60 return n as u64;
61 }
62 fib_naive(n - 1) + fib_naive(n - 2)
63}
64
65/// Hanoy minoralari: `n` ta diskni `from` ustunidan `to` ustuniga ko'chirish qadamlari.
66///
67/// **Qoidalar:** bir vaqtda bitta disk; katta disk kichigining ustiga qo'yilmaydi.
68///
69/// **Rekursiv g'oya (3 qadam):**
70/// 1. Yuqoridagi `n-1` diskni yordamchi ustunga ko'chir;
71/// 2. Eng katta diskni maqsad ustunga qo'y;
72/// 3. `n-1` diskni yordamchidan maqsadga ko'chir.
73///
74/// - **Qadamlar soni:** `2ⁿ − 1` — bundan kamiga iloji yo'q (isbotlangan).
75///
76/// # Misol
77/// ```
78/// use rust_algorithms::other::recursion::hanoi;
79///
80/// let qadamlar = hanoi(3, 'A', 'C', 'B');
81/// assert_eq!(qadamlar.len(), 7); // 2³ − 1
82/// assert_eq!(qadamlar[0], ('A', 'C'));
83/// ```
84pub fn hanoi(n: u32, from: char, to: char, aux: char) -> Vec<(char, char)> {
85 let mut qadamlar = Vec::new();
86 fn go(n: u32, from: char, to: char, aux: char, out: &mut Vec<(char, char)>) {
87 if n == 0 {
88 return;
89 }
90 go(n - 1, from, aux, to, out);
91 out.push((from, to));
92 go(n - 1, aux, to, from, out);
93 }
94 go(n, from, to, aux, &mut qadamlar);
95 qadamlar
96}
97
98/// Satrni rekursiv teskari o'girish (o'quv maqsadida).
99///
100/// Amalda: `s.chars().rev().collect::<String>()`.
101///
102/// # Misol
103/// ```
104/// use rust_algorithms::other::recursion::reverse_string;
105///
106/// assert_eq!(reverse_string("salom"), "molas");
107/// assert_eq!(reverse_string(""), "");
108/// ```
109pub fn reverse_string(s: &str) -> String {
110 let belgilar: Vec<char> = s.chars().collect();
111 fn go(b: &[char]) -> String {
112 match b.split_first() {
113 None => String::new(),
114 Some((birinchi, qolgan)) => format!("{}{}", go(qolgan), birinchi),
115 }
116 }
117 go(&belgilar)
118}
119
120/// Ackermann funksiyasi — rekursiyaning "chegarasi".
121///
122/// Bu funksiya **primitiv rekursiv emas**: uni tsikllar bilan (oldindan ma'lum
123/// takrorlanishlar soni bilan) yozib bo'lmaydi. Juda tez o'sadi:
124/// `A(4, 2)` ≈ 2·10¹⁹⁷²⁸ — koinotdagi atomlardan ko'p raqamli son.
125///
126/// Shu sababli faqat kichik argumentlar bilan chaqiring (`m <= 3`).
127///
128/// # Misol
129/// ```
130/// use rust_algorithms::other::recursion::ackermann;
131///
132/// assert_eq!(ackermann(1, 1), 3);
133/// assert_eq!(ackermann(2, 3), 9);
134/// assert_eq!(ackermann(3, 3), 61);
135/// ```
136pub fn ackermann(m: u64, n: u64) -> u64 {
137 if m == 0 {
138 n + 1
139 } else if n == 0 {
140 ackermann(m - 1, 1)
141 } else {
142 ackermann(m - 1, ackermann(m, n - 1))
143 }
144}
145
146/// Ikkilik qidiruvning rekursiv ko'rinishi (`start..end` oralig'ida).
147///
148/// "Bo'l va hukmronlik qil" ning eng sodda namunasi.
149///
150/// # Misol
151/// ```
152/// use rust_algorithms::other::recursion::binary_search_rec;
153///
154/// let v = [1, 3, 5, 7, 9];
155/// assert_eq!(binary_search_rec(&v, 7), Some(3));
156/// assert_eq!(binary_search_rec(&v, 4), None);
157/// ```
158pub fn binary_search_rec(list: &[i64], target: i64) -> Option<usize> {
159 fn go(list: &[i64], target: i64, lo: usize, hi: usize) -> Option<usize> {
160 if lo >= hi {
161 return None; // baza: oraliq bo'sh
162 }
163 let mid = lo + (hi - lo) / 2;
164 match list[mid].cmp(&target) {
165 std::cmp::Ordering::Equal => Some(mid),
166 std::cmp::Ordering::Less => go(list, target, mid + 1, hi),
167 std::cmp::Ordering::Greater => go(list, target, lo, mid),
168 }
169 }
170 go(list, target, 0, list.len())
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn faktorial() {
179 assert_eq!(factorial(1), Some(1));
180 assert_eq!(factorial(10), Some(3_628_800));
181 assert_eq!(factorial(20), Some(2_432_902_008_176_640_000));
182 assert_eq!(factorial(21), None);
183 }
184
185 #[test]
186 fn fib_naive_kichik_qiymatlarda() {
187 let kutilgan = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34];
188 for (n, &k) in kutilgan.iter().enumerate() {
189 assert_eq!(fib_naive(n as u32), k);
190 }
191 }
192
193 #[test]
194 fn hanoy_qadamlari_qoidaga_mos() {
195 for n in 1..=8u32 {
196 let qadamlar = hanoi(n, 'A', 'C', 'B');
197 assert_eq!(qadamlar.len(), (1 << n) - 1);
198
199 // Simulyatsiya: qoidalar buzilmasligini tekshiramiz
200 let mut ustunlar = std::collections::HashMap::from([
201 ('A', (1..=n).rev().collect::<Vec<u32>>()), // pastda katta
202 ('B', Vec::new()),
203 ('C', Vec::new()),
204 ]);
205 for (from, to) in qadamlar {
206 let disk = ustunlar.get_mut(&from).unwrap().pop().expect("bo'sh ustun");
207 let maqsad = ustunlar.get_mut(&to).unwrap();
208 if let Some(&ust) = maqsad.last() {
209 assert!(disk < ust, "katta disk kichigining ustiga qo'yildi");
210 }
211 maqsad.push(disk);
212 }
213 assert_eq!(ustunlar[&'C'].len(), n as usize);
214 }
215 }
216
217 #[test]
218 fn satrni_teskari() {
219 assert_eq!(reverse_string("abc"), "cba");
220 assert_eq!(reverse_string("a"), "a");
221 assert_eq!(reverse_string("o'zbek"), "kebz'o");
222 }
223
224 #[test]
225 fn ackermann_kichik_qiymatlar() {
226 assert_eq!(ackermann(0, 0), 1);
227 assert_eq!(ackermann(1, 0), 2);
228 assert_eq!(ackermann(2, 2), 7);
229 assert_eq!(ackermann(3, 2), 29);
230 }
231
232 #[test]
233 fn rekursiv_binary_search() {
234 let v: Vec<i64> = (0..100).map(|x| x * 2).collect();
235 for (i, &x) in v.iter().enumerate() {
236 assert_eq!(binary_search_rec(&v, x), Some(i));
237 }
238 assert_eq!(binary_search_rec(&v, 1), None);
239 assert_eq!(binary_search_rec(&[], 1), None);
240 }
241}