问题
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:
- The modulo checks are run using
par.forall
, ie they are totally hidden within theFlow
thatfilter
s, but I can see how it would be useful to have aMap
from thecandidate n
to anotherMap
of eachn % _
. Maybe. - 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 checkingn % _
that are redundant. In fact, even if I think then
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