How can I modify my Akka streams Prime sieve to exclude modulo checks for known primes?

雨燕双飞 提交于 2019-12-23 01:41:32

问题


I wrote a sieve using akka streams to find prime members of an arbitrary source of Int:

object Sieve extends App {

  implicit val system = ActorSystem()
  implicit val mat = ActorMaterializer(ActorMaterializerSettings(system))
  implicit val ctx = implicitly[ExecutionContext](system.dispatcher)

  val NaturalNumbers = Source.fromIterator(() => Iterator.from(2))

  val IsPrimeByEurithmethes: Flow[Int, Int, _] = Flow[Int].filter {
    case n: Int =>
      (2 to Math.floor(Math.sqrt(n)).toInt).par.forall(n % _ != 0)
  }

  NaturalNumbers.via(IsPrimeByEurithmethes).throttle(100000, 1 second, 100000, ThrottleMode.Shaping).to(Sink.foreach(println)).run()
}

Ok, so this appears to work decently well. However, there are at least a few potential areas of concern:

  1. The modulo checks are run using par.forall, ie they are totally hidden within the Flow that filters, but I can see how it would be useful to have a Map from the candidate n to another Map of each n % _. Maybe.
  2. I am checking way too many of the candidates needlessly - both in terms of checking n that I will already know are NOT prime based on previous results, and by checking n % _ that are redundant. In fact, even if I think the n is prime, it suffices to check only the known primes up until that point.

The second point is my more immediate concern.

I think I can prove rather easily that there is a more efficient way - by filtering out the source given each NEW prime.

So then....

2, 3, 4, 5, 6, 7, 8, 9, 10, 11... => (after finding p=2)

2, 3,    5,    7,    9,   , 11... => (after finding p=3)

2, 3,    5,    7,         , 11... => ...

Now after finding a p and filtering the source, we need to know whether the next candidate is a p. Well, we can say for sure it is prime if the largest known prime is greater than its root, which will Always happen I believe, so it suffices to just pick the next element...

2, 3, 4, 5, 6, 7, 8, 9, 10, 11... => (after finding p=2) PICK n(2) = 3

2, 3,    5,    7,    9,   , 11... => (after finding p=3) PICK n(3) = 5

2, 3,    5,    7,         , 11... => (after finding p=5) PICK n(5) = 7

This seems to me like a rewriting of the originally-provided sieve to do far fewer checks at the cost of introducing a strict sequential dependency.

Another idea - I could remove the constraint by working things out in terms of symbols, like the minimum set of modulo checks that necessitate primality, etc.

Am I barking up the wrong tree? IF not, how can I go about messing with my source in this manner?


回答1:


I just started fiddling around with akka streams recently so there might be better solutions than this (especially since the code feels kind of clumsy to me) - but your second point seemed to be just the right challenge for me to try out building a feedback loop within akka streams.

Find my full solution here: https://gist.github.com/MartinHH/de62b3b081ccfee4ae7320298edd81ee

The main idea was to accumulate the primes that are already found and merge them with the stream of incoming natural numbers so the primes-check could be done based on the results up to N like this:

def isPrime(n: Int, primesSoFar: SortedSet[Int]): Boolean =
  !primesSoFar.exists(n % _ == 0) &&
    !(primesSoFar.lastOption.getOrElse(2) to Math.floor(Math.sqrt(n)).toInt).par.exists(n % _ == 0)


来源:https://stackoverflow.com/questions/36381603/how-can-i-modify-my-akka-streams-prime-sieve-to-exclude-modulo-checks-for-known

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!