Skip to main content

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}