22 de septiembre de 2019

More on prime parallelograms

Introduction

I was reading an article about how to graph the "prime parallelograms" in J using verbs, which is based on the second part of video by Numberphile.

The idea is to take the nth prime and subtract the number that is obtained by inverting the order of its representation in base 2. For example, the 16th prime is 53 = 1101012, when we reverse it we get 1010112 = 43, so the value is 53 - 43 = 10. If we graph this function, parallelograms appear.



I wondered what would happen if we graph all the numbers instead of only the primes. Also, I wanted to understand where these parallelograms come from.

Additional modules

To graph them in Racket we will need to use some additional modules, so we start with

#lang racket
(require math/number-theory)
(require plot)
#;(plot-new-window? #t)

I also like to change how the numbers appear on the axes. It is a technical part, and it almost doesn’t change the result. The code is at the bottom.

Rebuilding the graph

First, we define the function that reverses the order of the bits in the binary representation of the number.

(define (binary-reverse x)
  (let loop ([x x] [r 0])
    (if (zero? x)
       r
       (loop (quotient x 2) (+ (* 2 r) (remainder x 2))))))

Let's see some examples

(for ([i (in-range 16)])
  (define r (binary-reverse i))
  (displayln (list i r (number->string i 2) (number->string r 2))))

> (0 0 0 0)
> (1 1 1 1)
> (2 1 10 1)
> (3 3 11 11)
> (4 1 100 1)
> (5 5 101 101)
> (6 3 110 11)
> (7 7 111 111)
> (8 1 1000 1)
> (9 9 1001 1001)
> (10 5 1010 101)
> (11 13 1011 1101)
> (12 3 1100 11)
> (13 11 1101 1011)
> (14 7 1110 111)
> (15 15 1111 1111)

Note: It is possible to define this function using strings, but using strings is always much slower than operating directly with integers.

To simplify the notation, let's call inv this function that reverses the order of the bits, p the function that calculates the nth prime and let's call f the function f(n) = n - inv(n).

Now we can make a list with the points (n, f(p(n))) = (n, p(n) - inv(p(n))) as vectors.

(define prim/original (for/list ([i (in-naturals 1)]
                                 [p (in-list (next-primes 1 10000))])
                        (vector i (- p (binary-reverse p)))))

Note: We could write our own functions to test primality, but the build-in next-primes and prime? are better.

And we draw it with plot

(plot (points prim/original))



We fix the color and opacity to make the drawing more nice

(plot #:title "Prime Paralelograms (original)"
      (points prim/original
              #:sym 'dot
              #:color 'blue #:alpha .5)
      #:x-label "n" #:y-label "f(p(n))"
      #:x-min 0)


To save the image we use plot-file

(plot-file #:title "Prime Paralelograms (original)"
           (points prim/original
                   #:sym 'dot
                   #:color 'blue #:alpha .5)
           "prime-paralelograms(original).png"
           #:x-label "n" #:y-label "f(p(n))"
           #:x-min 0)

In order to compare it with the next graph, we draw it again. But with the labels of the values of the x-axis aligned to the left (so that the edge of the graph is the edge of the image).

(parameterize ([plot-x-tick-label-anchor 'top-right])
  (plot #:title "Prime Paralelograms (original)"
        (points prim/original
                #:sym 'dot
                #:color 'blue #:alpha .5)
        #:x-label "n" #:y-label "f(p(n))"
        #:x-min 0))


Now we change what we graph. We change the x part of the points, instead of using in which position of the list of prime it is, we use the value of the prime, that is, instead of drawing (n, f(p(n)) we draw (p(n), f(p(n)) = (p, f(p)).

(define prim/new (for/list ([i (in-naturals 1)]
                            [p (in-list (next-primes 1 10000))])
                   (vector p (- p (binary-reverse p)))))

(parameterize ([plot-x-tick-label-anchor 'top-right])
  (plot #:title "Prime Paralelograms"
        (points prim/new
                #:sym 'dot
                #:color 'blue #:alpha .5)
        #:x-label "p(n)" #:y-label "f(p(n))"
        #:x-min 0))

We can compare it with the previous one:

We get a very similar drawing if we ignore the scale of the x-axis. The parallelograms are cut at slightly different positions, but they are very similar.

What happens is that if we draw the nth prime we get almost a line.

(define prim/scale (for/list ([i (in-naturals 1)]
                              [p (in-list (next-primes 1 10000))])
                     (vector i p)))

(plot #:title "Primes"
      (points prim/scale
              #:sym 'dot)
      #:x-label "n" #:y-label "p(n) = nth prime"
      #:x-min 0)



The inverse is the function that counts the number of primes that in general is called pi(n) and its slope is slowly changing, more or less like 1/log(n), but log(n) is a function that grows very slowly. So the change between the graphs is almost a linear transformation. Therefore, when we use it to change the x-axis in the previous drawings we see that the shape of the parallelograms changes very little. (We should be able to notice more distortion in the first parallelograms.)

Comparing with all numbers

Now let's see how the graph looks when we use all the numbers instead of just the primes. To be able to compare the graph better, we fix the same range for the y-axis in all the graphs. Also, I like to choose a chart size so that the line y=x has approximately 45°. (In Excel you make a graph and then with the mouse you fix all the details of the axes and scales. Here you have to put all that fixes in the program so that the scales are exactly as you want.)

(define all (for/list ([i (in-range 1 131072)])
              (vector i (- i (binary-reverse i)))))

(plot #:title "All Paralelograms"
      (points all
              #:sym 'dot
              #:color 'black #:alpha .1)
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


Let’s repeat one of the previous graph, but on these new scales. We will create the list of primes in a slightly different way.

(define prim (for/list ([i (in-range 1 131072)]
                        #:when (prime? i))
              (vector i (- i (binary-reverse i)))))

(plot #:title "Prime Paralelograms"
      (points prim
              #:sym 'dot
              #:color 'blue #:alpha .5)
      #:x-label "p" #:y-label "f(p)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


And we superimpose the last two graphs

(plot #:title "All vs Prime Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points prim
                    #:sym 'dot
                    #:color 'blue #:alpha .5))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


What we see is that the prime parallelograms occupy the bottom half of the parallelograms that are formed when we graph all the numbers.

Even and odd

Almost all primes are odd. As they are odd, when you invert the order of the bits in binary you get a number with the same number of figures in binary. So the result has about the same size. You can bound the result and formalize this idea, but it is nicer to compare in a graphic what happens when we replace prime numbers with odd numbers.

(define odd (for/list ([i (in-range 1 131072 2)])
              (vector i (- i (binary-reverse i)))))

(plot #:title "All vs Odd Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points odd
                    #:sym 'dot
                    #:color 'red #:alpha .1))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


We see that the odd number occupy essentially the same parallelograms, although there are fewer gaps.

On the other hand, the even numbers end in 0 in binary, so by reversing the order of their bits in binary they lose at least one figure, so the result is smaller. When we draw them we get this graph.

(define even (for/list ([i (in-range 2 131072 2)])
               (vector i (- i (binary-reverse i)))))

(plot #:title "All vs Even Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points even
                    #:sym 'dot
                    #:color 'green #:alpha .1))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


We see that the even numbers essentially occupy the upper halves, that is, they do not overlap with what is painted by the primes. So the holes in the prime parallelograms are due to non-prime odd numbers.

Some ideas to try

  • What happens in base 3? Do we get the obvious generalization? In base 4, do we get flags?
  • Redraw to see only the first parallelograms. The change of scale is not so linear at the beginning, so when drawing the parallelograms of the original article (n, f(p(n)) they should be more distorted.
  • Redraw everything with the x-axis in logarithmic scale, so that all parallelograms have the same width and you can see the first ones easier. I don't know what scale to use for the y-axis. Maybe you have to draw each parallelogram separately?
  • The prime numbers are not random, but they look quite random. It might be interesting to generate a list of fake-primes, filtering the odd numbers with a distribution similar to that of the primes and see how the graph looks.

Changing the format of the labels

I like to change how the numbers appear on the axes, I don't like that 100000 appears as 105. This part is technical and almost does not change the result, so I will not explain the details. (Seems like a good idea for a PR as an additional option for linear-ticks.)

(require plot/utils)
(define ((linear-ticks-format/no-sc) min max pre-ticks)
  (define digits (digits-for-range min max))
  (map (lambda (pt)
         (real->plot-label (pre-tick-value pt) digits #f))
       pre-ticks))
(define (linear-ticks/no-sc) (ticks (linear-ticks-layout) (linear-ticks-format/no-sc)))
(plot-x-ticks (linear-ticks/no-sc)) ; almost global change
(plot-y-ticks (linear-ticks/no-sc)) ; almost global change

Full code

#lang racket
(require math/number-theory)
(require plot)
#;(plot-new-window? #t)

(require plot/utils)
(define ((linear-ticks-format/no-sc) min max pre-ticks)
  (define digits (digits-for-range min max))
  (map (lambda (pt)
         (real->plot-label (pre-tick-value pt) digits #f))
       pre-ticks))
(define (linear-ticks/no-sc) (ticks (linear-ticks-layout) (linear-ticks-format/no-sc)))
(plot-x-ticks (linear-ticks/no-sc)) ; almost global change
(plot-y-ticks (linear-ticks/no-sc)) ; almost global change

(define (binary-reverse x)
  (let loop ([x x] [r 0])
    (if (zero? x)
       r
       (loop (quotient x 2) (+ (* 2 r) (remainder x 2))))))

(for ([i (in-range 16)])
  (define r (binary-reverse i))
  (displayln (list i r (number->string i 2) (number->string r 2))))

#;(for ([i (in-range 128)]
        #:when (prime? i))
    (define r (binary-reverse i))
    (displayln (list i r (number->string i 2) (number->string r 2))))

;ORIGINAL
(define prim/original (for/list ([i (in-naturals 1)]
                                 [p (in-list (next-primes 1 10000))])
                        (vector i (- p (binary-reverse p)))))

(plot (points prim/original))

(plot #:title "Prime Paralelograms (original)"
      (points prim/original
              #:sym 'dot
              #:color 'blue #:alpha .5)
      #:x-label "n" #:y-label "f(p(n))"
      #:x-min 0)

(plot-file #:title "Prime Paralelograms (original)"
           (points prim/original
                   #:sym 'dot
                   #:color 'blue #:alpha .5)
           "prime-paralelograms(original).png"
           #:x-label "n" #:y-label "f(p(n))"
           #:x-min 0)

(parameterize ([plot-x-tick-label-anchor 'top-right])
  (plot #:title "Prime Paralelograms (original)"
        (points prim/original
                #:sym 'dot
                #:color 'blue #:alpha .5)
        #:x-label "n" #:y-label "f(p(n))"
        #:x-min 0))

;NEW
(define prim/new (for/list ([i (in-naturals 1)]
                            [p (in-list (next-primes 1 10000))])
                   (vector p (- p (binary-reverse p)))))

(parameterize ([plot-x-tick-label-anchor 'top-right])
  (plot #:title "Prime Paralelograms"
        (points prim/new
                #:sym 'dot
                #:color 'blue #:alpha .5)
        #:x-label "p(n)" #:y-label "f(p(n))"
        #:x-min 0))

;SCALE
(define prim/scale (for/list ([i (in-naturals 1)]
                              [p (in-list (next-primes 1 10000))])
                     (vector i p)))

(plot #:title "Primes"
      (points prim/scale
              #:sym 'dot)
      #:x-label "n" #:y-label "p(n) = nth prime"
      #:x-min 0)

;All
(define all (for/list ([i (in-range 1 131072)])
              (vector i (- i (binary-reverse i)))))

(plot #:title "All Paralelograms"
      (points all
              #:sym 'dot
              #:color 'black #:alpha .1)
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

(define prim (for/list ([i (in-range 1 131072)]
                        #:when (prime? i))
              (vector i (- i (binary-reverse i)))))

(plot #:title "Prime Paralelograms"
      (points prim
              #:sym 'dot
              #:color 'blue #:alpha .5)
      #:x-label "p" #:y-label "f(p)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

(plot #:title "All vs Prime Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points prim
                    #:sym 'dot
                    #:color 'blue #:alpha .5))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

; ODD and EVEN
(define odd (for/list ([i (in-range 1 131072 2)])
              (vector i (- i (binary-reverse i)))))

(plot #:title "All vs Odd Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points odd
                    #:sym 'dot
                    #:color 'red #:alpha .1))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

(define even (for/list ([i (in-range 2 131072 2)])
               (vector i (- i (binary-reverse i)))))

(plot #:title "All vs Even Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points even
                    #:sym 'dot
                    #:color 'green #:alpha .1))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

Más sobre los paralelogramos de primos

Introducción

Estuve leyendo un artículo sobre como graficar los “paralelogramos de primos” en J usando verbos (en ingés), que está basado en la segunda parte de un video de Numberphile (en ingés).

La idea es tomar el n-ésimo primo y restarle el número que se obtiene invirtiendo el orden de su representación en base 2. Por ejemplo, el 16-avo primo es 53=1101012, al darlo vuelta queda 1010112=43, así que el valor es 53-43=10. Al graficar esta función aparecen unos paralelogramos.


La duda que me surgió es qué pasaba si graficábamos todos los números en vez de dibujar sólo los primos. Y además entender de dónde vienen estos paralelogramos.

Módulos adicionales

Para graficarlos en Racket vamos a necesitar usar algunos modulos adicionales, así que empezamos con

#lang racket
(require math/number-theory)
(require plot)
#;(plot-new-window? #t)

A mí también me gusta modificar cómo aparecen los números en los ejes. Es medio técnico y casi o cambia el resultado. El código está abajo.

Reconstruyendo el dibujo

Primero definimos la función que invierte el orden de los bits en la representación binaria del número.

(define (binary-reverse x)
  (let loop ([x x] [r 0])
    (if (zero? x)
       r
       (loop (quotient x 2) (+ (* 2 r) (remainder x 2))))))

Veamos algunos ejemplos

(for ([i (in-range 16)])
  (define r (binary-reverse i))
  (displayln (list i r (number->string i 2) (number->string r 2))))

> (0 0 0 0)
> (1 1 1 1)
> (2 1 10 1)
> (3 3 11 11)
> (4 1 100 1)
> (5 5 101 101)
> (6 3 110 11)
> (7 7 111 111)
> (8 1 1000 1)
> (9 9 1001 1001)
> (10 5 1010 101)
> (11 13 1011 1101)
> (12 3 1100 11)
> (13 11 1101 1011)
> (14 7 1110 111)
> (15 15 1111 1111)

Nota: Es posible definirla usando strings, pero usar strings siempre es mucho más lento que operar directamente con los números enteros.

Para simplificar la notación, llamemos inv a esta función que invierte el orden de los bits, p a la función que calcula el n-esimo primo y f a f(n)=n-inv(n).
Ahora podemos hacernos una lista con los puntos (n, f(p(n))) = (n, p(n) - inv(p(n))) en forma de vectores.

(define prim/original (for/list ([i (in-naturals 1)]
                                 [p (in-list (next-primes 1 10000))])
                        (vector i (- p (binary-reverse p)))))

Nota: Es posible escribir nuestra propia función para tertear primalidad, pero las funciones next-primes y prime? son mejores.

Y la dibujamos con plot

(plot (points prim/original))




Arreglamos el color y la opacidad para que el dibujo sea más lindo

(plot #:title "Prime Paralelograms (original)"
      (points prim/original
              #:sym 'dot
              #:color 'blue #:alpha .5)
      #:x-label "n" #:y-label "f(p(n))"
      #:x-min 0)  


Para guardar la imagen usamos plot-file

(plot-file #:title "Prime Paralelograms (original)"
           (points prim/original
                   #:sym 'dot
                   #:color 'blue #:alpha .5)
           "prime-paralelograms(original).png"
           #:x-label "n" #:y-label "f(p(n))"
           #:x-min 0)

Para poder compararla con el próximo gráfico, la dibujamos de vuelta. Pero con las etiquetas de los valores del eje x hacia la izquierda (para que el borde del gráfico sea el borde de la imagen).

(parameterize ([plot-x-tick-label-anchor 'top-right])
  (plot #:title "Prime Paralelograms (original)"
        (points prim/original
                #:sym 'dot
                #:color 'blue #:alpha .5)
        #:x-label "n" #:y-label "f(p(n))"
        #:x-min 0))
  

Ahora cambiamos lo que graficamos. Cambiamos la posición en x de los puntos, en vez de tomar el número de primo tomamos el valor del primo, o sea que en vez de dibujar (n, f(p(n)) dibujamos (p(n), f(p(n)) = (p, f(p)).

(define prim/new (for/list ([i (in-naturals 1)]
                            [p (in-list (next-primes 1 10000))])
                   (vector p (- p (binary-reverse p)))))

(parameterize ([plot-x-tick-label-anchor 'top-right])
  (plot #:title "Prime Paralelograms"
        (points prim/new
                #:sym 'dot
                #:color 'blue #:alpha .5)
        #:x-label "p(n)" #:y-label "f(p(n))"
        #:x-min 0))

Podemos compararlo con el dibujo anterior:
El dibujo que obtenemos es muy similar, si ignoramos la escala del eje x. Los paralelogramos cortan en posiciones ligeramente diferentes, pero son muy parecidos.
Lo que pasa es que si dibujamos el n-esimo primo obtenemos casi una recta.

(define prim/scale (for/list ([i (in-naturals 1)]
                              [p (in-list (next-primes 1 10000))])
                     (vector i p)))

(plot #:title "Primes"
      (points prim/scale
              #:sym 'dot)
      #:x-label "n" #:y-label "p(n) = nth prime"
      #:x-min 0)


La inversa es la función que cuenta la cantidad de primos que en general se llama pi(n) y su pendiente va cambiando lentamente, más o menos como 1/log(n), pero log(n) es una función que crece muy lentamente. Así que el cambio entre los gráficos es casi una transformación lineal y por eso a cambiar el eje en los dibujos anteriores vemos que la forma de los paralelogramos cambia poco. (Deberíamos poder notar más distorsión en los primeros paralelogramos.)

Comparando con todos los números

Veamos ahora como queda el gráfico cuando usamos todos los números en vez de sólo los primos. Para poder comparar mejor los gráficos, fijamos el mismo rango para el eje y en todos los gráficos. Además, me gusta elegir un tamaño de gráfico para que la recta y=x tenga aproximadamente 45°. (En Excel uno hace un gráfico y después con el mouse arregla todos los detalles de los ejes y escalas. Acá hay que poner todo eso en el programa para que las escalas queden exactamente como uno quiere.)

(define all (for/list ([i (in-range 1 131072)])
              (vector i (- i (binary-reverse i)))))

(plot #:title "All Paralelograms"
      (points all
              #:sym 'dot
              #:color 'black #:alpha .1)
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


Repetimos el gráfico de los primos en la misma escala, armando la lista de primos en este rango de una manera ligeramente diferente.

(define prim (for/list ([i (in-range 1 131072)]
                        #:when (prime? i))
              (vector i (- i (binary-reverse i)))))

(plot #:title "Prime Paralelograms"
      (points prim
              #:sym 'dot
              #:color 'blue #:alpha .5)
      #:x-label "p" #:y-label "f(p)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


Y los superponemos

(plot #:title "All vs Prime Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points prim
                    #:sym 'dot
                    #:color 'blue #:alpha .5))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)
 [Gráfico]

Lo que vemos es que los paralelogramos de los primos ocupan la mitad de abajo de los paralelogramos que se forman cuando graficamos todos los números.

Pares e impares

Casi todos los primos son impares. Por ser impares al invertir el orden de sus cifras en binario se obtiene un número con la misma cantidad de cifras en binario. Así que es aproximadamente del mismo tamaño. Se pueden hacer algunas acotaciones y formalizar esta idea, pero es más lindo comparar en el gráfico que pasa cuando reemplazamos los números primos por los números impares.

(define odd (for/list ([i (in-range 1 131072 2)])
              (vector i (- i (binary-reverse i)))))

(plot #:title "All vs Odd Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points odd
                    #:sym 'dot
                    #:color 'red #:alpha .1))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


Vemos que los impares ocupan el esencialmente los mismos paralelogramos, aunque hay menos huecos.

En cambio, los números pares terminan en 0 en binario, así que al invertir el orden de sus cifras en binario al menos pierden una cifra y son más chicos. Al dibujar tenemos

(define even (for/list ([i (in-range 2 131072 2)])
               (vector i (- i (binary-reverse i)))))

(plot #:title "All vs Even Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points even
                    #:sym 'dot
                    #:color 'green #:alpha .1))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)


Vemos que los pares ocupan esencialmente las mitades de arriba, o sea que no se superponen con lo pintado por los primos. Entonces que los huequitos en los paralelogramos de los primos se deben a números impares no primos.

Algunas ideas para probar

  • ¿Qué pasa en base 3? ¿Es la generalización obvia? ¿En base 4 son banderitas?
  • Redibujar para ver sólo los primeros paralelogramos. El cambio de escala no es tan lineal al principio y al dibujar los paralelogramos del artículo original (n, f(p(n)) deberían estar más deformados.
  • Redibujar todo con el eje x en escala logarítmica para que todos los paralelogramos tengan el mismo ancho y se pueda ver bien los primeros. No sé qué escala usar para el eje y. ¿Quizás haya que dibujar cada paralelogramo por separado?
  • Los primos no son aleatorios, pero parecen bastante aleatorios. Podría ser interesante generar una lista de truchi-primos, filtrando los números impares con una distribución parecida a la de los primos y ver cómo queda el gráfico.

Cambiando el formato de las etiquetas

A mí me gusta modificar cómo aparecen los números en los ejes, no me gusta que 100000 aparece como 105. Esta parte es medio técnica y casi no cambia el resultado, así que no voy a explicar los detalles. (Parece una buena idea para un PR como una opción adicional de linear-ticks.)

(require plot/utils)
(define ((linear-ticks-format/no-sc) min max pre-ticks)
  (define digits (digits-for-range min max))
  (map (lambda (pt)
         (real->plot-label (pre-tick-value pt) digits #f))
       pre-ticks))
(define (linear-ticks/no-sc) (ticks (linear-ticks-layout) (linear-ticks-format/no-sc)))
(plot-x-ticks (linear-ticks/no-sc)) ; almost global change
(plot-y-ticks (linear-ticks/no-sc)) ; almost global change

Código completo

#lang racket
(require math/number-theory)
(require plot)
#;(plot-new-window? #t)

(require plot/utils)
(define ((linear-ticks-format/no-sc) min max pre-ticks)
  (define digits (digits-for-range min max))
  (map (lambda (pt)
         (real->plot-label (pre-tick-value pt) digits #f))
       pre-ticks))
(define (linear-ticks/no-sc) (ticks (linear-ticks-layout) (linear-ticks-format/no-sc)))
(plot-x-ticks (linear-ticks/no-sc)) ; almost global change
(plot-y-ticks (linear-ticks/no-sc)) ; almost global change

(define (binary-reverse x)
  (let loop ([x x] [r 0])
    (if (zero? x)
       r
       (loop (quotient x 2) (+ (* 2 r) (remainder x 2))))))

(for ([i (in-range 16)])
  (define r (binary-reverse i))
  (displayln (list i r (number->string i 2) (number->string r 2))))

#;(for ([i (in-range 128)]
        #:when (prime? i))
    (define r (binary-reverse i))
    (displayln (list i r (number->string i 2) (number->string r 2))))

;ORIGINAL
(define prim/original (for/list ([i (in-naturals 1)]
                                 [p (in-list (next-primes 1 10000))])
                        (vector i (- p (binary-reverse p)))))

(plot (points prim/original))

(plot #:title "Prime Paralelograms (original)"
      (points prim/original
              #:sym 'dot
              #:color 'blue #:alpha .5)
      #:x-label "n" #:y-label "f(p(n))"
      #:x-min 0)

(plot-file #:title "Prime Paralelograms (original)"
           (points prim/original
                   #:sym 'dot
                   #:color 'blue #:alpha .5)
           "prime-paralelograms(original).png"
           #:x-label "n" #:y-label "f(p(n))"
           #:x-min 0)

(parameterize ([plot-x-tick-label-anchor 'top-right])
  (plot #:title "Prime Paralelograms (original)"
        (points prim/original
                #:sym 'dot
                #:color 'blue #:alpha .5)
        #:x-label "n" #:y-label "f(p(n))"
        #:x-min 0))

;NEW
(define prim/new (for/list ([i (in-naturals 1)]
                            [p (in-list (next-primes 1 10000))])
                   (vector p (- p (binary-reverse p)))))

(parameterize ([plot-x-tick-label-anchor 'top-right])
  (plot #:title "Prime Paralelograms"
        (points prim/new
                #:sym 'dot
                #:color 'blue #:alpha .5)
        #:x-label "p(n)" #:y-label "f(p(n))"
        #:x-min 0))

;SCALE
(define prim/scale (for/list ([i (in-naturals 1)]
                              [p (in-list (next-primes 1 10000))])
                     (vector i p)))

(plot #:title "Primes"
      (points prim/scale
              #:sym 'dot)
      #:x-label "n" #:y-label "p(n) = nth prime"
      #:x-min 0)

;All
(define all (for/list ([i (in-range 1 131072)])
              (vector i (- i (binary-reverse i)))))

(plot #:title "All Paralelograms"
      (points all
              #:sym 'dot
              #:color 'black #:alpha .1)
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

(define prim (for/list ([i (in-range 1 131072)]
                        #:when (prime? i))
              (vector i (- i (binary-reverse i)))))

(plot #:title "Prime Paralelograms"
      (points prim
              #:sym 'dot
              #:color 'blue #:alpha .5)
      #:x-label "p" #:y-label "f(p)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

(plot #:title "All vs Prime Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points prim
                    #:sym 'dot
                    #:color 'blue #:alpha .5))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

; ODD and EVEN
(define odd (for/list ([i (in-range 1 131072 2)])
              (vector i (- i (binary-reverse i)))))

(plot #:title "All vs Odd Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points odd
                    #:sym 'dot
                    #:color 'red #:alpha .1))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)

(define even (for/list ([i (in-range 2 131072 2)])
               (vector i (- i (binary-reverse i)))))

(plot #:title "All vs Even Paralelograms"
      (list (points all
                    #:sym 'dot
                    #:color 'black #:alpha .1)
            (points even
                    #:sym 'dot
                    #:color 'green #:alpha .1))
      #:x-label "n" #:y-label "f(n)"
      #:y-min -65536 #:y-max 131072
      #:x-min 0
      #:width 400 #:height 600)