nekolib/math/sieve_n2_plus_1.rs
1//! $n^2+1$ 型素数の篩。
2
3use std::fmt::Debug;
4
5/// $n^2+1$ 型素数の篩。
6///
7/// # Idea
8/// $n^2+1$ が素因数 $p < n$ を持つとき、以下が成り立つ。
9/// $$ n^2+1 \\equiv 0 \\pmod{p}. $$
10/// このとき、各 $k$ に対して以下が成り立つ。
11///
12/// - $(n+kp)^2+1 \\equiv 0 \\pmod{p}$,
13/// - $(kp-n)^2+1 \\equiv 0 \\pmod{p}$.
14///
15/// # References
16/// - <https://inamori.hateblo.jp/entry/20100510/p1>
17///
18/// # See also
19/// - <https://inamori.hateblo.jp/entry/20110930/p1>
20/// - $n^2+n+1$ 型素数についての話。
21/// - <https://twitter.com/min_25_/status/1418781997892136960>
22/// - <https://twitter.com/min_25_/status/1418794801625853952>
23/// - $an^2+bn+c$ 型素数についての話。
24#[derive(Clone, Debug)]
25pub struct SieveN2Plus1 {
26 a: Vec<usize>,
27 f: Vec<Vec<(usize, u32)>>,
28}
29
30impl SieveN2Plus1 {
31 /// 初期化する。
32 pub fn new(n: usize) -> Self {
33 let mut a: Vec<_> = (0..=n).map(|k| k * k + 1).collect();
34 let mut f = vec![vec![]; n + 1];
35 f[1] = vec![(2, 1)];
36 for j in (3..=n).step_by(2) {
37 let (d, e) = Self::div_pow(a[j], 2);
38 f[j].push((2, e));
39 a[j] = d;
40 }
41 for i in 2..=n {
42 let p = a[i];
43 if p == 1 {
44 continue;
45 }
46 if p == i * i + 1 {
47 f[i].push((p, 1));
48 }
49 let init1 = if p == i * i + 1 { p + i } else { i };
50 let init2 = (n + p - 1) / p * p;
51 for j in (init1..=n).step_by(p).chain((init2..=n).step_by(p)) {
52 let (d, e) = Self::div_pow(a[j], p);
53 if e > 0 {
54 f[j].push((p, e));
55 a[j] = d;
56 }
57 }
58 }
59 Self { a, f }
60 }
61
62 fn div_pow(mut a: usize, p: usize) -> (usize, u32) {
63 let mut e = 0;
64 while a % p == 0 {
65 a /= p;
66 e += 1;
67 }
68 (a, e)
69 }
70
71 /// $n^2+1$ の形の素数を返す。
72 ///
73 /// # Examples
74 /// ```
75 /// use nekolib::math::SieveN2Plus1;
76 ///
77 /// let ss = SieveN2Plus1::new(10);
78 /// let primes: Vec<_> = ss.primes().collect();
79 /// assert_eq!(primes, [2, 5, 17, 37, 101]);
80 /// ```
81 pub fn primes(&self) -> impl Iterator<Item = usize> + '_ {
82 (1..self.a.len()).filter(move |&i| self.a[i] != 1).map(|i| i * i + 1)
83 }
84
85 /// $n^2+1$ が素数のとき真を返す。
86 ///
87 /// # Examples
88 /// ```
89 /// use nekolib::math::SieveN2Plus1;
90 ///
91 /// let ss = SieveN2Plus1::new(10);
92 /// assert!(ss.is_prime(2)); // 5 is prime
93 /// assert!(!ss.is_prime(5)); // 26 = 2 * 13
94 /// ```
95 pub fn is_prime(&self, n: usize) -> bool { n > 0 && self.a[n] != 1 }
96
97 /// $n^2+1$ を素因数分解する。
98 ///
99 /// 底の昇順とは限らないので注意。最小の反例は `n = 21`。
100 ///
101 /// # Examples
102 /// ```
103 /// use nekolib::math::SieveN2Plus1;
104 ///
105 /// let ss = SieveN2Plus1::new(10);
106 /// assert_eq!(ss.factors(0).next(), None);
107 /// assert_eq!(ss.factors(4).collect::<Vec<_>>(), [(17, 1)]);
108 /// assert_eq!(ss.factors(7).collect::<Vec<_>>(), [(2, 1), (5, 2)]);
109 /// ```
110 pub fn factors(&self, n: usize) -> impl Iterator<Item = (usize, u32)> + '_ {
111 self.f[n].iter().cloned()
112 }
113
114 /// $n^2+1$ を素因数を列挙する。重複あり。
115 ///
116 /// 底の昇順とは限らないので注意。最小の反例は `n = 21`。
117 ///
118 /// # Examples
119 /// ```
120 /// use nekolib::math::SieveN2Plus1;
121 ///
122 /// let ss = SieveN2Plus1::new(10);
123 /// assert_eq!(ss.factors_dup(0).next(), None);
124 /// assert_eq!(ss.factors_dup(4).collect::<Vec<_>>(), [17]);
125 /// assert_eq!(ss.factors_dup(7).collect::<Vec<_>>(), [2, 5, 5]);
126 /// ```
127 pub fn factors_dup(&self, n: usize) -> impl Iterator<Item = usize> + '_ {
128 self.factors(n).flat_map(|(p, e)| std::iter::repeat(p).take(e as usize))
129 }
130}
131
132#[test]
133fn test() {
134 use linear_sieve::LinearSieve;
135
136 let n = 3000;
137 let ls = LinearSieve::new(n * n + 1);
138 let ss = SieveN2Plus1::new(n);
139 for i in 0..=n {
140 assert_eq!(ls.is_prime(i * i + 1), ss.is_prime(i));
141
142 {
143 // factors
144 let expected: Vec<_> = ls.factors_dup(i * i + 1).collect();
145 let mut actual: Vec<_> = ss.factors_dup(i).collect();
146 actual.sort_unstable();
147 assert_eq!(actual, expected);
148 }
149 }
150}