NAME
Math::PlanePath::GcdRationals -- rationals by triangular GCD
SYNOPSIS
use Math::PlanePath::GcdRationals;
my $path = Math::PlanePath::GcdRationals->new;
my ($x, $y) = $path->n_to_xy (123);
DESCRIPTION
This path enumerates X/Y rationals using a method by Lance Fortnow taking a greatest common divisor out of a triangular position. It has the attraction of being both efficient to calculate (a GCD) and completing X/Y blocks with a much smaller N range than the tree based rationals.
http://blog.computationalcomplexity.org/2004/03/counting-rationals-quickly.html
13 | 79 80 81 82 83 84 85 86 87 88 89 90
12 | 67 71 73 77 278
11 | 56 57 58 59 60 61 62 63 64 65 233 235
10 | 46 48 52 54 192 196
9 | 37 38 40 41 43 44 155 157 161
8 | 29 31 33 35 122 126 130
7 | 22 23 24 25 26 27 93 95 97 99 101 103
6 | 16 20 68 76 156
5 | 11 12 13 14 47 49 51 53 108 111 114
4 | 7 9 30 34 69 75 124
3 | 4 5 17 19 39 42 70 74 110
2 | 2 8 18 32 50 72 98
1 | 1 3 6 10 15 21 28 36 45 55 66 78 91
Y=0 |
--------------------------------------------------------
X=0 1 2 3 4 5 6 7 8 9 10 11 12 13
The mapping from N to rational is
N = i + j*(j-1)/2 upper triangle 1 <= i <= j
gcd = GCD(i,j)
rational = i/j + gcd-1
which means
X = (i + j*(gcd-1)) / gcd
Y = j/gcd
The i,j position is a numbering of points above the X=Y diagonal by rows, in the style of PyramidRows step=1.
j=4 7 8 9 10
j=3 4 5 6
j=2 2 3
j=1 1
i=1 2 3 4
If GCD(i,j)=1 then X/Y is simply X=i,Y=j unchanged. This means fractions X/Y < 1 are numbered by rows with increasing numerator, but skipping positions where i,j have a common factor.
The skipped positions where i,j have a common factor become rationals X/Y>1, ie. below the X=Y diagonal. GCD(i,j)-1 is the integer part as R = i/j+(gcd-1). For example N=51 is at i=6,j=10 by rows and that i,j has common factor gcd(6,10)=2 so becomes rational R = 6/10+(2-1) = 3/5+1 = 8/5, ie. X=8,Y=5.
Triangular Numbers
The bottom row Y=1 is the triangular numbers N=1,3,6,10,etc, k*(k-1)/2. Such an N is at i=k,j=k and thus gcd(i,j)=k which divides out to Y=1.
Y = j/gcd
= 1 on the bottom row
X = (i + j*(gcd-1)) / gcd
= (k + k*(k-1)) / k
= k-1 successive points on that bottom row
N=1,2,4,7,11,etc in the vertical at X=1 immediately following those triangulars on the bottom row, ie.
N on X=1 column = Y*(Y-1)/2 + 1
Primes
If N is prime then it's above the sloping line X=2*Y. If N is composite then it might be above or below, but the primes are always above. Here's the table with dots "..." for the X=2*Y line.
primes and composites above
6 | 16 20 68
| .... X=2*Y
5 | 11 12 13 14 47 49 51 53 ....
| ....
4 | 7 9 30 34 .... 69
| ....
3 | 4 5 17 19 .... 39 42 70 always
| .... composite
2 | 2 8 .... 18 32 50 below
| ....
1 | 1 ..3. 6 10 15 21 28 36 45 55
| ....
Y=0 | ....
---------------------------------------------
X=0 1 2 3 4 5 6 7 8 9 10
Values below X=2*Y such as 39 and 42 are always composite. Values above such as 19 and 30 are either prime or composite. Only X=2,Y=1 is exactly on the line, which is prime N=3 as it happens. Other X=2*k,Y=k are not an X/Y rational in least terms because it has common factor k.
This pattern of primes and composites occurs because N is a multiple of gcd(i,j) when gcd odd, or a multiple of gcd/2 when gcd even.
N = i + j*(j-1)/2
gcd = gcd(i,j)
N = gcd * (i/gcd + j/gcd * (j-1)/2) when gcd odd
gcd/2 * (2i/gcd + j/gcd * (j-1)) when gcd even
If gcd odd then either j/gcd or j-1 is even, taking the "/2". If gcd even then only gcd/2 can come out as a factor since the full gcd might leave both j/gcd and j-1 odd and so the "/2" not an integer. That happens for example to N=70
N = 70
i = 4, j = 12 for 4 + 12*11/2 = 70 = N
gcd(i,j) = 4
but N is not a multiple of 4, only of 4/2=2
Of course knowing gcd or gcd/2 is a factor is only useful when that factor is 2 or more, so only
odd gcd with gcd >= 2 means gcd >= 3
even gcd with gcd/2 >= 2 means gcd >= 4
so N composite when gcd(i,j) >= 3
If gcd<3 then the "factor" coming out is only 1 and says nothing about whether N is prime or composite. There are both prime and composite N for gcd<3, as can be seen among the values above the X=2*Y line in the table above.
Rows Reverse
Option pairs_order => "rows_reverse"
reverses the order of points within the rows of i,j pairs,
j=4 10 9 8 7
j=3 6 5 4
j=2 3 2
j=1 1
i=1 2 3 4
The point numbering becomes
pairs_order => "rows_reverse"
11 | 66 65 64 63 62 61 60 59 58 57
10 | 55 53 49 47 209
9 | 45 44 42 41 39 38 170 168
8 | 36 34 32 30 135 131
7 | 28 27 26 25 24 23 104 102 100 98
6 | 21 17 77 69
5 | 15 14 13 12 54 52 50 48 118
4 | 10 8 35 31 76 70
3 | 6 5 20 18 43 40 75 71
2 | 3 9 19 33 51 73
1 | 1 2 4 7 11 16 22 29 37 46 56
Y=0 |
------------------------------------------------
X=0 1 2 3 4 5 6 7 8 9 10 11
The triangular numbers per "Triangular Numbers" above are now in the X=1 column, ie. at the left rather than the bottom. The Y=1 bottom row is the next after each triangular, ie. T(X)+1.
Diagonals
Option pairs_order => "diagonals_down"
takes the i,j pairs by diagonals down from the Y axis. pairs_order => "diagonals_up"
likewise but upwards from the X=Y centre up to the Y axis. This is in the style of the DiagonalsOctant path.
diagonals_down diagonals_up
j=7 13 j=7 16
j=6 10 14 j=6 12 15
j=5 7 11 15 j=5 9 11 14
j=4 5 8 12 16 j=4 6 8 10 13
j=3 3 6 9 j=3 4 5 7
j=2 2 4 j=2 2 3
j=1 1 j=1 1
i=1 2 3 4 i=1 2 3 4
The resulting path becomes
pairs_order => "diagonals_down"
9 | 21 27 40 47 63 72
8 | 17 28 41 56 74
7 | 13 18 23 29 35 42 58 76
6 | 10 30 44
5 | 7 11 15 20 32 46 62 80
4 | 5 12 22 48 52
3 | 3 6 14 24 33 55
2 | 2 8 19 34 54
1 | 1 4 9 16 25 36 49 64 81
Y=0 |
--------------------------------
X=0 1 2 3 4 5 6 7 8 9
pairs_order => "diagonals_up"
9 | 25 29 39 45 58 65
8 | 20 28 38 50 80
7 | 16 19 23 27 32 37 63 78
6 | 12 26 48
5 | 9 11 14 17 35 46 59 74
4 | 6 10 24 44 54
3 | 4 5 15 22 34 51
2 | 2 8 18 33 52
1 | 1 3 7 13 21 31 43 57 73
Y=0 |
--------------------------------
X=0 1 2 3 4 5 6 7 8 9
For "diagonals_down" the Y=1 bottom row is the perfect squares which are at i=j in the DiagonalsOctant and have gcd(i,j)=i thus becoming X=i,Y=1.
The gcd shears moves points downwards and shears them across horizontally.
| 1
| 1 gcd=1 slope=-1
| 1
| 1
| 1
| 1
| 1
| 1
| 1
| . gcd=2 slope=0
| . 2
| . 2 3 gcd=3 slope=1
| . 2 3 gcd=4 slope=2
| . 2 3 4
| . 3 4 5 gcd=5 slope=3
| . 4 5
| . 4 5
| . 5
+-------------------------------
The line of "1"s is the diagonal with gcd=1 and thus X,Y=i,j unchanged.
The line of "2"s is when gcd=2 so X=(i+j)/2,Y=j/2. Since i+j=d is constant within the diagonal this makes X=d fixed, ie. a vertical.
Then gcd=3 becomes X=(i+2j)/3 which slopes across by +1 for each i, or gcd=4 X=(i+3j)/4 slope +2, etc.
Of course only some of the points in a diagonal have a given gcd, but those which do are transformed this way. The effect is that for N up to a given diagonal row all the "*" points in the following are traversed, plus extras in wedge shaped arms out to the side.
| *
| * * up to a given diagonal points "*"
| * * * all visited, plus some wedges out
| * * * * to the right
| * * * * *
| * * * * * /
| * * * * * / --
| * * * * * --
| * * * * *--
+--------------
In terms of the rationals X/Y the effect is that up to N=d^2 diagonal d=2j the fractions enumerated are
N=d^2
enumerates to num <= d and num+den <= 2*d
FUNCTIONS
See "FUNCTIONS" in Math::PlanePath for behaviour common to all path classes.
$path = Math::PlanePath::GcdRationals->new ()
$path = Math::PlanePath::GcdRationals->new (pairs_order => $str)
-
Create and return a new path object. The
pairs_order
option can be"rows" (default) "rows_reverse" "diagonals_down" "diagonals_up"
FORMULAS
X,Y to N
The defining formula above for X,Y can be reversed
X/Y = i/j + g-1
g-1 = floor(X/Y)
Y = j/g
X = ((g-1)*j + i)/g
so
j = Y*g
i = X*g - (g-1)*Y*g
So N = i + j*(j-1)/2 = X*g - (g-1)*Y*g + Y*g*(Y*g-1)/2 = X*g + ((Y-2)*g + 1)*Y*g/2 = (((Y-2)*g + 1)*Y + 2X)*g/2
The /2 division is exact. If Y and g are both odd and so don't take that divisor then the term (Y-2)*g+1 is odd*odd+1 so even.
Y*g in the formulas is the first multiple of Y which is strictly greater than X. It can be formed from the g-1=floor(X/Y) division,
X = Y*quot + rem division
g = quot+1
Y*g = Y*(q+1) = X+Y-rem
= X+Y-rem
If a division gives quotient and remainder for the same price then X+Y-rem instead of Y*g might reduce a multiply to instead an add or subtract.
OEIS
This enumeration of rationals is in Sloane's Online Encyclopedia of Integer Sequences in the following forms
http://oeis.org/A054531 (etc)
A054531 - Y coordinate, ie. denominators
SEE ALSO
Math::PlanePath, Math::PlanePath::DiagonalRationals, Math::PlanePath::RationalsTree, Math::PlanePath::CoprimeColumns, Math::PlanePath::DiagonalsOctant
HOME PAGE
http://user42.tuxfamily.org/math-planepath/index.html
LICENSE
Copyright 2011, 2012 Kevin Ryde
This file is part of Math-PlanePath.
Math-PlanePath is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version.
Math-PlanePath is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Math-PlanePath. If not, see <http://www.gnu.org/licenses/>.