Emirp
Challenge: Reversible Primes
A prime number is a number that is only divisible by 1 and itself, for example, 7
. An emirp is a prime number that results in a different prime when its decimal digits are reversed. For example, 107
and 701
are a pair of emirps, and 3,049
and 9,403
.
Palindromic numbers are not emirps. 101
is a prime and its reverse is itself -- it is not an emirp.
Your task for this challenge is write a generator that will add up all the first n
emirps. To be precise, you should write a generator emirp
which takes a @ud
number n
as an input, and returns a @ud
number which is the sum of the first n
emirps.
Example usage:
> +emirp 10
638
The first 10 emirps are 13, 17, 31, 37, 71, 73, 79, 97, 107, 113
, and their sum is 638
.
Solutions
These solutions were submitted by the Urbit community as part of a competition in ~2024.3. They are made available under the MIT License and CC0. We ask you to acknowledge authorship should you utilize these elsewhere.
Solution #1
By ~nodmel-todsyr. A very fast and efficient solution.
:: emirp.hoon
:: Finds a sum of first n emirp numbers
|^
|= [n=@ud]
=/ i 0
=/ sum 0
=/ number 13
=| emirps=(set @ud)
|-
?: =(i n)
sum
=^ x=@ud emirps (find-emirp number emirps)
%= $
i +(i)
sum (add sum x)
:: iterating only over 6k +- 1 numbers
number (add ?:(=((mod x 6) 1) 4 2) x)
==
:: Finds a smallest emirp which is greater than or equal to x.
:: Adds flipped emirp to the set of emirps
:: If emirp is in the set, returns it immediately
::
++ find-emirp
|= [x=@ud emirps=(set @ud)]
^- [@ud (set @ud)]
=/ flipped (flip x)
?: (~(has in emirps) x)
[x emirps]
?: &(!=(x flipped) (is-prime x) (is-prime flipped))
[x (~(put in emirps) flipped)]
$(x (add ?:(=((mod x 6) 1) 4 2) x))
:: Checks if x is a prime.
::
++ is-prime
|= [x=@ud]
^- ?
?: (lte x 3)
(gth x 1)
?: |(=((mod x 2) 0) =((mod x 3) 0))
%.n
=/ limit p:(sqt x)
=/ j 5
|-
?: (gth j limit)
%.y
?: |(=((mod x j) 0) =((mod x (add 2 j)) 0))
%.n
$(j (add j 6))
:: Flips a number.
::
++ flip
|= [number=@ud]
^- @ud
=/ m 0
=/ rip (curr div 10)
=/ last (curr mod 10)
|-
?: =(number 0)
m
%= $
number (rip number)
m (add (mul 10 m) (last number))
==
--
Solution #2
By ~ramteb-tinmut. Well-commented, easy to read, and fast.
:: emirp.hoon
:: Return the sum of the first n emirp numbers
::
|= target=@ud
=/ emirp-candidate 13 :: starting at the first emirp makes sense
=| emirps=(list @ud)
=<
sieve
|%
++ sieve
:: When the target is reached, sum the list of emirps
::
?: =(target (lent emirps))
|-
?~ emirps
0
(add i.emirps $(emirps t.emirps))
:: Whilst below target, recurse on is-emirp after incrementing
::
%= sieve
emirps (is-emirp emirp-candidate)
emirp-candidate (add emirp-candidate 1)
==
++ is-emirp
|= candidate=@ud
=/ reversed (reverse-ud candidate)
:: Check if candidate number is a palindrome
::
?: !=(reversed candidate)
:: Is it also a prime?
::
?: (is-prime [candidate (sqt candidate)])
:: Check if the reverse is also a prime:
::
?: (is-prime [reversed (sqt reversed)])
:: Success! - store the emirp
::
(into emirps 1 candidate)
:: The reverse is not a prime:
::
emirps
:: Not prime
::
emirps
:: Palindrome & invalid
::
emirps
++ is-prime
=/ divisor 2
|= [candidate=@ud root=[@ @]]
:: Fastest check first - has the divisor reached our input candidate?
::
?: =(candidate divisor)
%.y
:: Ensure non-zero modulo
::
?: =((mod candidate divisor) 0)
%.n
:: If not a prime, then number must have a divisor less than or equal to its square root. There's an edge case if the square root is not a whole number, in which case we need to round up:
::
?: &(=(+3.root 0) (gth divisor +2.root))
%.y
?: (gth divisor (add 1 +2.root))
%.y
:: Increment divisor and repeat
::
%= $
divisor (add divisor 1)
==
++ reverse-ud
|= number=@ud
^- @ud
=/ reversed 0
:: Return reversed @ud when number reaches zero
::
|-
?: =(0 number)
reversed
:: Otherwise loop until all digits are swapped:
::
=. reversed (add (mul 10 reversed) (mod number 10))
$(number (div number 10))
--
Unit Tests
Following a principle of test-driven development, the unit tests below allow us to check for expected behavior. To run the tests yourself, follow the instructions in the Unit Tests section.
/+ *test
/= emirp /gen/emirp
|%
++ test-01
%+ expect-eq
!> `@ud`13
!> (emirp 1)
++ test-02
%+ expect-eq
!> `@ud`30
!> (emirp 2)
++ test-03
%+ expect-eq
!> `@ud`169
!> (emirp 5)
++ test-04
%+ expect-eq
!> `@ud`638
!> (emirp 10)
++ test-05
%+ expect-eq
!> `@ud`6.857
!> (emirp 25)
++ test-06
%+ expect-eq
!> `@ud`32.090
!> (emirp 50)
++ test-07
%+ expect-eq
!> `@ud`115.370
!> (emirp 100)
++ test-08
%+ expect-eq
!> `@ud`4.509.726
!> (emirp 500)
--
Last updated