Math-PlanePath

 view release on metacpan or  search on metacpan

lib/Math/PlanePath/GcdRationals.pm  view on Meta::CPAN

      |         .         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. vertical.

Then gcd=3 becomes X=(i+2j)/3 which slopes across by +1 for each i, or gcd=4
has X=(i+3j)/4 slope +2, etc.

Of course only some of the points in an i,j 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 with diagonal
d=2j the fractions enumerated are

    N=d^2
    enumerates all num/den where num <= d and num+den <= 2*d

=head1 FUNCTIONS

See L<Math::PlanePath/FUNCTIONS> for behaviour common to all path classes.

=over

=item C<$path = Math::PlanePath::GcdRationals-E<gt>new ()>

=item C<$path = Math::PlanePath::GcdRationals-E<gt>new (pairs_order =E<gt> $str)>

Create and return a new path object.  The C<pairs_order> option can be

    "rows"               (default)
    "rows_reverse"
    "diagonals_down"
    "diagonals_up"

=back

=head1 FORMULAS

=head2 X,Y to N -- Rows

The defining formula above for X,Y can be inverted to give i,j and N.  This
calculation doesn't notice if X,Y have a common factor, so a coprime(X,Y)
test must be made separately if necessary (for C<xy_to_n()> it is).

    X/Y = g-1 + (i/g)/(j/g)

The g-1 integer part is recovered by a division X divide Y,

    X = quot*Y + rem   division by Y rounded towards 0
      where 0 <= rem < Y
      unless Y=1 in which case use quot=X-1, rem=1
    g-1 = quot
    g = quot+1

The Y=1 special case can instead be left as the usual kind of division
quot=X,rem=0, so 0E<lt>=remE<lt>Y.  This will give i=0 which is outside the
intended 1E<lt>=iE<lt>=j range, but j is 1 bigger and the combination still
gives the correct N.  It's as if the i=g,j=g point at the end of a row is
moved to i=0,j=g+1 just before the start of the next row.  If only N is of
interest not the i,j then it can be left rem=0.

Equating the denominators in the X/Y formula above gives j by

    Y = j/g          the definition above

    j = g*Y
      = (quot+1)*Y
    j = X+Y-rem      per the division X=quot*Y+rem

And equating the numerators gives i by

    X = (g-1)*Y + i/g     the definition above

    i = X*g - (g-1)*Y*g
      = X*g - quot*Y*g
      = X*g - (X-rem)*g     per the division X=quot*Y+rem
    i = rem*g
    i = rem*(quot+1)

Then N from i,j by the definition above

    N = i + j*(j-1)/2

For example X=11,Y=4 divides X/Y as 11=4*2+3 for quot=2,rem=3 so i=3*(2+1)=9
j=11+4-3=12 and so N=9+12*11/2=75 (as shown in the first table above).

It's possible to use only the quotient p, not the remainder rem, by taking
j=(quot+1)*Y instead of j=X+Y-rem, but usually a division operation gives
the remainder at no extra cost, or a cost small enough that it's worth
swapping a multiply for an add or two.

The gcd g can be recovered by rounding up in the division, instead of
rounding down and then incrementing with g=quot+1.

   g = ceil(X/Y)
     = cquot for division X=cquot*Y - crem

But division in most programming languages is towards 0 or towards
-infinity, not upwards towards +infinity.

=head2 X,Y to N -- Rows Reverse

For pairs_order="rows_reverse", the horizontal i is reversed to j-i+1.  This
can be worked into the triangular part of the N formula as

    Nrrev = (j-i+1) + j*(j-1)/2        for 1<=i<=j
          = j*(j+1)/2 - i + 1

The Y=1 case described above cannot be left to go through with rem=0 giving
i=0 and j+1 since the reversal j-i+1 is then not correct.  Either use rem=1
as described, or if not then compensate at the end,

    if r=0 then j -= 2            adjust
    Nrrev = j*(j+1)/2 - i + 1     same Nrrev as above

For example X=5,Y=1 is quot=5,rem=0 gives i=0*(5+1)=0 j=5+1-0=6.  Without
adjustment it would be Nrrev=6*7/2-0+1=22 which is wrong.  But adjusting
j-=2 so that j=6-2=4 gives the desired Nrrev=4*5/2-0+1=11 (per the table in
L</Rows Reverse> above).

=cut

# No, not quite
#
# =head2 Rectangle N Range -- Rows
#
# An over-estimate of the N range can be calculated just from the X,Y to N
# formula above.
#
# Within a row N increases with increasing X, so for a rectangle the minimum
# is in the left column and the maximum in the right column.
#
# Within a column N values increase until reaching the end of a "g" wedge,
# then drop down a bit.  So the maximum is either the top-right corner of the
# rectangle, or the top of the next lower wedge, ie. smaller Y but bigger g.
# Conversely the minimum is either the bottom right of the rectangle, or the
# bottom of the next higher wedge, ie. smaller g but bigger Y.  (Is that
# right?)
#
# This is an over-estimate because it ignores which X,Y points are coprime and
# thus actually should have N values.
#
# =head2 Rectangle N Range -- Rows Reverse
#
# When row pairs are taken in reverse order increasing X is not increasing N,
# but rather the maximum N of a row is at the left end of the wedge.

=pod

=head1 OEIS

This enumeration of rationals is in Sloane's Online Encyclopedia of Integer
Sequences in the following forms

=over

L<http://oeis.org/A054531> (etc)

=back

    pairs_order="rows" (the default)
      A226314   X coordinate
      A054531   Y coordinate, being N/GCD(i,j)
      A000124   N in X=1 column, triangular+1
      A050873   ceil(X/Y), gcd by rows
      A050873-A023532  floor(X/Y)
                gcd by rows and subtract 1 unless i=j

    pairs_order="diagonals_down"
      A033638   N in X=1 column, quartersquares+1 and pronic+1
      A000290   N in Y=1 row, perfect squares

    pairs_order="diagonals_up"
      A002620   N in X=1 column, squares and pronics
      A002061   N in Y=1 row, central polygonals (extra initial 1)
      A002522   N at Y=X+1 above leading diagonal, squares+1

=head1 SEE ALSO

L<Math::PlanePath>,
L<Math::PlanePath::DiagonalRationals>,
L<Math::PlanePath::RationalsTree>,
L<Math::PlanePath::CoprimeColumns>,
L<Math::PlanePath::DiagonalsOctant>

=head1 HOME PAGE

L<http://user42.tuxfamily.org/math-planepath/index.html>



( run in 0.699 second using v1.01-cache-2.11-cpan-df04353d9ac )