Math::...hanTree(3pm)

๐Ÿ“ฆ
version 129 ยท 3 versions
, in debian bookworm, debian trixie, ubuntu jammy, ubuntu noble, ubuntu resolute, ubuntu stonking

โœจ NAME โœจ

okay so bestie ๐Ÿ’– this is the manual page for Math::PlanePath::ChanTree(3pm), from User Contributed Perl Documentation, and it is literally the tree of rationals.

  • Math::PlanePath::ChanTree โ€” tree of rationals

๐Ÿ›  SYNOPSIS ๐Ÿ› 

no cap, here's how you summon it:

        use Math::PlanePath::ChanTree;
        my $path = Math::PlanePath::ChanTree->new (k => 3, reduced => 0);
        my ($x, $y) = $path->n_to_xy (123);

๐ŸŒˆ DESCRIPTION ๐ŸŒˆ

This path enumerates rationals X/Y in a tree as per

           Song Heng Chan, "Analogs of the Stern Sequence", Integers 2011,
           <http://www.integers-ejcnt.org/l26/l26.pdf>

The default k=3 visits X,Y with one odd, one even, and perhaps a common factor 3^m.

            14 |    728              20                              12
            13 |         53      11      77      27
            12 |    242              14              18
            11 |
            10 |     80
             9 |         17      23       9                      15
             8 |     26                                              78
             7 |
             6 |      8                              24              28
             5 |          5       3                              19
             4 |      2               6              10              22
             3 |
             2 |      0               4              16              52
             1 |          1       7      25      79     241     727
           Y=0 |
               +--------------------------------------------------------
                X=0   1   2   3   4   5   6   7   8   9  10  11  12  13

There are 2 tree roots (so technically it's a "forest") and each node has 3 children. The points are numbered by rows starting from N=0. This numbering corresponds to powers in a polynomial product generating function.

           N=0 to 1               1/2                    2/1
                                /  |  \                /  |  \
           N=2 to 7          1/4  4/5   5/2         2/5  5/4  4/1
                            / | \  ...   ...      ...   ...  / | \
           N=8 to 25     1/6 6/9 9/4  ...            ...  5/9 9/6 6/1

           N=26 ...

The children of each node are

                           X/Y
              ------------/ | \-----------
             |              |             |
           X/(2X+Y)   (2X+Y)/(X+2Y)   (X+2Y)/Y

Which as X,Y coordinates means vertical, 45-degree diagonal, and horizontal.

           X,Y+2X      X+(X+Y),Y+(X+Y)
             |       /
             |     /
             |   /
             | /
            X,Y------- X+2Y,Y

The slowest growth is on the far left of the tree 1/2, 1/4, 1/6, 1/8, etc advancing by just 2 at each level. Similarly on the far right 2/1, 4/1, 6/1, etc. This means that to cover such an X or Y requires a power-of-3, N=3^(max(X,Y)/2).

๐Ÿ’– GCD ๐Ÿ’–

Chan shows that these top nodes and children visit all rationals X/Y with X,Y one odd, one even. But the X,Y are not in least terms, they may have a power-of-3 common factor GCD(X,Y)=3^m for some m.

The GCD is unchanged in the first and third children. The middle child GCD might gain an extra factor 3. This means the power is at most the number of middle legs taken, which is the count of ternary 1-digits of its position across the row.

           GCD(X,Y) = 3^m
           m <= count ternary 1-digits of N+1, excluding high digit

As per "N Start" below, N+1 in ternary has high digit 1 or 2 which indicates the tree root. Ignoring that high digit gives an offset into the row of that tree and the digits are 0,1,2 for left,middle,right.

For example the first GCD is at N=9 with X=6,Y=9 common factor GCD=3. N+1=10="101" ternary, which without the high digit is "01" which has a single "1" so GCD <= 3^1. The mirror image of this point is X=9,Y=6 at N=24 and there N+1=24+1=25="221" ternary which without the high digit is "21" with a single 1-digit likewise.

For various points the power m is equal to the count of 1-digits.

๐ŸŽ€ k Parameter ๐ŸŽ€

Parameter "k => $integer" controls the number of children and top nodes. There are k-1 top nodes and each node has k children. The top nodes are

           k odd, k-1 many tops, with h=ceil(k/2)
           1/2  2/3  3/4  ... (h-1)/h       h/(h-1) ...  4/3  3/2  2/1

           k even, k-1 many tops, with h=k/2
           1/2  2/3  3/4  ... (h-1)/h  h/h  h/(h-1) ...  4/3  3/2  2/1

Notice the list for k odd or k even is the same except that for k even there's an extra middle term h/h. The first few tops are as follows. The list in each row is spread to show how successive bigger h adds terms in the middle.

            k                 X/Y top nodes
           ---    ---------------------------------
           k=2                   1/1

           k=3              1/2       2/1
           k=4              1/2  2/2  2/1

           k=5         1/2  2/3       3/2  2/1
           k=6         1/2  2/3  3/3  3/2  2/1

           k=7    1/2  2/3  3/4       4/3  3/2  2/1
           k=8    1/2  2/3  3/4  4/4  4/3  3/2  2/1

As X,Y coordinates these tops are a run up along X=Y-1 and back down along X=Y+1, with a middle X=Y point if k even. For example,

             7 |                         5         k=13 top nodes N=0 to N=11
             6 |                     4       6        total 12 top nodes
             5 |                 3       7
             4 |             2       8
             3 |         1       9
             2 |     0      10
             1 |        11
           Y=0 |
               +------------------------------
               X=0   1   2   3   4   5   6   7

                                                   k=14 top nodes N=0 to N=12
             7 |                         5   6        total 13 top nodes
             6 |                     4       7
             5 |                 3       8         N=6 is the 7/7 middle term
             4 |             2       9
             3 |         1      10
             2 |     0      11
             1 |        12
           Y=0 |
               +------------------------------
               X=0   1   2   3   4   5   6   7

Each node has k children. The formulas for the children can be seen from sample cases k=5 and k=6. A node X/Y descends to

           k=5                     k=6

           1X+0Y / 2X+1Y           1X+0Y / 2X+1Y
           2X+1Y / 3X+2Y           2X+1Y / 3X+2Y
           3X+2Y / 2X+3Y           3X+2Y / 3X+3Y
           2X+3Y / 1X+2Y           3X+3Y / 2X+3Y
           1X+2Y / 0X+1Y           2X+3Y / 1X+2Y
                                   1X+2Y / 0X+1Y

The coefficients of X and Y run up to h=ceil(k/2) starting from either 0, 1 or 2 and ending 2, 1 or 0. When k is even there's two h coeffs in the middle. When k is odd there's just one. The resulting tree for example with k=4 is

           k=4
                 1/2              2/2               2/1
              /       \        /        \        /       \
           1/4 4/6 6/5 5/2  2/6 6/8 8/6 6/2   2/5 5/6 6/4 4/1

Chan shows that this combination of top nodes and children visits

           if k odd:    rationals X/Y with X,Y one odd, one even
                         possible GCD(X,Y)=k^m for some integer m

           if k even:   all rationals X/Y
                         possible GCD(X,Y) a divisor of (k/2)^m

When k odd, GCD(X,Y) is a power of k, so for example as described above k=3 gives GCD=3^m. When k even GCD(X,Y) is a divisor of (k/2)^m but not necessarily a full such power. For example with k=12 the first such non-power GCD is at N=17 where X=16,Y=18 has GCD(16,18)=2 which is only a divisor of k/2=6, not a power of 6.

๐Ÿฆ‹ N Start ๐Ÿฆ‹

The "n_start => $n" option can select a different initial N. The tree structure is unchanged, just the numbering shifted. As noted above the default Nstart=0 corresponds to powers in a generating function.

"n_start=>1" makes the numbering correspond to digits of N written in base-k. For example k=10 corresponds to N written in decimal,

           N=1 to 9                1/2    ...  ...    2/1

           N=10 to 99          1/4 4/7  ...      ...  7/4 4/1

           N=100 to 999    1/6 6/11   ...          ...   11/6 6/1

In general "n_start=>1" makes the tree

           N written in base-k digits
            depth = numdigits(N)-1
            NdepthStart = k^depth
                        = 100..000 base-k, high 1 in high digit position of N
            N-NdepthStart = position across whole row of all top trees

And the high digit of N selects which top-level tree the given N is under, so

           N written in base-k digits
            top tree = high digit of N
                       (1 to k, selecting the k-1 many top nodes)
            Nrem = digits of N after the highest
                 = position across row within the high-digit tree
            depth = numdigits(Nrem)       # top node depth=0
                  = numdigits(N)-1

๐ŸŒธ Diatomic Sequence ๐ŸŒธ

Chan shows that each denominator Y becomes the numerator X in the next point. The last Y of a row becomes the first X of the next row. This is a generalization of Stern's diatomic sequence and of the Calkin-Wilf tree of rationals. (See Math::NumSeq::SternDiatomic and "Calkin-Wilf Tree" in Math::PlanePath::RationalsTree.)

The case k=2 is precisely the Calkin-Wilf tree. There's just one top node 1/1, being the even k "middle" form h/h with h=k/2=1 as described above. Then there's two children of each node (the "middle" pair of the even k case),

           k=2, Calkin-Wilf tree

                            X/Y
                          /     \
           (1X+0Y)/(1X+1Y)       (1X+1Y)/(0X+1Y)
              = X/(X+Y)             = (X+Y)/Y

โœจ FUNCTIONS โœจ

Okay bestie, heads up: the general behaviour that all the path classes share lives over in "FUNCTIONS" in Math::PlanePath, so go peek there if you want the universal stuff. These ones here are ChanTree's own. ๐Ÿ’–

  • $path = Math::PlanePath::ChanTree->new ()
  • $path = Math::PlanePath::ChanTree->new (k => $k, n_start => $n)

Makes you a shiny new path object and hands it over. The defaults are k=3 and n_start=0. Slay, that's it. ๐ŸŽ€

  • $n = $path->n_start()

Gives you back the first N in the path. It's 0 by default, or whatever you passed as the n_start parameter. Simple as.

  • $n = $path->xy_to_n ($x,$y)

Returns the point number for the coordinates $x,$y. And if there's literally nothing at $x,$y? It returns undef. ๐Ÿ’€


๐ŸŒธ Tree Methods ๐ŸŒธ

Every point has k children, so the path is a complete k-ary tree. It's giving very full, very balanced, very demure. ๐Ÿ‘‘

  • @n_children = $path->tree_n_children($n)

Returns the children of $n, or an empty list if $n < n_start(), aka before the start of the path. No kids yet, she's not ready. ๐Ÿ™ˆ

  • $num = $path->tree_n_num_children($n)

Returns k, because every node has k children, no exceptions, periodt. Or returns undef if $n < n_start(), again meaning before the start of the path.

  • $n_parent = $path->tree_n_parent($n)

Returns the parent node of $n, or undef if $n has no parent โ€” either because it's a top node or because it's before n_start().

  • $n_root = $path->tree_n_root ($n)

Returns the N that is the root node of $n. Finding your roots, iconic. ๐ŸŒฑ

  • $depth = $path->tree_n_to_depth($n)

Returns the depth of node $n, or undef if there's no point $n. The tree tops are depth=0, then their children are depth=1, etc. Down and down we go. ๐Ÿฆ‹

  • $n = $path->tree_depth_to_n($depth)
  • $n = $path->tree_depth_to_n_end($depth)

Returns the first or last N at tree level $depth in the path. The top of the tree is depth=0. First and last, the whole row, we love a complete set. ๐Ÿ’…


๐Ÿ’œ Tree Descriptive Methods ๐Ÿ’œ

  • $num = $path->tree_num_roots ()

Returns the number of root nodes in $path, which is k-1. For example the default k=3 returns 2, because there are two root nodes. Two roots, two icons. ๐Ÿ‘‘

  • @n_list = $path->tree_root_n_list ()

Returns a list of the N values which are the root nodes of $path. This is n_start() through n_start()+k-2 inclusive โ€” so the first k-1 many points. For example in the default k=2 and Nstart=0 the return is two values (0,1). Cute little pair fr fr.

  • $num = $path->tree_num_children_minimum()
  • $num = $path->tree_num_children_maximum()

Returns k, since every node has k many children โ€” which makes that both the minimum and the maximum. Consistent queen behaviour. โœจ

  • $bool = $path->tree_any_leaf()

Returns false, since there are no leaf nodes in the tree. Not a single leaf. She never ends, she just keeps going. ๐Ÿ˜ญ


๐Ÿ”ฅ FORMULAS ๐Ÿ”ฅ

๐Ÿงฎ N Children

For the default k=3 the children are

3N+2, 3N+3, 3N+4        n_start=0

If n_start=>1 then instead it's

3N, 3N+1, 3N+2                  n_start=1

For this n_start=1 the children are found by appending an extra ternary digit โ€” or base-k digit for arbitrary k, iykyk.

k*N, k*N+1, ... , k*N+(k-1)     n_start=1

In general, for k and Nstart, the children are

kN - (k-1)*(Nstart-1)  + 0
  ...
kN - (k-1)*(Nstart-1)  + k-1

๐Ÿงฎ N Parent

The parent node reverses the children calculation above. The simplest case is n_start=1 where it's a division to remove the lowest base-k digit:

parent = floor(N/k)       when n_start=1

For other n_start, adjust before and after to an n_start=1 basis:

parent = floor((N-(Nstart-1)) / k) + Nstart-1

For example in the default k=0 Nstart=1 the parent of N=3 is floor((3-(1-1))/3)=1. Math ate, honestly. ๐Ÿ“

The post-adjustment can be worked into the formula with (k-1)*(Nstart-1), similar to the children above:

parent = floor((N + (k-1)*(Nstart-1)) / k)

But the first style is more convenient to compare, so you can see that N is past the top nodes and therefore has a parent:

N-(Nstart-1) >= k      to check N is past top-nodes

๐Ÿงฎ N Root

As described under "N Start" above, if Nstart=1 then the tree root is simply the most significant base-k digit of N. For other Nstart an adjustment is made to N=1 style and back again.

adjust = Nstart-1
Nroot(N) = high_base_k_digit(N-adjust) + adjust

๐Ÿงฎ N to Depth

The structure of the tree means

depth = floor(logk(N+1))    for n_start=0

For example if k=3 then all of N=8 through N=25 inclusive have depth=floor(log3(N+1))=2. With an n_start it becomes

depth = floor(logk(N-(Nstart-1)))

And n_start=1 is the simplest case of all โ€” it's just the length of N written in base-k digits:

depth = floor(logk(N))     for n_start=1

๐ŸŒˆ OEIS ๐ŸŒˆ

This tree is in Sloane's Online Encyclopedia of Integer Sequences, as

<http://oeis.org/A191379> (etc)

k=3, n_start=0  (the defaults)
  A191379   X coordinate, and Y=X(N+n)

And as noted above, k=2 is the Calkin-Wilf tree. So go see "OEIS" in Math::PlanePath::RationalsTree for the CW related sequences. ๐Ÿฆ‹


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

  • Math::PlanePath
  • Math::PlanePath::RationalsTree
  • Math::PlanePath::PythagoreanTree

๐Ÿ  HOME PAGE ๐Ÿ 

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


๐Ÿ“œ LICENSE ๐Ÿ“œ

Copyright 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 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/>.


Bestie that's a wrap on the whole page ๐Ÿ’– this one's perl v5.32.0, dated 2021-01-23, from the Math::...hanTree(3pm) manual โ€” go forth and tree, you absolute legend. ๐Ÿ‘‘โœจ