Circular primes

From Rosetta Code
Revision as of 19:57, 8 April 2020 by Petelomax (talk | contribs) (Julia comparison)
Circular primes is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Definitions

A circular prime is a prime number with the property that the number generated at each intermediate step when cyclically permuting its (base 10) digits will also be prime.

For example: 1193 is a circular prime, since 1931, 9311 and 3119 are all also prime.

Note that a number which is a cyclic permutation of a smaller circular prime is not considered to be itself a circular prime. So 13 is a circular prime but 31 is not.


A repunit (denoted by R) is a number whose base 10 representation contains only the digit 1.

For example: R(2) = 11 and R(5) = 11111 are repunits.


Task
  • Find the first 19 circular primes.


  • If your language has access to arbitrary precision integer arithmetic, given that they are all repunits, find the next 4 circular primes.


  • (Stretch) Determine which of the following repunits are probably circular primes: R(5003), R(9887), R(15073), R(25031), R(35317) and R(49081). The larger ones may take a long time to process so just do as many as you reasonably can.


See also


Factor

Unfortunately Factor's miller-rabin test or bignums aren't quite up to the task of finding the next four circular prime repunits in a reasonable time. It takes ~90 seconds to check R(7)-R(1031).

Works with: Factor version 0.99 2020-03-02

<lang factor>USING: combinators.short-circuit formatting io kernel lists lists.lazy math math.combinatorics math.functions math.parser math.primes sequences sequences.extras ;

! Create an ordered infinite lazy list of circular prime ! "candidates" -- the numbers 2, 3, 5 followed by numbers ! composed of only the digits 1, 3, 7, and 9.

candidates ( -- list )
   L{ "2" "3" "5" "7" } 2 lfrom
   [ "1379" swap selections >list ] lmap-lazy lconcat lappend ;
circular-prime? ( str -- ? )
   all-rotations {
       [ [ infimum ] [ first = ] bi ]
       [ [ string>number prime? ] all? ]
   } 1&& ;
circular-primes ( -- list )
   candidates [ circular-prime? ] lfilter ;
prime-repunits ( -- list )
   7 lfrom [ 10^ 1 - 9 / prime? ] lfilter ;

"The first 19 circular primes are:" print 19 circular-primes ltake [ write bl ] leach nl nl

"The next 4 circular primes, in repunit format, are:" print 4 prime-repunits ltake [ "R(%d) " printf ] leach nl</lang>

Output:
The first 19 circular primes are:
2 3 5 7 11 13 17 37 79 113 197 199 337 1193 3779 11939 19937 193939 199933 

The next 4 circular primes, in repunit format, are:
R(19) R(23) R(317) R(1031) 

Go

<lang go>package main

import (

   "fmt"
   big "github.com/ncw/gmp"
   "strings"

)

// OK for 'small' numbers. func isPrime(n int) bool {

   switch {
   case n < 2:
       return false
   case n%2 == 0:
       return n == 2
   case n%3 == 0:
       return n == 3
   default:
       d := 5
       for d*d <= n {
           if n%d == 0 {
               return false
           }
           d += 2
           if n%d == 0 {
               return false
           }
           d += 4
       }
       return true
   }

}

func repunit(n int) *big.Int {

   ones := strings.Repeat("1", n)
   b, _ := new(big.Int).SetString(ones, 10)
   return b

}

var circs = []int{}

// binary search is overkill for a small number of elements func alreadyFound(n int) bool {

   for _, i := range circs {
       if i == n {
           return true
       }
   }
   return false

}

func isCircular(n int) bool {

   nn := n
   pow := 1 // will eventually contain 10 ^ d where d is number of digits in n
   for nn > 0 {
       pow *= 10
       nn /= 10
   }
   nn = n
   for {
       nn *= 10
       f := nn / pow // first digit
       nn += f * (1 - pow)
       if alreadyFound(nn) {
           return false
       }
       if nn == n {
           break
       }
       if !isPrime(nn) {
           return false
       }
   }
   return true

}

func main() {

   fmt.Println("The first 19 circular primes are:")
   digits := [4]int{1, 3, 7, 9}
   q := []int{1, 2, 3, 5, 7, 9}  // queue the numbers to be examined
   fq := []int{1, 2, 3, 5, 7, 9} // also queue the corresponding first digits
   count := 0
   for {
       f := q[0]   // peek first element
       fd := fq[0] // peek first digit
       if isPrime(f) && isCircular(f) {
           circs = append(circs, f)
           count++
           if count == 19 {
               break
           }
       }
       copy(q, q[1:])   // pop first element
       q = q[:len(q)-1] // reduce length by 1
       copy(fq, fq[1:]) // ditto for first digit queue
       fq = fq[:len(fq)-1]
       if f == 2 || f == 5 { // if digits > 1 can't contain a 2 or 5
           continue
       }
       // add numbers with one more digit to queue
       // only numbers whose last digit >= first digit need be added
       for _, d := range digits {
           if d >= fd {
               q = append(q, f*10+d)
               fq = append(fq, fd)
           }
       }
   }
   fmt.Println(circs)
   fmt.Println("\nThe next 4 circular primes, in repunit format, are:")
   count = 0
   var rus []string
   for i := 7; count < 4; i++ {
       if repunit(i).ProbablyPrime(10) {
           count++
           rus = append(rus, fmt.Sprintf("R(%d)", i))
       }
   }
   fmt.Println(rus)
   fmt.Println("\nThe following repunits are probably circular primes:")
   for _, i := range []int{5003, 9887, 15073, 25031, 35317, 49081} {
       fmt.Printf("R(%-5d) : %t\n", i, repunit(i).ProbablyPrime(10))
   }

}</lang>

Output:
The first 19 circular primes are:
[2 3 5 7 11 13 17 37 79 113 197 199 337 1193 3779 11939 19937 193939 199933]

The next 4 circular primes, in repunit format, are:
[R(19) R(23) R(317) R(1031)]

The following repunits are probably circular primes:
R(5003 ) : false
R(9887 ) : false
R(15073) : false
R(25031) : false
R(35317) : false
R(49081) : true

Julia

Note that the evalpoly function used in this program was added in Julia 1.4 <lang julia>using Lazy, Primes

function iscircularprime(n)

   !isprime(n) && return false
   dig = digits(n)
   return all(i -> (m = evalpoly(10, circshift(dig, i))) >= n && isprime(m), 1:length(dig)-1)

end

filtcircular(n, rang) = Int.(collect(take(n, filter(iscircularprime, rang)))) isprimerepunit(n) = isprime(evalpoly(BigInt(10), ones(Int, n))) filtrep(n, rang) = collect(take(n, filter(isprimerepunit, rang)))

println("The first 19 circular primes are:\n", filtcircular(19, Lazy.range(2))) print("\nThe next 4 circular primes, in repunit format, are: ",

   mapreduce(n -> "R($n) ", *, filtrep(4, Lazy.range(6))))

println("\n\nChecking larger repunits:") for i in [5003, 9887, 15073, 25031, 35317, 49081]

   println("R($i) is ", isprimerepunit(i) ? "prime." : "not prime.")

end

</lang>

Output:
The first 19 circular primes are:
[2, 3, 5, 7, 11, 13, 17, 37, 79, 113, 197, 199, 337, 1193, 3779, 11939, 19937, 193939, 199933]

The next 4 circular primes, in repunit format, are: R(19) R(23) R(317) R(1031)

Checking larger repunits:
R(5003) is not prime.
R(9887) is not prime.
R(15073) is not prime.
R(25031) is not prime.
R(35317) is not prime.
R(49081) is prime.

Phix

<lang Phix>function circular(integer p)

   integer len = length(sprintf("%d",p)),
           pow = power(10,len-1),
           p0 = p
   for i=1 to len-1 do
       p = pow*remainder(p,10)+floor(p/10)
       if p<p0 or not is_prime(p) then return false end if
   end for
   return true

end function

sequence c = {} integer n = 1 while length(c)<19 do

   integer p = get_prime(n)
   if circular(p) then c &= p end if
   n += 1

end while printf(1,"The first 19 circular primes are:\n%v\n\n",{c})

include mpfr.e procedure repunit(mpz z, integer n)

   mpz_set_str(z,repeat('1',n))

end procedure

c = {} n = 7 mpz z = mpz_init() randstate state = gmp_randinit_mt() while length(c)<4 do

   repunit(z,n)
   if mpz_probable_prime_p(z,state) then
       c = append(c,sprintf("R(%d)",n))
   end if
   n += 1

end while printf(1,"The next 4 circular primes, in repunit format, are:\n%s\n\n",{join(c)})</lang>

Output:
The first 19 circular primes are:
{2,3,5,7,11,13,17,37,79,113,197,199,337,1193,3779,11939,19937,193939,199933}

The next 4 circular primes, in repunit format, are:
R(19) R(23) R(317) R(1031)

stretch

<lang Phix>constant t = {5003, 9887, 15073, 25031, 35317, 49081} printf(1,"The following repunits are probably circular primes:\n") for i=1 to length(t) do

   integer ti = t[i]
   atom t0 = time()
   repunit(z,ti)
   bool bPrime = mpz_probable_prime_p(z,state,1)
   printf(1,"R(%d) : %t (%s)\n", {ti, bPrime, elapsed(time()-t0)})

end for</lang>

Output:

64-bit can only cope with the first five (it terminates abruptly on the sixth)
For comparison, the above Julia code (8/4/20, 64 bit) manages 1s, 5.6s, 15s, 50s, 1 min 50s (and 1 hour 45 min 40s) on the same box.

The following repunits are probably circular primes:
R(5003) : false (2.0s)
R(9887) : false (13.5s)
R(15073) : false (45.9s)
R(25031) : false (1 minute and 19s)
R(35317) : false (3 minutes and 04s)

32-bit is much slower and can only cope with the first four

The following repunits are probably circular primes:
R(5003) : false (10.2s)
R(9887) : false (54.9s)
R(15073) : false (2 minutes and 22s)
R(25031) : false (7 minutes and 45s)
diag looping, error code is 1, era is #00644651

Raku

Most of the repunit testing is relatively speedy using the ntheory library. The really slow ones are R(25031), at ~42 seconds and R(49081) at 922(!!) seconds.

<lang perl6>#!/usr/bin/env raku

  1. 20200406 Raku programming solution

sub isCircular(\n) {

  return False unless n.is-prime;
  my @circular = n.comb;
  return False if @circular.min < @circular[0];
  for 1 ..^ @circular -> $i {
     return False unless .is-prime and $_ >= n given @circular.rotate($i).join;
  }
  True

}

say "The first 19 circular primes are:"; say ((2..*).hyper.grep: { isCircular $_ })[^19];

say "\nThe next 4 circular primes, in repunit format, are:"; loop ( my $i = 7, my $count = 0; $count < 4; $i++ ) {

  ++$count, say "R($i)" if (1 x $i).is-prime

}

use ntheory:from<Perl5> qw[is_prime];

say "\nRepunit testing:";

(5003, 9887, 15073, 25031, 35317, 49081).map: {

   my $now = now;
   say "R($_): Prime? ", ?is_prime("{1 x $_}"), "  {(now - $now).fmt: '%.2f'}"

}</lang>

Output:
The first 19 circular primes are:
(2 3 5 7 11 13 17 37 79 113 197 199 337 1193 3779 11939 19937 193939 199933)

The next 4 circular primes, in repunit format, are:
R(19)
R(23)
R(317)
R(1031)

Repunit testing:
R(5003): Prime? False  0.00
R(9887): Prime? False  0.01
R(15073): Prime? False  0.02
R(25031): Prime? False  41.40
R(35317): Prime? False  0.32
R(49081): Prime? True  921.73

REXX

By far, the greatest cost of CPU time is in the generation of a suitable number of primes. <lang rexx>/*REXX program finds & displays circular primes (with a title & in a horizontal format).*/ parse arg N hp . /*obtain optional arguments from the CL*/ if N== | N=="," then N= 19 /* " " " " " " */ if hp== | hp=="," then hp= 1000000 /* " " " " " " */ call genP /*gen primes up to hp (200,000). */ q= 024568 /*digs that most circular P can't have.*/

  1. =0; $= /*#: circular P count; $: is a list.*/
     do j=2  until #==N                         /* [↓]   traipse through the number(s).*/
     if \!.j                       then iterate /*Is  J  not prime?   Then skip number.*/
     if j>9 & verify(j, q, 'M')>0  then iterate /*Does J contain forbidden digs?  Skip.*/
     if \circP(j)                  then iterate /*Not circular?  Then skip this number.*/
     #= # + 1                                   /*bump the  count  of circular primes. */
     $= $ j                                     /*add this prime number  ──►  $  list. */
     end   /*j*/                                /*at this point, $ has a leading blank.*/

say center(' first' # "circular primes ", 79, '─') say strip($) exit /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ circP: procedure expose @. !.; parse arg x 1 ox /*obtain a prime number to be examined.*/

               do  length(x)-1                  /*parse  X  number, rotating the digits*/
               parse var  x    2  y  1  f  2    /*get rightmost digs & the first digit.*/
               x= y || f                        /*construct a new possible circular P. */
               if x<ox  then return 0           /*is number < the original?  ¬ circular*/
               if \!.x  then return 0           /* "    "   not prime?       ¬ circular*/
               end   /*len···*/
      return 1                                  /*passed all tests,  X is a circular P.*/

/*──────────────────────────────────────────────────────────────────────────────────────*/ genP: @.1=2; @.2=3; @.3=5; @.4=7; @.5=11; @.6= 13; nP=6 /*assign low primes; # primes. */

     !.= 0; !.2=1; !.3=1; !.5=1; !.7=1; !.11=1; !.13=1  /*assign primality to numbers. */
      do j=@.nP+4  by 2  to hp                  /*only find odd primes from here on.   */
      if j// 3==0  then iterate                 /*is J divisible by  #3  Then not prime*/
      parse var j  -1 _;if _==5  then iterate /*Is last digit a "5"?     "   "    "  */
      if j// 7==0  then iterate                 /*is J divisible by  7?    "   "    "  */
      if j//11==0  then iterate                 /* " "     "      " 11?    "   "    "  */
      if j//13==0  then iterate                 /*is "     "      " 13?    "   "    "  */
          do k=7  while k*k<=j                  /*divide by some generated odd primes. */
          if j // @.k==0  then iterate j        /*Is J divisible by  P?  Then not prime*/
          end   /*k*/                           /* [↓]  a prime  (J)  has been found.  */
      nP= nP+1;           !.j=1;       @.nP= j  /*bump P cnt;  assign P to @.  and  !. */
      end       /*j*/;    return</lang>
output   when using the default inputs:
────────────────────────── first 19 circular primes ───────────────────────────
2 3 5 7 11 13 17 37 79 113 197 199 337 1193 3779 11939 19937 193939 199933