Elixir Count Semi Primes
defmodule CountSemiPrimes do
def count_semi_primes(n, p, q) do
primes = sieve(n)
semi_flags = semi_prime_flags(n, primes)
prefix = prefix_sums(semi_flags, n)
p
|> Enum.zip(q)
|> Enum.map(fn {pi, qi} -> Map.get(prefix, qi) - Map.get(prefix, pi - 1, 0) end)
end
defp sieve(n) do
initial = for i <- 0..n, into: %{}, do: {i, i >= 2}
limit = :math.sqrt(n) |> trunc()
Enum.reduce(2..limit//1, initial, fn i, primes ->
if Map.get(primes, i) do
Enum.reduce(i * i..n//i, primes, fn k, acc -> Map.put(acc, k, false) end)
else
primes
end
end)
end
defp semi_prime_flags(n, primes) do
initial = for i <- 0..n, into: %{}, do: {i, 0}
limit = :math.sqrt(n) |> trunc()
Enum.reduce(2..limit//1, initial, fn k, flags ->
if Map.get(primes, k) do
mark_multiples(k, 2, n, primes, flags)
else
flags
end
end)
end
defp mark_multiples(k, i, n, _primes, flags) when i * k > n, do: flags
defp mark_multiples(k, i, n, primes, flags) do
flags = if Map.get(primes, i), do: Map.put(flags, k * i, 1), else: flags
mark_multiples(k, i + 1, n, primes, flags)
end
defp prefix_sums(flags, n) do
{result, _last} =
Enum.reduce(1..n//1, {%{0 => Map.get(flags, 0, 0)}, Map.get(flags, 0, 0)}, fn i,
{acc, prev} ->
cur = prev + Map.get(flags, i, 0)
{Map.put(acc, i, cur), cur}
end)
result
end
end
This precomputes semiprimes and prefix sums so each range query becomes a quick subtraction.