How to resolve the algorithm Sieve of Eratosthenes step by step in the Scala programming language
Published on 12 May 2024 09:40 PM
How to resolve the algorithm Sieve of Eratosthenes step by step in the Scala programming language
Table of Contents
Problem Statement
The Sieve of Eratosthenes is a simple algorithm that finds the prime numbers up to a given integer.
Implement the Sieve of Eratosthenes algorithm, with the only allowed optimization that the outer loop can stop at the square root of the limit, and the inner loop may start at the square of the prime just found. That means especially that you shouldn't optimize by using pre-computed wheels, i.e. don't assume you need only to cross out odd numbers (wheel based on 2), numbers equal to 1 or 5 modulo 6 (wheel based on 2 and 3), or similar wheels based on low primes. If there's an easy way to add such a wheel based optimization, implement it as an alternative version.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Sieve of Eratosthenes step by step in the Scala programming language
Source code in the scala programming language
import scala.annotation.tailrec
import scala.collection.parallel.mutable
import scala.compat.Platform
object GenuineEratosthenesSieve extends App {
def sieveOfEratosthenes(limit: Int) = {
val (primes: mutable.ParSet[Int], sqrtLimit) = (mutable.ParSet.empty ++ (2 to limit), math.sqrt(limit).toInt)
@tailrec
def prim(candidate: Int): Unit = {
if (candidate <= sqrtLimit) {
if (primes contains candidate) primes --= candidate * candidate to limit by candidate
prim(candidate + 1)
}
}
prim(2)
primes
}
// BitSet toList is shuffled when using the ParSet version. So it has to be sorted before using it as a sequence.
assert(sieveOfEratosthenes(15099480).size == 976729)
println(s"Successfully completed without errors. [total ${Platform.currentTime - executionStart} ms]")
}
object SoEwithBitSet {
def makeSoE_PrimesTo(top: Int): Iterator[Int] = {
val topNdx = (top - 3) / 2 //odds composite BitSet buffer offset down to 3
val cmpsts = new scala.collection.mutable.BitSet(topNdx + 1) //size includes topNdx
@inline def cullPrmCmpsts(prmNdx: Int) = {
val prm = prmNdx + prmNdx + 3; cmpsts ++= ((prm * prm - 3) >>> 1 to topNdx by prm) }
(0 to (Math.sqrt(top).toInt - 3) / 2).filterNot { cmpsts }.foreach { cullPrmCmpsts }
Iterator.single(2) ++ (0 to topNdx).filterNot { cmpsts }.map { pi => pi + pi + 3 } }
}
object SoEwithArray {
def makeSoE_PrimesTo(top: Int) = {
import scala.annotation.tailrec
val topNdx = (top - 3) / 2 + 1 //odds composite BitSet buffer offset down to 3 plus 1 for overflow
val (cmpsts, sqrtLmtNdx) = (new Array[Int]((topNdx >>> 5) + 1), (Math.sqrt(top).toInt - 3) / 2)
@inline def isCmpst(ci: Int): Boolean = (cmpsts(ci >>> 5) & (1 << (ci & 31))) != 0
@inline def setCmpst(ci: Int): Unit = cmpsts(ci >>> 5) |= 1 << (ci & 31)
@tailrec def forCndNdxsFrom(cndNdx: Int): Unit =
if (cndNdx <= sqrtLmtNdx) {
if (!isCmpst(cndNdx)) { //is prime
val p = cndNdx + cndNdx + 3
@tailrec def cullPrmCmpstsFrom(cmpstNdx: Int): Unit =
if (cmpstNdx <= topNdx) { setCmpst(cmpstNdx); cullPrmCmpstsFrom(cmpstNdx + p) }
cullPrmCmpstsFrom((p * p - 3) >>> 1) }
forCndNdxsFrom(cndNdx + 1) }; forCndNdxsFrom(0)
@tailrec def getNxtPrmFrom(cndNdx: Int): Int =
if ((cndNdx > topNdx) || !isCmpst(cndNdx)) cndNdx + cndNdx + 3 else getNxtPrmFrom(cndNdx + 1)
Iterator.single(2) ++ Iterator.iterate(3)(p => getNxtPrmFrom(((p + 2) - 3) >>> 1)).takeWhile(_ <= top)
}
}
object Main extends App {
import SoEwithArray._
val top_num = 100000000
val strt = System.nanoTime()
val count = makeSoE_PrimesTo(top_num).size
val end = System.nanoTime()
println(s"Successfully completed without errors. [total ${(end - strt) / 1000000} ms]")
println(f"Found $count primes up to $top_num" + ".")
println("Using one large mutable Array and tail recursive loops.")
}
object APFSoEPagedOdds {
import scala.annotation.tailrec
private val CACHESZ = 1 << 18 //used cache buffer size
private val PGSZ = CACHESZ / 4 //number of int's in cache
private val PGBTS = PGSZ * 32 //number of bits in buffer
//processing output type is a tuple of low bit (odds) address,
// bit range size, and the actual culled page segment buffer.
private type Chunk = (Long, Int, Array[Int])
//produces an iteration of all the primes from an iteration of Chunks
private def enumChnkPrms(chnks: Stream[Chunk]): Iterator[Long] = {
def iterchnk(chnk: Chunk) = { //iterating primes per Chunk
val (lw, rng, bf) = chnk
@tailrec def nxtpi(i: Int): Int = { //find next prime index not composite
if (i < rng && (bf(i >>> 5) & (1 << (i & 31))) != 0) nxtpi(i + 1) else i }
Iterator.iterate(nxtpi(0))(i => nxtpi(i + 1)).takeWhile { _ < rng }
.map { i => ((lw + i) << 1) + 3 } } //map from index to prime value
chnks.toIterator.flatMap { iterchnk } }
//culls the composite number bit representations from the bit-packed page buffer
//using a given source of a base primes iterator
private def cullPg(bsprms: Iterator[Long],
lowi: Long, buf: Array[Int]): Unit = {
//cull for all base primes until square >= nxt
val rng = buf.length * 32; val nxt = lowi + rng
@tailrec def cull(bps: Iterator[Long]): Unit = {
//given prime then calculate the base start address for prime squared
val bp = bps.next(); val s = (bp * bp - 3) / 2
//almost all of the execution time is spent in the following tight loop
@tailrec def cullp(j: Int): Unit = { //cull the buffer for given prime
if (j < rng) { buf(j >>> 5) |= 1 << (j & 31); cullp(j + bp.toInt) } }
if (s < nxt) { //only cull for primes squared less than max
//calculate the start address within this given page segment
val strt = if (s >= lowi) (s - lowi).toInt else {
val b = (lowi - s) % bp
if (b == 0) 0 else (bp - b).toInt }
cullp(strt); if (bps.hasNext) cull(bps) } } //loop for all primes in range
//for the first page, use own bit pattern as a source of base primes
//if another source is not given
if (lowi <= 0 && bsprms.isEmpty)
cull(enumChnkPrms(Stream((0, buf.length << 5, buf))))
//otherwise use the given source of base primes
else if (bsprms.hasNext) cull(bsprms) }
//makes a chunk given a low address in (odds) bits
private def mkChnk(lwi: Long): Chunk = {
val rng = PGBTS; val buf = new Array[Int](rng / 32);
val bps = if (lwi <= 0) Iterator.empty else enumChnkPrms(basePrms)
cullPg(bps, lwi, buf); (lwi, rng, buf) }
//new independent source of base primes in a stream of packed-bit arrays
//memoized by converting it to a Stream and retaining a reference here
private val basePrms: Stream[Chunk] =
Stream.iterate(mkChnk(0)) { case (lw, rng, bf) => { mkChnk(lw + rng) } }
//produces an infinite iterator over all the chunk results
private def itrRslts[R](rsltf: Chunk => R): Iterator[R] = {
def mkrslt(lwi: Long) = { //makes tuple of result and next low index
val c = mkChnk(lwi); val (_, rng, _) = c; (rsltf(c), lwi + rng) }
Iterator.iterate(mkrslt(0)) { case (_, nlwi) => mkrslt(nlwi) }
.map { case (rslt, _) => rslt} } //infinite iteration of results
//iterates across the "infinite" produced output primes
def enumSoEPrimes(): Iterator[Long] = //use itrRsltsMP to produce Chunks iteration
Iterator.single(2L) ++ enumChnkPrms(itrRslts { identity }.toStream)
//counts the number of remaining primes in a page segment buffer
//using a very fast bitcount per integer element
//with special treatment for the last page
private def countpgto(top: Long, b: Array[Int], nlwp: Long) = {
val numbts = b.length * 32; val prng = numbts * 2
@tailrec def cnt(i: Int, c: Int): Int = { //tight int bitcount loop
if (i >= b.length) c else cnt (i + 1, c - Integer.bitCount(b(i))) }
if (nlwp > top) { //for top in page, calculate int address containing top
val bi = ((top - nlwp + prng) >>> 1).toInt
val w = bi >>> 5; b(w) |= -2 << (bi & 31) //mark all following as composite
for (i <- w + 1 until b.length) b(i) = -1 } //for all int's to end of buffer
cnt(0, numbts) } //counting the entire buffer in every case
//counts all the primes up to a top value
def countSoEPrimesTo(top: Long): Long = {
if (top < 2) return 0L else if (top < 3) return 1L //no work necessary
//count all Chunks using multi-processing
val gen = itrRslts { case (lwi, rng, bf) =>
val nlwp = (lwi + rng) * 2 + 3; (countpgto(top, bf, nlwp), nlwp) }
//a loop to take Chunk's up to including top limit but not past it
@tailrec def takeUpto(acc: Long): Long = {
val (cnt, nlwp) = gen.next(); val nacc = acc + cnt
if (nlwp <= top) takeUpto(nacc) else nacc }; takeUpto(1) }
}
object MainSoEPagedOdds extends App {
import APFSoEPagedOdds._
countSoEPrimesTo(100)
val top = 1000000000
val strt = System.currentTimeMillis()
val cnt = enumSoEPrimes().takeWhile { _ <= top }.length
// val cnt = countSoEPrimesTo(top)
val elpsd = System.currentTimeMillis() - strt
println(f"Found $cnt primes up to $top in $elpsd milliseconds.")
}
def birdPrimes() = {
def oddPrimes: Stream[Int] = {
def merge(xs: Stream[Int], ys: Stream[Int]): Stream[Int] = {
val (x, y) = (xs.head, ys.head)
if (y > x) x #:: merge(xs.tail, ys) else if (x > y) y #:: merge(xs, ys.tail) else x #:: merge(xs.tail, ys.tail)
}
def primeMltpls(p: Int): Stream[Int] = Stream.iterate(p * p)(_ + p + p)
def allMltpls(ps: Stream[Int]): Stream[Stream[Int]] = primeMltpls(ps.head) #:: allMltpls(ps.tail)
def join(ams: Stream[Stream[Int]]): Stream[Int] = ams.head.head #:: merge(ams.head.tail, join(ams.tail))
def oddPrms(n: Int, composites: Stream[Int]): Stream[Int] =
if (n >= composites.head) oddPrms(n + 2, composites.tail) else n #:: oddPrms(n + 2, composites)
//following uses a new recursive source of odd base primes
3 #:: oddPrms(5, join(allMltpls(oddPrimes)))
}
2 #:: oddPrimes
}
class CIS[A](val start: A, val continue: () => CIS[A])
def primesBirdCIS: Iterator[Int] = {
def merge(xs: CIS[Int], ys: CIS[Int]): CIS[Int] = {
val (x, y) = (xs.start, ys.start)
if (y > x) new CIS(x, () => merge(xs.continue(), ys))
else if (x > y) new CIS(y, () => merge(xs, ys.continue()))
else new CIS(x, () => merge(xs.continue(), ys.continue()))
}
def primeMltpls(p: Int): CIS[Int] = {
def nextCull(cull: Int): CIS[Int] = new CIS[Int](cull, () => nextCull(cull + 2 * p))
nextCull(p * p)
}
def allMltpls(ps: CIS[Int]): CIS[CIS[Int]] =
new CIS[CIS[Int]](primeMltpls(ps.start), () => allMltpls(ps.continue()))
def join(ams: CIS[CIS[Int]]): CIS[Int] = {
new CIS[Int](ams.start.start, () => merge(ams.start.continue(), join(ams.continue())))
}
def oddPrimes(): CIS[Int] = {
def oddPrms(n: Int, composites: CIS[Int]): CIS[Int] = { //"minua"
if (n >= composites.start) oddPrms(n + 2, composites.continue())
else new CIS[Int](n, () => oddPrms(n + 2, composites))
}
//following uses a new recursive source of odd base primes
new CIS(3, () => oddPrms(5, join(allMltpls(oddPrimes()))))
}
Iterator.single(2) ++ Iterator.iterate(oddPrimes())(_.continue()).map(_.start)
}
def SoEInc: Iterator[Int] = {
val nextComposites = scala.collection.mutable.HashMap[Int, Int]()
def oddPrimes: Iterator[Int] = {
val basePrimes = SoEInc
basePrimes.next()
basePrimes.next() // skip the two and three prime factors
@tailrec def makePrime(state: (Int, Int, Int)): (Int, Int, Int) = {
val (candidate, nextBasePrime, nextSquare) = state
if (candidate >= nextSquare) {
val adv = nextBasePrime << 1
nextComposites += ((nextSquare + adv) -> adv)
val np = basePrimes.next()
makePrime((candidate + 2, np, np * np))
} else if (nextComposites.contains(candidate)) {
val adv = nextComposites(candidate)
nextComposites -= (candidate) += (Iterator.iterate(candidate + adv)(_ + adv)
.dropWhile(nextComposites.contains(_)).next() -> adv)
makePrime((candidate + 2, nextBasePrime, nextSquare))
} else (candidate, nextBasePrime, nextSquare)
}
Iterator.iterate((5, 3, 9)) { case (c, p, q) => makePrime((c + 2, p, q)) }
.map { case (p, _, _) => p }
}
List(2, 3).toIterator ++ oddPrimes
}
You may also check:How to resolve the algorithm Count the coins step by step in the Racket programming language
You may also check:How to resolve the algorithm Pythagorean quadruples step by step in the FreeBASIC programming language
You may also check:How to resolve the algorithm XML/DOM serialization step by step in the AArch64 Assembly programming language
You may also check:How to resolve the algorithm Create a file step by step in the AArch64 Assembly programming language
You may also check:How to resolve the algorithm User input/Text step by step in the Oz programming language