Skip to content

Commit a4a4cf0

Browse files
erfan-khademsiriak
andauthored
Add linear sieve (rust-lang#288)
Co-authored-by: Andrii Siriak <siryaka@gmail.com>
1 parent 14a6fe6 commit a4a4cf0

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

DIRECTORY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
* [Prime Numbers](https://github.com/TheAlgorithms/Rust/blob/master/src/math/prime_numbers.rs)
6262
* [Trial Division](https://github.com/TheAlgorithms/Rust/blob/master/src/math/trial_division.rs)
6363
* [Miller Rabin](https://github.com/TheAlgorithms/Rust/blob/master/src/math/miller_rabin.rs)
64+
* [Linear Sieve](https://github.com/TheAlgorithms/Rust/blob/master/src/math/linear_sieve.rs)
6465
* Searching
6566
* [Binary Search](https://github.com/TheAlgorithms/Rust/blob/master/src/searching/binary_search.rs)
6667
* [Binary Search Recursive](https://github.com/TheAlgorithms/Rust/blob/master/src/searching/binary_search_recursive.rs)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ RESTART BUILD
4242
- [x] [Pascal's triangle](./src/math/pascal_triangle.rs)
4343
- [x] [Square root with Newton's method](./src/math/square_root.rs)
4444
- [x] [Fast power algorithm](./src/math/fast_power.rs)
45+
- [x] [Linear Sieve](./src/math/linear_sieve.rs)
4546

4647
## [Dynamic Programming](./src/dynamic_programming)
4748

src/math/linear_sieve.rs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
Linear Sieve algorithm:
3+
Time complexity is indeed O(n) with O(n) memory, but the sieve generally
4+
runs slower than a well implemented sieve of Eratosthenes. Some use cases are:
5+
- factorizing any number k in the sieve in O(log(k))
6+
- calculating arbitrary multiplicative functions on sieve numbers
7+
without increasing the time complexity
8+
- As a by product, all prime numbers less than `max_number` are stored
9+
in `primes` vector.
10+
*/
11+
pub struct LinearSieve {
12+
max_number: usize,
13+
primes: Vec<usize>,
14+
minimum_prime_factor: Vec<usize>,
15+
}
16+
17+
impl LinearSieve {
18+
pub const fn new() -> Self {
19+
LinearSieve {
20+
max_number: 0,
21+
primes: vec![],
22+
minimum_prime_factor: vec![],
23+
}
24+
}
25+
26+
pub fn prepare(&mut self, max_number: usize) -> Result<(), &'static str> {
27+
if max_number <= 1 {
28+
return Err("Sieve size should be more than 1");
29+
}
30+
if self.max_number > 0 {
31+
return Err("Sieve already initialized");
32+
}
33+
self.max_number = max_number;
34+
self.minimum_prime_factor.resize(max_number + 1, 0);
35+
for i in 2..=max_number {
36+
if self.minimum_prime_factor[i] == 0 {
37+
self.minimum_prime_factor[i] = i;
38+
self.primes.push(i);
39+
/*
40+
if needed, a multiplicative function can be
41+
calculated for this prime number here:
42+
function[i] = base_case(i);
43+
*/
44+
}
45+
for p in self.primes.iter() {
46+
let mlt = (*p) * i;
47+
if *p > i || mlt > max_number {
48+
break;
49+
}
50+
self.minimum_prime_factor[mlt] = *p;
51+
/*
52+
multiplicative function for mlt can be calculated here:
53+
if i % p:
54+
function[mlt] = add_to_prime_exponent(function[i], i, p);
55+
else:
56+
function[mlt] = function[i] * function[p]
57+
*/
58+
}
59+
}
60+
Ok(())
61+
}
62+
63+
pub fn factorize(&self, mut number: usize) -> Result<Vec<usize>, &'static str> {
64+
if number > self.max_number {
65+
return Err("Number is too big, its minimum_prime_factor was not calculated");
66+
}
67+
if number == 0 {
68+
return Err("Number is zero");
69+
}
70+
let mut result: Vec<usize> = Vec::new();
71+
while number > 1 {
72+
result.push(self.minimum_prime_factor[number]);
73+
number /= self.minimum_prime_factor[number];
74+
}
75+
Ok(result)
76+
}
77+
}
78+
79+
#[cfg(test)]
80+
mod tests {
81+
use super::LinearSieve;
82+
83+
#[test]
84+
fn small_primes_list() {
85+
let mut ls = LinearSieve::new();
86+
ls.prepare(25).unwrap();
87+
assert_eq!(ls.primes, vec![2, 3, 5, 7, 11, 13, 17, 19, 23]);
88+
}
89+
90+
#[test]
91+
fn divisible_by_mpf() {
92+
let mut ls = LinearSieve::new();
93+
ls.prepare(1000).unwrap();
94+
for i in 2..=1000 {
95+
let div = i / ls.minimum_prime_factor[i];
96+
assert_eq!(i % ls.minimum_prime_factor[i], 0);
97+
if div == 1 {
98+
// Number must be prime
99+
assert!(ls.primes.binary_search(&i).is_ok());
100+
}
101+
}
102+
}
103+
104+
#[test]
105+
fn check_factorization() {
106+
let mut ls = LinearSieve::new();
107+
ls.prepare(1000).unwrap();
108+
for i in 1..=1000 {
109+
let factorization = ls.factorize(i).unwrap();
110+
let mut product = 1usize;
111+
for (idx, p) in factorization.iter().enumerate() {
112+
assert!(ls.primes.binary_search(&p).is_ok());
113+
product *= *p;
114+
if idx > 0 {
115+
assert!(*p >= factorization[idx - 1]);
116+
}
117+
}
118+
assert_eq!(product, i);
119+
}
120+
}
121+
122+
#[test]
123+
fn check_number_of_primes() {
124+
let mut ls = LinearSieve::new();
125+
ls.prepare(100_000).unwrap();
126+
assert_eq!(ls.primes.len(), 9592);
127+
}
128+
}

src/math/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
mod extended_euclidean_algorithm;
22
mod fast_power;
33
mod greatest_common_divisor;
4+
mod linear_sieve;
45
mod miller_rabin;
56
mod pascal_triangle;
67
mod perfect_numbers;
@@ -14,6 +15,7 @@ pub use self::fast_power::fast_power;
1415
pub use self::greatest_common_divisor::{
1516
greatest_common_divisor_iterative, greatest_common_divisor_recursive,
1617
};
18+
pub use self::linear_sieve::LinearSieve;
1719
pub use self::miller_rabin::miller_rabin;
1820
pub use self::pascal_triangle::pascal_triangle;
1921
pub use self::perfect_numbers::perfect_numbers;

0 commit comments

Comments
 (0)