Skip to main content

nekolib/math/
carmichael_lambda.rs

1//! Carmichael の $\\lambda$ 関数。
2
3use super::factors;
4use super::lcm;
5
6use factors::Factors;
7use lcm::Lcm;
8
9/// Carmichael の $\\lambda$ 関数。
10///
11/// $\\lambda(n)$ は、$\\gcd(a, n)$ である任意の $a$ に対して
12/// $a^m\\equiv 1 \\pmod{n}$ となる最小の $m$ として定義される。
13///
14/// $\\gdef\\lcm{\\operatorname\*{lcm}}$
15/// 以下の式によって計算される。
16/// - $\\lambda(1) = 1$
17/// - $\\lambda(2) = 1$
18/// - $\\lambda(4) = 2$
19/// - $\\lambda(2^e) = 2^{e-2}$ for $e\\ge 3$
20/// - $\\lambda(p^e) = \\varphi(p^e) = p^{e-1}(p-1)$ for odd prime $p$
21/// - $\\lambda(\\prod\_{p:\\text{ prime}} p\_i^{e\_i}) = \\lcm\_i \\lambda(p\_i^{e\_i})$
22///
23/// # Complexity
24/// $O(\\sqrt{n})$ time.
25///
26/// # Examples
27/// ```
28/// use nekolib::math::CarmichaelLambda;
29///
30/// assert_eq!(1_u64.carmichael_lambda(), 1);
31/// assert_eq!(15_u64.carmichael_lambda(), 4);
32/// assert_eq!(21_u64.carmichael_lambda(), 6);
33/// assert_eq!(33_u64.carmichael_lambda(), 10);
34/// ```
35pub trait CarmichaelLambda {
36    fn carmichael_lambda(self) -> Self;
37}
38
39macro_rules! impl_uint {
40    ($t:ty) => {
41        impl CarmichaelLambda for $t {
42            fn carmichael_lambda(self) -> Self {
43                let n = self;
44                let e2 = n.trailing_zeros() as $t;
45                let mut res = match e2 {
46                    0 | 1 => 1,
47                    2 => 2,
48                    _ => 1 << (e2 - 2),
49                };
50                for (p, e) in (n >> e2).factors() {
51                    res = res.lcm(p.pow(e - 1) * (p - 1));
52                }
53                res
54            }
55        }
56    };
57    ( $($t:ty)* ) => { $(impl_uint!($t);)* };
58}
59
60impl_uint!(u8 u16 u32 u64 u128 usize);
61
62#[test]
63fn test() {
64    use gcd::Gcd;
65
66    let n_max = 1000_usize;
67    assert_eq!(1_usize.carmichael_lambda(), 1);
68    for n in 2..=n_max {
69        let mut pow = vec![1; n];
70        let relp: Vec<_> = (0..n).filter(|&a| a.gcd(n) == 1).collect();
71        for e in 1..n {
72            for &a in &relp {
73                pow[a] = pow[a] * a % n;
74            }
75            if pow.iter().all(|&x| x == 1) {
76                assert_eq!(
77                    n.carmichael_lambda(),
78                    e,
79                    "lambda({}) = {}? {}",
80                    n,
81                    e,
82                    n.carmichael_lambda()
83                );
84                break;
85            }
86        }
87        assert!(pow.iter().all(|&x| x == 1));
88    }
89}