rust_algorithms/numbers/arithmetic.rs
1//! Arifmetik algoritmlar: EKUB/EKUK, tez darajaga ko'tarish, modul arifmetikasi, Fibonachchi.
2
3/// Eng katta umumiy bo'luvchi (EKUB / GCD) — Evklid algoritmi.
4///
5/// **G'oya:** `gcd(a, b) = gcd(b, a mod b)`. Chunki `a` va `b` ning har qanday umumiy
6/// bo'luvchisi `a - k·b` ni ham bo'ladi. `b` nolga aylanganda `a` — javob.
7///
8/// - **Time:** O(log min(a, b)), **Space:** O(1).
9///
10/// # Misol
11/// ```
12/// use rust_algorithms::numbers::gcd;
13///
14/// assert_eq!(gcd(48, 18), 6);
15/// assert_eq!(gcd(17, 5), 1); // o'zaro tub
16/// assert_eq!(gcd(0, 9), 9);
17/// ```
18pub fn gcd(a: u64, b: u64) -> u64 {
19 let (mut a, mut b) = (a, b);
20 while b != 0 {
21 let t = a % b;
22 a = b;
23 b = t;
24 }
25 a
26}
27
28/// Eng kichik umumiy karrali (EKUK / LCM).
29///
30/// `lcm(a,b) = a / gcd(a,b) * b` — avval bo'lamiz, keyin ko'paytiramiz:
31/// bu overflow ehtimolini kamaytiradi.
32///
33/// # Misol
34/// ```
35/// use rust_algorithms::numbers::lcm;
36///
37/// assert_eq!(lcm(4, 6), 12);
38/// assert_eq!(lcm(0, 5), 0);
39/// ```
40pub fn lcm(a: u64, b: u64) -> u64 {
41 if a == 0 || b == 0 {
42 return 0;
43 }
44 a / gcd(a, b) * b
45}
46
47/// Kengaytirilgan Evklid algoritmi: `(g, x, y)` shundayki `a·x + b·y = g = gcd(a, b)`.
48///
49/// **Nima uchun kerak:** modul bo'yicha teskari element topish, diofant tenglamalar,
50/// RSA kabi kriptografik sxemalar.
51///
52/// # Misol
53/// ```
54/// use rust_algorithms::numbers::extended_gcd;
55///
56/// let (g, x, y) = extended_gcd(240, 46);
57/// assert_eq!(g, 2);
58/// assert_eq!(240 * x + 46 * y, g);
59/// ```
60pub fn extended_gcd(a: i64, b: i64) -> (i64, i64, i64) {
61 if b == 0 {
62 return (a, 1, 0);
63 }
64 let (g, x1, y1) = extended_gcd(b, a % b);
65 (g, y1, x1 - (a / b) * y1)
66}
67
68/// `a` ning `n` -darajasi — tez darajaga ko'tarish (binary exponentiation).
69///
70/// **G'oya:** `a⁸ = ((a²)²)²`. Darajani ikkilik sanoq sistemasida ko'rib,
71/// har qadamda asosni kvadratga ko'taramiz va kerakli bitlarda javobga ko'paytiramiz.
72///
73/// - **Time:** O(log n) — `a⁶⁴` uchun 64 ta emas, atigi 7 ta ko'paytirish!
74/// - Overflow bo'lsa `None` qaytadi.
75///
76/// # Misol
77/// ```
78/// use rust_algorithms::numbers::fast_pow;
79///
80/// assert_eq!(fast_pow(2, 10), Some(1024));
81/// assert_eq!(fast_pow(3, 0), Some(1));
82/// assert_eq!(fast_pow(u64::MAX, 2), None); // overflow
83/// ```
84pub fn fast_pow(a: u64, n: u32) -> Option<u64> {
85 let mut natija: u64 = 1;
86 let mut asos = a;
87 let mut n = n;
88 while n > 0 {
89 if n & 1 == 1 {
90 natija = natija.checked_mul(asos)?;
91 }
92 n >>= 1;
93 if n > 0 {
94 asos = asos.checked_mul(asos)?;
95 }
96 }
97 Some(natija)
98}
99
100/// `(a^n) mod m` — modul bo'yicha tez darajaga ko'tarish.
101///
102/// Kriptografiyaning "ishchi oti": 2048-bitli sonlar bilan ham tez ishlaydi.
103/// Overflowdan saqlanish uchun ichkarida `u128` ishlatilgan.
104///
105/// - **Time:** O(log n).
106///
107/// # Misol
108/// ```
109/// use rust_algorithms::numbers::mod_pow;
110///
111/// assert_eq!(mod_pow(2, 10, 1000), 24); // 1024 mod 1000
112/// assert_eq!(mod_pow(3, 0, 7), 1);
113/// assert_eq!(mod_pow(123456789, 987654321, 1_000_000_007), 652541198);
114/// ```
115pub fn mod_pow(a: u64, n: u64, m: u64) -> u64 {
116 if m == 1 {
117 return 0;
118 }
119 let mut natija: u128 = 1;
120 let mut asos = (a % m) as u128;
121 let m128 = m as u128;
122 let mut n = n;
123 while n > 0 {
124 if n & 1 == 1 {
125 natija = natija * asos % m128;
126 }
127 asos = asos * asos % m128;
128 n >>= 1;
129 }
130 natija as u64
131}
132
133/// `a` ning `m` moduli bo'yicha teskarisi: `a · x ≡ 1 (mod m)`.
134///
135/// `gcd(a, m) != 1` bo'lsa teskari element mavjud emas → `None`.
136///
137/// # Misol
138/// ```
139/// use rust_algorithms::numbers::mod_inverse;
140///
141/// assert_eq!(mod_inverse(3, 11), Some(4)); // 3*4 = 12 ≡ 1 (mod 11)
142/// assert_eq!(mod_inverse(2, 4), None); // gcd(2,4) = 2
143/// ```
144pub fn mod_inverse(a: i64, m: i64) -> Option<i64> {
145 let (g, x, _) = extended_gcd(a.rem_euclid(m), m);
146 if g != 1 {
147 return None;
148 }
149 Some(x.rem_euclid(m))
150}
151
152/// `n!` — faktorial (iterativ, overflowga xavfsiz).
153///
154/// `u64` da `20!` gacha sig'adi; kattasida `None`.
155///
156/// # Misol
157/// ```
158/// use rust_algorithms::numbers::factorial;
159///
160/// assert_eq!(factorial(5), Some(120));
161/// assert_eq!(factorial(0), Some(1));
162/// assert_eq!(factorial(21), None);
163/// ```
164pub fn factorial(n: u32) -> Option<u64> {
165 (1..=n as u64).try_fold(1u64, |acc, x| acc.checked_mul(x))
166}
167
168/// `n` -Fibonachchi soni — iterativ, O(n).
169///
170/// **Nega rekursiya emas?** Sodda rekursiya bir xil qiymatni qayta-qayta hisoblab
171/// O(2ⁿ) ga chiqadi: `fib(50)` uyingizdagi kompyuterda bir necha kun ishlaydi.
172/// Bu yerda faqat oxirgi ikkita qiymatni eslab qolamiz.
173///
174/// # Misol
175/// ```
176/// use rust_algorithms::numbers::fibonacci;
177///
178/// assert_eq!(fibonacci(0), Some(0));
179/// assert_eq!(fibonacci(10), Some(55));
180/// assert_eq!(fibonacci(90), Some(2880067194370816120));
181/// assert_eq!(fibonacci(93), Some(12200160415121876738)); // u64 dagi oxirgisi
182/// assert_eq!(fibonacci(94), None); // u64 ga sig'maydi
183/// ```
184pub fn fibonacci(n: u32) -> Option<u64> {
185 if n > 200 {
186 return None;
187 }
188 // Ichkarida u128 — shunda 93-Fibonachchini ham overflowsiz hisoblaymiz.
189 let (mut a, mut b) = (0u128, 1u128);
190 for _ in 0..n {
191 let t = a + b;
192 a = b;
193 b = t;
194 }
195 u64::try_from(a).ok()
196}
197
198/// `n` -Fibonachchi soni — O(log n) ("fast doubling").
199///
200/// Formulalar:
201/// `F(2k) = F(k)·(2·F(k+1) − F(k))`, `F(2k+1) = F(k)² + F(k+1)²`.
202///
203/// Natija `u128` da qaytadi — `n = 180` gacha bemalol.
204///
205/// # Misol
206/// ```
207/// use rust_algorithms::numbers::{fibonacci, fibonacci_fast};
208///
209/// assert_eq!(fibonacci_fast(10), 55);
210/// assert_eq!(fibonacci_fast(90) as u64, fibonacci(90).unwrap());
211/// ```
212pub fn fibonacci_fast(n: u32) -> u128 {
213 fn doubling(n: u32) -> (u128, u128) {
214 if n == 0 {
215 return (0, 1);
216 }
217 let (a, b) = doubling(n / 2);
218 let c = a * (2 * b - a);
219 let d = a * a + b * b;
220 if n % 2 == 0 {
221 (c, d)
222 } else {
223 (d, c + d)
224 }
225 }
226 doubling(n).0
227}
228
229/// Sonning raqamlari (eng kattasidan kichigiga qarab).
230///
231/// # Misol
232/// ```
233/// use rust_algorithms::numbers::digits;
234///
235/// assert_eq!(digits(4071), vec![4, 0, 7, 1]);
236/// assert_eq!(digits(0), vec![0]);
237/// ```
238pub fn digits(mut n: u64) -> Vec<u8> {
239 if n == 0 {
240 return vec![0];
241 }
242 let mut out = Vec::new();
243 while n > 0 {
244 out.push((n % 10) as u8);
245 n /= 10;
246 }
247 out.reverse();
248 out
249}
250
251/// Raqamlar yig'indisi.
252///
253/// # Misol
254/// ```
255/// use rust_algorithms::numbers::digit_sum;
256///
257/// assert_eq!(digit_sum(12345), 15);
258/// ```
259pub fn digit_sum(mut n: u64) -> u64 {
260 let mut s = 0;
261 while n > 0 {
262 s += n % 10;
263 n /= 10;
264 }
265 s
266}
267
268/// Sonni teskari o'girish: `1234 → 4321`.
269///
270/// # Misol
271/// ```
272/// use rust_algorithms::numbers::reverse_number;
273///
274/// assert_eq!(reverse_number(1234), 4321);
275/// assert_eq!(reverse_number(1200), 21);
276/// ```
277pub fn reverse_number(mut n: u64) -> u64 {
278 let mut out = 0u64;
279 while n > 0 {
280 out = out * 10 + n % 10;
281 n /= 10;
282 }
283 out
284}
285
286/// Sonni `base` (2..=36) sanoq sistemasiga o'tkazadi.
287///
288/// # Misol
289/// ```
290/// use rust_algorithms::numbers::to_base;
291///
292/// assert_eq!(to_base(255, 2), "11111111");
293/// assert_eq!(to_base(255, 16), "ff");
294/// assert_eq!(to_base(0, 8), "0");
295/// ```
296pub fn to_base(mut n: u64, base: u32) -> String {
297 assert!((2..=36).contains(&base), "base 2..=36 oralig'ida bo'lsin");
298 if n == 0 {
299 return "0".to_string();
300 }
301 const RAQAMLAR: &[u8] = b"0123456789abcdefghijklmnopqrstuvwxyz";
302 let mut buf = Vec::new();
303 while n > 0 {
304 buf.push(RAQAMLAR[(n % base as u64) as usize]);
305 n /= base as u64;
306 }
307 buf.reverse();
308 String::from_utf8(buf).unwrap()
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn gcd_va_lcm() {
317 assert_eq!(gcd(12, 8), 4);
318 assert_eq!(gcd(0, 0), 0);
319 assert_eq!(gcd(7, 0), 7);
320 assert_eq!(lcm(21, 6), 42);
321 // gcd(a,b) * lcm(a,b) == a*b
322 for a in 1u64..30 {
323 for b in 1u64..30 {
324 assert_eq!(gcd(a, b) * lcm(a, b), a * b);
325 }
326 }
327 }
328
329 #[test]
330 fn extended_gcd_identiteti() {
331 for a in 1i64..50 {
332 for b in 1i64..50 {
333 let (g, x, y) = extended_gcd(a, b);
334 assert_eq!(g, gcd(a as u64, b as u64) as i64);
335 assert_eq!(a * x + b * y, g);
336 }
337 }
338 }
339
340 #[test]
341 fn fast_pow_togri() {
342 for a in 1u64..10 {
343 for n in 0u32..10 {
344 assert_eq!(fast_pow(a, n), Some(a.pow(n)));
345 }
346 }
347 }
348
349 #[test]
350 fn mod_pow_togri() {
351 let m = 1_000_000_007u64;
352 for a in 1u64..20 {
353 let mut kutilgan = 1u64;
354 for n in 0u64..20 {
355 assert_eq!(mod_pow(a, n, m), kutilgan, "a={a}, n={n}");
356 kutilgan = kutilgan * a % m;
357 }
358 }
359 assert_eq!(mod_pow(5, 100, 1), 0);
360 }
361
362 #[test]
363 fn mod_inverse_togri() {
364 let m = 13;
365 for a in 1..m {
366 let inv = mod_inverse(a, m).unwrap();
367 assert_eq!(a * inv % m, 1);
368 }
369 assert_eq!(mod_inverse(6, 9), None);
370 }
371
372 #[test]
373 fn fibonacci_ikki_usul_mos() {
374 for n in 0..=90u32 {
375 assert_eq!(fibonacci(n).unwrap() as u128, fibonacci_fast(n));
376 }
377 }
378
379 #[test]
380 fn raqamlar_bilan_ishlash() {
381 assert_eq!(digits(1000), vec![1, 0, 0, 0]);
382 assert_eq!(digit_sum(0), 0);
383 assert_eq!(reverse_number(0), 0);
384 assert_eq!(to_base(31, 32), "v");
385 }
386}