From 37337b3ea7b3d7da57b9fcec38c6aa89adff1b55 Mon Sep 17 00:00:00 2001 From: trivernis Date: Sat, 3 Feb 2024 17:08:21 +0100 Subject: [PATCH] Improve on the original prime to beat the sieve alorithm --- prime-sieve.roc | 27 +++++++---- prime.roc | 119 ++++++++++++++++++++++++------------------------ 2 files changed, 79 insertions(+), 67 deletions(-) diff --git a/prime-sieve.roc b/prime-sieve.roc index e6f271d..35f4398 100644 --- a/prime-sieve.roc +++ b/prime-sieve.roc @@ -1,31 +1,43 @@ app "prime2" packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.7.1/Icc3xJoIixF3hCcfXrDwLCu4wQHtNdPyoJkEbkgIElA.tar.br" } - imports [pf.Stdout, pf.Task] + imports [pf.Stdout, pf.Task, pf.Arg] provides [main] to pf main : Task.Task {} I32 main = + max <- Task.await parseArg numStrings = - primesIn 50_000_000 + primesIn max |> List.map Num.toStr |> Str.joinWith "\n" Stdout.line "$(numStrings)" +parseArg = + args <- Task.await (Arg.list) + arg = + args + |> List.get 1 + |> Result.withDefault "50_000_000" + |> Str.toU64 + when arg is + Ok a -> a |> Task.ok + Err InvalidNumStr -> crash "Passed argument is not a numberr" + primesIn : U64 -> List U64 primesIn = \n -> numbers = List.concat [2] (List.range { start: At 3u64, end: At n, step: 2 }) - primeSieve numbers + primeSieve numbers n -primeSieve : List U64 -> List U64 -primeSieve = \l -> +primeSieve : List U64, U64 -> List U64 +primeSieve = \l, max -> when l is [] -> [] [a] -> [a] - [head, .., tail] if (Num.powInt head 2) > tail -> l + [head, ..] if (Num.powInt head 2) > max -> l [head, .. as tail] -> filteredList = List.dropIf tail (\e -> Num.isMultipleOf e head) - List.concat [head] (primeSieve filteredList) + List.concat [head] (primeSieve filteredList max) countPrimesIn : U64 -> Nat countPrimesIn = \limit -> @@ -40,7 +52,6 @@ countPrimesIn = \limit -> List.range { start: At 3, end: At (Num.ceiling (Num.sqrt (Num.toF64 n))), step: 2 } |> List.any \a -> Num.isMultipleOf n a |> Bool.not - res expect diff --git a/prime.roc b/prime.roc index 80a9199..bc0bc14 100644 --- a/prime.roc +++ b/prime.roc @@ -1,73 +1,62 @@ app "prime" packages { pf: "https://github.com/roc-lang/basic-cli/releases/download/0.7.1/Icc3xJoIixF3hCcfXrDwLCu4wQHtNdPyoJkEbkgIElA.tar.br" } - imports [pf.Stdout, pf.Task] + imports [pf.Stdout, pf.Task, pf.Arg] provides [main] to pf main : Task.Task {} I32 main = + max <- Task.await parseArg numStrings = - primesIn 50_000_000 + primesIn max |> List.map Num.toStr |> Str.joinWith "\n" Stdout.line "$(numStrings)" -primesIn : U64 -> List U64 -primesIn = \n -> - List.range { start: At 0u64, end: At n } - |> List.map \e -> isPrimePre e - |> List.walk [] primeWalk +parseArg = + args <- Task.await (Arg.list) + arg = + args + |> List.get 1 + |> Result.withDefault "50_000_000" + |> Str.toU64 + when arg is + Ok a -> a |> Task.ok + Err InvalidNumStr -> crash "Passed argument is not a numberr" -PrimeResult : [No U64, Yes U64, Maybe U64] +primesIn : U64 -> List U64 +primesIn = \max -> + filterPrimesRec = \acc, l -> + when l is + [] -> acc + [head, .. as tail] if isPrimePost acc head -> + acc |> List.append head |> filterPrimesRec tail -primeWalk : List U64, PrimeResult -> List U64 -primeWalk = \l, n -> - when n is - Yes n2 -> - List.append l n2 + [_, .. as tail] -> filterPrimesRec acc tail + [2] + |> List.concat (List.range { start: At 3u64, end: At max, step: 2 }) + |> \l -> filterPrimesRec [] l - Maybe n2 -> - if isPrimePost l n2 then - List.append l n2 - else - l +isPrimePost = \list, n -> + isPrimeRec = \l, limit -> + when l is + [_] -> Bool.true + [head, .. as tail] -> + if Num.isMultipleOf n head then + Bool.false + else if head > limit then + Bool.true + else + isPrimeRec tail limit - No _ -> l + [] -> Bool.true + sqrtF = Num.sqrt (Num.toF32 n) + sqrtI = Num.floor sqrtF -isPrimePre : U64 -> PrimeResult -isPrimePre = \n -> - if n < 2 then - No n - else if n < 4 then - Yes n - else if Num.isEven n then - No n - else if - ( - firstPrimes - |> List.dropIf \p -> n < p - |> List.any \p -> Num.isMultipleOf n p - ) - then - No n + if Num.isZero (sqrtF - Num.toF32 sqrtI) then + Bool.false else - Maybe n - -firstPrimes = - [2, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 51, 67, 71, 73, 79, 83, 89, 97] - -isPrimePost : List U64, U64 -> Bool -isPrimePost = \l, n -> - nFloat = Num.toF32 n - valLimit = Num.ceiling (Num.sqrt nFloat) - # approximate the number of primes < sqrt(n) to limit the range we have to check with a 25% error tolerance - valLimitF = Num.toF32 valLimit - limit = Num.ceiling (valLimitF * 1.25 / (Num.log valLimitF)) - l - |> List.sublist { start: 0, len: limit } - |> List.dropIf \p -> p > valLimit - |> List.any \p -> Num.isMultipleOf n p - |> Bool.not + isPrimeRec list (sqrtI + 1) countPrimesIn : U64 -> Nat countPrimesIn = \limit -> @@ -75,14 +64,26 @@ countPrimesIn = \limit -> |> List.countIf \n -> if n < 4 then Bool.true - else if n % 2 == 0 then + else if Num.isEven n then Bool.false else - res = - List.range { start: At 3, end: At (Num.ceiling (Num.sqrt (Num.toF64 n))), step: 2 } - |> List.any \a -> Num.isMultipleOf n a - |> Bool.not + List.range { start: At 3, end: At (Num.ceiling (Num.sqrt (Num.toF64 n))), step: 2 } + |> List.any \a -> Num.isMultipleOf n a + |> Bool.not + +expect + ( + countPrimesIn 1000000 + |> \expected -> + dbg expected + + expected + ) + == ( + List.len (primesIn 1000000) + |> \actual -> + dbg actual - res + actual -expect countPrimesIn 10000 == List.len (primesIn 10000) + )