Skip to content

Commit ab8fb0b

Browse files
committed
get primes using segmented sieve
1 parent f1f5222 commit ab8fb0b

File tree

5 files changed

+170
-1
lines changed

5 files changed

+170
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ WIP, the descriptions of the below `unsolved yet` problems can be found in the [
8989
- [x] [Sieve of Eratosthenes](https://github.com/danrusei/algorithms_with_Go/tree/main/numbers/eratosthenes)
9090
- [] Convex Hull
9191
- [] Basic and Extended Euclidean algorithms
92-
- [] Segmented Sieve
92+
- [x] [Segmented Sieve](https://github.com/danrusei/algorithms_with_Go/tree/main/numbers/segmented)
9393
- [] Chinese remainder theorem
9494
- [] Lucas Theorem
9595

numbers/segmented/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Segmented Sieve
2+
3+
Source: [GeeksforGeeks](https://www.geeksforgeeks.org/segmented-sieve/amp/)
4+
Source: [Wikipedia - Segmented Sieves](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Segmented_sieve)
5+
6+
Given a number n, print all primes smaller than or equal to n.
7+
8+
The implemented [Sieve of Eratosthenes](https://github.com/danrusei/algorithms_with_Go/tree/main/numbers/eratosthenes) looks good, but consider the situation when n is large, the Simple Sieve faces following issues:
9+
10+
* an array of size Θ(n) may not fit in memory
11+
* the simple Sieve is not cache friendly even for slightly bigger n. The algorithm traverses the array without locality of reference
12+
13+
## Example
14+
15+
> Input : n =10
16+
> Output : 2 3 5 7
17+
>
18+
> Input : n = 20
19+
> Output: 2 3 5 7 11 13 17 19
20+
21+
## Algorithm
22+
23+
The idea of segmented sieve is to divide the range [0..n-1] in different segments and compute primes in all segments one by one. This algorithm first uses Simple Sieve to find primes smaller than or equal to √(n).
24+
25+
* use Simple Sieve to find all primes upto square root of ‘n’ and store these primes in an array “prime[]”. Store the found primes in an array ‘prime[]’.
26+
* divide [0..n-1] range in different segments such that size of every segment is at-most √n
27+
* for every segment [low..high]
28+
* create an array mark[high-low+1]. Here we need only O(x) space where x is number of elements in given range.
29+
* iterate through all primes found in step 1. For every prime, mark its multiples in given range [low..high].
30+
31+
In Simple Sieve, we needed O(n) space which may not be feasible for large n. Here we need O(√n) space and we process smaller ranges at a time (locality of reference)
32+
33+
## Result
34+
35+
```bash
36+
go test -v
37+
=== RUN TestPrimes
38+
=== RUN TestPrimes/10_value
39+
=== RUN TestPrimes/20_value
40+
=== RUN TestPrimes/99_value
41+
--- PASS: TestPrimes (0.00s)
42+
--- PASS: TestPrimes/10_value (0.00s)
43+
--- PASS: TestPrimes/20_value (0.00s)
44+
--- PASS: TestPrimes/99_value (0.00s)
45+
PASS
46+
```

numbers/segmented/primes.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package primes
2+
3+
import "math"
4+
5+
func segmentedSieve(num int) []int {
6+
limit := int(math.Floor(math.Sqrt(float64(num))) + 1)
7+
primes := getPrimes(limit)
8+
9+
result := []int{}
10+
11+
low := limit
12+
high := limit * 2
13+
14+
for low < num {
15+
if high >= num {
16+
high = num
17+
}
18+
19+
// mark primes in current range. A value in mark[i]
20+
// will finally be false if 'i-low' is Not a prime, else true.
21+
mark := make([]bool, limit+1)
22+
for i := 0; i < limit+1; i++ {
23+
mark[i] = true
24+
}
25+
26+
for i := 0; i < len(primes); i++ {
27+
28+
//find the minimum number in [low..high] that is a multiple of prime[i]
29+
loLim := int(math.Floor(float64(low/primes[i])) * float64(primes[i]))
30+
31+
if loLim < low {
32+
loLim += primes[i]
33+
}
34+
// mark multiples of prime[i] in [low..high]: marking j - low for j
35+
for j := loLim; j < high; j += primes[i] {
36+
mark[j-low] = false
37+
}
38+
}
39+
40+
for i := low; i < high; i++ {
41+
if mark[i-low] {
42+
result = append(result, i)
43+
}
44+
}
45+
46+
// update low and high for next segment
47+
low = low + limit
48+
high = high + limit
49+
50+
}
51+
primes = append(primes, result...)
52+
return primes
53+
}
54+
55+
func getPrimes(num int) []int {
56+
numbers := make([]bool, num)
57+
58+
//create the slice of bools and mark initially all true
59+
//a value in numbers[i] will finally be false if i is Not a prime, else true.
60+
for i := 0; i < num; i++ {
61+
numbers[i] = true
62+
}
63+
64+
for p := 2; p*p < num; p++ {
65+
//if numbers[p] is not changed, then it is a prime
66+
// this selects only the alreade identified prime numbers
67+
if numbers[p] {
68+
// search for multiple of p, and mark them as false
69+
for i := p * p; i < num; i += p {
70+
numbers[i] = false
71+
}
72+
}
73+
}
74+
75+
//create the list that containts only the prime numbers
76+
primes := []int{}
77+
for i := 2; i < num; i++ {
78+
if numbers[i] {
79+
primes = append(primes, i)
80+
}
81+
}
82+
return primes
83+
}

numbers/segmented/primes_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package primes
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestPrimes(t *testing.T) {
9+
for _, tc := range testcases {
10+
t.Run(tc.name, func(t *testing.T) {
11+
result := segmentedSieve(tc.num)
12+
if !reflect.DeepEqual(result, tc.expected) {
13+
t.Errorf("Expected: %d, got: %d", tc.expected, result)
14+
}
15+
})
16+
}
17+
}

numbers/segmented/test_cases.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package primes
2+
3+
var testcases = []struct {
4+
name string
5+
num int
6+
expected []int
7+
}{
8+
{
9+
name: "10_value",
10+
num: 10,
11+
expected: []int{2, 3, 5, 7},
12+
},
13+
{
14+
name: "20_value",
15+
num: 20,
16+
expected: []int{2, 3, 5, 7, 11, 13, 17, 19},
17+
},
18+
{
19+
name: "99_value",
20+
num: 99,
21+
expected: []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97},
22+
},
23+
}

0 commit comments

Comments
 (0)