Skip to content

Commit 68bc3c8

Browse files
Add Sieve of Eratosthenes algorithm (#154)
1 parent df205e7 commit 68bc3c8

File tree

1 file changed

+212
-0
lines changed

1 file changed

+212
-0
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# Sieve of Eratosthenes Algorithm
2+
#
3+
# The Sieve of Eratosthenes is an ancient algorithm for finding all prime numbers
4+
# up to a given limit. It works by iteratively marking the multiples of each prime
5+
# starting from 2, and the unmarked numbers that remain are primes.
6+
#
7+
# Time Complexity: O(n log log n)
8+
# Space Complexity: O(n)
9+
#
10+
# Input: A positive integer n (the upper limit)
11+
# Output: A vector of all prime numbers from 2 to n
12+
13+
sieve_of_eratosthenes <- function(n) {
14+
# Handle edge cases
15+
if (n < 2) {
16+
return(integer(0)) # No primes less than 2
17+
}
18+
19+
# Create a boolean array "prime[0..n]" and initialize all entries as TRUE
20+
prime <- rep(TRUE, n + 1)
21+
prime[1] <- FALSE # 1 is not a prime number
22+
23+
p <- 2
24+
while (p * p <= n) {
25+
# If prime[p] is not changed, then it is a prime
26+
if (prime[p]) {
27+
# Update all multiples of p starting from p^2
28+
for (i in seq(p * p, n, by = p)) {
29+
prime[i] <- FALSE
30+
}
31+
}
32+
p <- p + 1
33+
}
34+
35+
# Collect all prime numbers
36+
primes <- which(prime)[-1] # Remove index 1 (since 1 is not prime)
37+
return(primes)
38+
}
39+
40+
# Optimized version that only checks odd numbers after 2
41+
sieve_of_eratosthenes_optimized <- function(n) {
42+
# Handle edge cases
43+
if (n < 2) {
44+
return(integer(0))
45+
}
46+
if (n == 2) {
47+
return(2)
48+
}
49+
50+
# Start with 2 (the only even prime)
51+
primes <- c(2)
52+
53+
# Create boolean array for odd numbers only (3, 5, 7, ...)
54+
# Index i represents number (2*i + 3)
55+
size <- (n - 1) %/% 2
56+
is_prime <- rep(TRUE, size)
57+
58+
# Sieve process for odd numbers
59+
for (i in 1:size) {
60+
if (is_prime[i]) {
61+
num <- 2 * i + 1 # Convert index to actual odd number
62+
63+
# Mark multiples of num starting from num^2
64+
if (num * num <= n) {
65+
start_idx <- (num * num - 1) %/% 2 # Convert num^2 to index
66+
for (j in seq(start_idx, size, by = num)) {
67+
if (j <= size) {
68+
is_prime[j] <- FALSE
69+
}
70+
}
71+
}
72+
}
73+
}
74+
75+
# Collect odd primes
76+
odd_primes <- 2 * which(is_prime) + 1
77+
primes <- c(primes, odd_primes)
78+
79+
return(primes)
80+
}
81+
82+
# Function to count primes up to n (useful for large n)
83+
count_primes_sieve <- function(n) {
84+
if (n < 2) {
85+
return(0)
86+
}
87+
88+
prime <- rep(TRUE, n + 1)
89+
prime[1] <- FALSE
90+
91+
p <- 2
92+
while (p * p <= n) {
93+
if (prime[p]) {
94+
for (i in seq(p * p, n, by = p)) {
95+
prime[i] <- FALSE
96+
}
97+
}
98+
p <- p + 1
99+
}
100+
101+
return(sum(prime))
102+
}
103+
104+
# Function to check if a number is prime using trial division (for comparison)
105+
is_prime_trial_division <- function(n) {
106+
if (n <= 1) return(FALSE)
107+
if (n <= 3) return(TRUE)
108+
if (n %% 2 == 0 || n %% 3 == 0) return(FALSE)
109+
110+
i <- 5
111+
while (i * i <= n) {
112+
if (n %% i == 0 || n %% (i + 2) == 0) {
113+
return(FALSE)
114+
}
115+
i <- i + 6
116+
}
117+
return(TRUE)
118+
}
119+
120+
# Segmented sieve for finding primes in a range [low, high]
121+
segmented_sieve <- function(low, high) {
122+
# First, find all primes up to sqrt(high)
123+
limit <- floor(sqrt(high))
124+
primes <- sieve_of_eratosthenes(limit)
125+
126+
# Create a boolean array for range [low, high]
127+
size <- high - low + 1
128+
is_prime <- rep(TRUE, size)
129+
130+
# Mark multiples of each prime in the range
131+
for (prime in primes) {
132+
# Find the minimum number in [low, high] that is a multiple of prime
133+
start <- max(prime * prime, low + (prime - low %% prime) %% prime)
134+
135+
# Mark multiples of prime in the range
136+
for (j in seq(start, high, by = prime)) {
137+
is_prime[j - low + 1] <- FALSE
138+
}
139+
}
140+
141+
# Handle the case where low = 1 (1 is not prime)
142+
if (low == 1) {
143+
is_prime[1] <- FALSE
144+
}
145+
146+
# Collect primes in the range
147+
range_primes <- (low:high)[is_prime]
148+
return(range_primes)
149+
}
150+
151+
# Example usage and testing
152+
cat("=== Sieve of Eratosthenes Algorithm ===\n")
153+
154+
# Test with small number
155+
cat("Primes up to 30:\n")
156+
primes_30 <- sieve_of_eratosthenes(30)
157+
cat(paste(primes_30, collapse = ", "), "\n")
158+
cat("Count:", length(primes_30), "\n\n")
159+
160+
# Test optimized version
161+
cat("Optimized sieve - Primes up to 30:\n")
162+
primes_30_opt <- sieve_of_eratosthenes_optimized(30)
163+
cat(paste(primes_30_opt, collapse = ", "), "\n")
164+
cat("Count:", length(primes_30_opt), "\n\n")
165+
166+
# Test with larger number
167+
cat("Primes up to 100:\n")
168+
primes_100 <- sieve_of_eratosthenes(100)
169+
cat("Count:", length(primes_100), "\n")
170+
cat("First 10 primes:", paste(primes_100[1:10], collapse = ", "), "\n")
171+
cat("Last 10 primes:", paste(tail(primes_100, 10), collapse = ", "), "\n\n")
172+
173+
# Performance comparison for counting primes
174+
cat("=== Performance Comparison ===\n")
175+
n <- 1000
176+
177+
# Count using sieve
178+
start_time <- Sys.time()
179+
sieve_count <- count_primes_sieve(n)
180+
sieve_time <- as.numeric(Sys.time() - start_time, units = "secs")
181+
182+
# Count using trial division
183+
start_time <- Sys.time()
184+
trial_count <- sum(sapply(2:n, is_prime_trial_division))
185+
trial_time <- as.numeric(Sys.time() - start_time, units = "secs")
186+
187+
cat("Primes up to", n, ":\n")
188+
cat("Sieve method:", sieve_count, "primes (", sprintf("%.4f", sieve_time), "seconds )\n")
189+
cat("Trial division:", trial_count, "primes (", sprintf("%.4f", trial_time), "seconds )\n")
190+
cat("Speedup:", sprintf("%.2f", trial_time / sieve_time), "x\n\n")
191+
192+
# Test segmented sieve
193+
cat("=== Segmented Sieve Example ===\n")
194+
cat("Primes between 50 and 100:\n")
195+
range_primes <- segmented_sieve(50, 100)
196+
cat(paste(range_primes, collapse = ", "), "\n")
197+
cat("Count:", length(range_primes), "\n\n")
198+
199+
# Edge cases
200+
cat("=== Edge Cases ===\n")
201+
cat("Primes up to 1:", paste(sieve_of_eratosthenes(1), collapse = ", "), "\n")
202+
cat("Primes up to 2:", paste(sieve_of_eratosthenes(2), collapse = ", "), "\n")
203+
cat("Primes up to 3:", paste(sieve_of_eratosthenes(3), collapse = ", "), "\n")
204+
205+
# Large example (uncomment for larger tests)
206+
# cat("\n=== Large Scale Test ===\n")
207+
# large_n <- 10000
208+
# start_time <- Sys.time()
209+
# large_primes <- sieve_of_eratosthenes(large_n)
210+
# end_time <- Sys.time()
211+
# cat("Found", length(large_primes), "primes up to", large_n, "\n")
212+
# cat("Computation time:", as.numeric(end_time - start_time, units = "secs"), "seconds\n")

0 commit comments

Comments
 (0)