A recent discovery, quoted from   Quantamagazine   (March 13, 2016):

     Two mathematicians have uncovered a simple, previously unnoticed property of 
     prime numbers — those numbers that are divisible only by 1 and themselves.     
     Prime numbers, it seems, have decided preferences about the final digits of    
     the primes that immediately follow them.  

and

     This conspiracy among prime numbers seems, at first glance, to violate a     
     longstanding assumption in number theory:  that prime numbers behave much    
     like random numbers. 

                            ─── (original authors from Stanford University):      
                            ─── Kannan Soundararajan  and  Robert Lemke Oliver    



The task is to check this assertion, modulo 10.

Lets call    i -> j    a transition if    i    is the last decimal digit of a prime, and    j    the last decimal digit of the following prime.



Task

Considering the first one million primes.   Count, for any pair of successive primes, the number of transitions    i -> j    and print them along with their relative frequency, sorted by    i .

You can see that, for a given    i ,   frequencies are not evenly distributed.



Observation

(Modulo 10),   primes whose last digit is   9   "prefer"   the digit   1   to the digit   9,   as its following prime.



Extra credit

Do the same for one hundred million primes.



Example for 10,000 primes

10000 first primes. Transitions prime % 10 → next-prime % 10.
1 → 1 count:        365 frequency: 3.65 %
1 → 3 count:        833 frequency: 8.33 %
1 → 7 count:        889 frequency: 8.89 %
1 → 9 count:        397 frequency: 3.97 %
2 → 3 count:          1 frequency: 0.01 %
3 → 1 count:        529 frequency: 5.29 %
3 → 3 count:        324 frequency: 3.24 %
3 → 5 count:          1 frequency: 0.01 %
3 → 7 count:        754 frequency: 7.54 %
3 → 9 count:        907 frequency: 9.07 %
5 → 7 count:          1 frequency: 0.01 %
7 → 1 count:        655 frequency: 6.55 %
7 → 3 count:        722 frequency: 7.22 %
7 → 7 count:        323 frequency: 3.23 %
7 → 9 count:        808 frequency: 8.08 %
9 → 1 count:        935 frequency: 9.35 %
9 → 3 count:        635 frequency: 6.35 %
9 → 7 count:        541 frequency: 5.41 %
9 → 9 count:        379 frequency: 3.79 % 



# extend SET, CLEAR and ELEM to operate on rows of BITS #
OP   SET   = ( INT n, REF[]BITS b )REF[]BITS:
     BEGIN
         INT w = n OVER bits width;
         b[ w ] := ( ( 1 + ( n MOD bits width ) ) SET b[ w ] );
         b
     END # SET # ;
OP   CLEAR = ( INT n, REF[]BITS b )REF[]BITS:
     BEGIN
         INT w = n OVER bits width;
         b[ w ] := ( ( 1 + ( n MOD bits width ) ) CLEAR b[ w ] );
         b
     END # SET # ;
OP   ELEM  = ( INT n, REF[]BITS b )BOOL: ( 1 + ( n MOD bits width ) ) ELEM b[ n OVER bits width ];
 
# constructs a bit array long enough to hold n values #
OP   BITARRAY = ( INT n )REF[]BITS: HEAP[ 0 : n OVER bits width ]BITS;
 
# construct a BITS value of all TRUE #
BITS all true = BEGIN
                    BITS v := 16r0;
                    FOR bit TO bits width DO v := bit SET v OD;
                    v
                END;
 
# initialises a bit array to all TRUE #
OP   SETALL   = ( REF[]BITS b )REF[]BITS:
     BEGIN
         FOR p FROM LWB b TO UPB b DO b[ p ] := all true OD;
         b
     END # SETALL # ;
 
# construct a sieve initialised to all TRUE apart from the first bit      #
INT sieve max = 15 500 000; # somewhat larger than the 1 000 000th prime  #
INT prime max =  1 000 000;
REF[]BITS sieve = 1 CLEAR SETALL BITARRAY sieve max;
 
# sieve the primes #
FOR s FROM 2 TO ENTIER sqrt( sieve max ) DO
    IF s ELEM sieve
    THEN
        FOR p FROM s * s BY s TO sieve max DO p CLEAR sieve OD
    FI
OD;
 
# count the number of times each combination of                           #
# ( last digit of previous prime, last digit of prime ) occurs            #
[ 0 : 9, 0 : 9 ]INT counts;
FOR p FROM 0 TO 9 DO FOR n FROM 0 TO 9 DO counts[ p, n ] := 0 OD OD;
INT previous prime := 2;
INT primes found   := 1;
FOR p FROM 3 TO sieve max WHILE primes found < prime max DO
    IF p ELEM sieve
    THEN
        primes found +:= 1;
        counts[ previous prime MOD 10, p MOD 10 ] +:= 1;
        previous prime := p
    FI
OD;
 
# print the counts #
# there are thus 4 possible final digits: 1, 3, 7, 9                      #
STRING labels = "123456789";                   # "labels" for the counts  #
INT total := 0;
FOR p TO 9 DO FOR n TO 9 DO total +:= counts[ p, n ] OD OD;
print( ( whole( primes found, 0 ), " primes, last prime considered: ", previous prime, newline ) );
FOR p TO 9 DO
    FOR n TO 9 DO
        IF counts[ p, n ] /= 0
        THEN
            print( ( labels[ p ], "->", labels[ n ]
                   , whole( counts[ p, n ], -8 )
                   , fixed( ( 100 * counts[ p, n ] ) / total, -8, 2 )
                   , newline
                   )
                 )
        FI
    OD
OD