OEIS/A291939: Difference between revisions

From tehowiki
Jump to navigation Jump to search
imported>Gfis
with .pl
imported>Gfis
m 2 x ")" removed
 
(5 intermediate revisions by the same user not shown)
Line 10: Line 10:
==Generating Perl Program==
==Generating Perl Program==
[[File:A291939-3d.png|600px|right|First 5 terms of A291939]]
[[File:A291939-3d.png|600px|right|First 5 terms of A291939]]
After some experimenting it became clear that the description is incomplete, and that it is difficult to develop the sequence with 2-dimensional paper and pencil. The following Perl program generates the sequence, and it has a more detailled description at the beginning:
After some experimenting it became clear that the description is incomplete, and that it is difficult to develop the sequence with 2-dimensional paper and pencil. The Perl program below generates the sequence, and it has a more detailled description at the beginning. The latest version of the program and its auxilliary files for the 3D visualization can be found on a [https://github.com/gfis/fasces/tree/master/oeis/A291939 Github repository].
<pre>
<pre>
<nowiki>
<nowiki>
Line 34: Line 34:
# and the "layer" z is outside.
# and the "layer" z is outside.
# Process all CSs with increasing starting number coln = 1, 2, 3 ... 10000.
# Process all CSs with increasing starting number coln = 1, 2, 3 ... 10000.
# Begin at the end of any CS (4 2 1), and proceeding up to
# Begin at the end of any CS (4, 2, 1), and proceeding up to
# the starting number. Name the elements e(1) = 1, e(2) = 2, e(3) = 4 etc.
# the starting number. Name the elements e[1] = 1, e[2] = 2, e[3] = 4 etc.
# Position the trailing element e(1) = 1 at coordinates (x,y,z) = (0,0,0).  
# Position the trailing element e[1] = 1 at coordinates (x,y,z) = (0,0,0).  
# Investigate all e[i] (i > 1):
# Investigate all e[i] (i > 1):
# for even e[i] "go right" = store e[i] at (e[i-1)].(x+1), e[i-1].y, e[i-1].z),  
# for even e[i] "go right" = store e[i] at (e[i-1].(x+1), e[i-1].y, e[i-1].z),  
# for odd  e[i] "go down"  = store e[i] at (e[i-1)].x, e[i-1].(y+1), e[i-1].z),
# for odd  e[i] "go down"  = store e[i] at (e[i-1].x, e[i-1].(y+1), e[i-1].z),
# whenever that position is not occupied by a different number,  
# whenever that position is not occupied by a different number,  
# otherwise "go up", i.e. increase the layer z by one for all new elements
# otherwise "go out", i.e. increase the layer z by one for all new elements
# to be stored from now on.
# to be stored from now on.
# The target sequence A291939 = a(n) consists of the starting  
# The target sequence A291939 = a(n) consists of the starting  
Line 123: Line 123:
} # allocate
} # allocate
</nowiki></pre>
</nowiki></pre>
==Input Collatz sequences==
==Input Collatz sequences==
The program reads the first 10000 Collatz sequences from [https://oeis.org/A070165/a070165.txt a070165.txt]:
The program reads the first 10000 Collatz sequences from [https://oeis.org/A070165/a070165.txt a070165.txt]:
Line 153: Line 154:
19/21: [19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
19/21: [19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
20/8: [20, 10, 5, 16, 8, 4, 2, 1]
20/8: [20, 10, 5, 16, 8, 4, 2, 1]
21/8: [21, 64, 32, 16, 8, 4, 2, 1]
22/16: [22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
23/16: [23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
24/11: [24, 12, 6, 3, 10, 5, 16, 8, 4, 2, 1]
25/24: [25, 76, 38, 19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
26/11: [26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
27/112: [27, 82, 41, 124, 62, 31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
28/19: [28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
29/19: [29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
30/19: [30, 15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
31/107: [31, 94, 47, 142, 71, 214, 107, 322, 161, 484, 242, 121, 364, 182, 91, 274, 137, 412, 206, 103, 310, 155, 466, 233, 700, 350, 175, 526, 263, 790, 395, 1186, 593, 1780, 890, 445, 1336, 668, 334, 167, 502, 251, 754, 377, 1132, 566, 283, 850, 425, 1276, 638, 319, 958, 479, 1438, 719, 2158, 1079, 3238, 1619, 4858, 2429, 7288, 3644, 1822, 911, 2734, 1367, 4102, 2051, 6154, 3077, 9232, 4616, 2308, 1154, 577, 1732, 866, 433, 1300, 650, 325, 976, 488, 244, 122, 61, 184, 92, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
32/6: [32, 16, 8, 4, 2, 1]
33/27: [33, 100, 50, 25, 76, 38, 19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
34/14: [34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
35/14: [35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
36/22: [36, 18, 9, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
37/22: [37, 112, 56, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
...
...
10000/30: [10000, 5000, 2500, 1250, 625, 1876, 938, 469, 1408, 704, 352, 176, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
10000/30: [10000, 5000, 2500, 1250, 625, 1876, 938, 469, 1408, 704, 352, 176, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
</nowiki>
</pre>
== makefile ==
With <code>mode=0</code> the program generates the b-file for A291939 only. With <code>mode=1</code> it outputs additional comment lines which can be used to build a visualization of the underlaying 3D structure. The generation of the files is controlled by a Unix <code>makefile</code>:
<pre>
<nowiki>
#!make
# draw Collatz sequences, OEIS A070165 -> A291939
# @(#) $Id$
# 2018-03-23, Georg Fischer
#----------
MODE=0
SIZE=100
all: bfile
#----------
wget:
wget https://oeis.org/A070165/a070165.txt
bfile:
perl a291939.pl $(MODE) a070165.txt | tee b291939.txt
a291939-3d.js: makefile a291939.pl a070165.txt
grep -E "^[0-9]" a070165.txt | head -$(SIZE) > cs100.tmp
perl a291939.pl 1 cs100.tmp > mode1.tmp
grep -E "# coords" mode1.tmp | cut -d " " -f 3 >> $@
echo var vec = [0,0,0,1 > $@
perl a291939.pl 1 cs100.tmp | grep -E "# coords" | cut -d " " -f 3 >> $@
echo ]\; >> $@
echo var colls = [0,0,0,1,1 >> $@
grep -E "# collision" mode1.tmp | cut -d " " -f 3 >> $@
echo ,0,0,0,0,0]\; >> $@
cat $@
deploy:
echo either make deploy-windows or make deploy-linux
deploy-windows:
cp -v *.js *.html c:/users/gfis/xampp/htdocs/threejs
deploy-linux:
sudo cp -v *.js *.html        /var/www/html/threejs
sudo chown -R georg:www-data  /var/www/html/threejs/*
sudo chmod 750                /var/www/html/threejs/*
</nowiki>
</pre>
== 3D Visualization ==
For the visualization, the Javascript library '''[http://threejs.org Three.js]''' is used. There is
* a variable data file <code>a291939-3d.js</code> which is generated by the <code>makefile</code> above, and
* a fixed frame '''<code>[http://www.teherba.org/threejs/a291939-3d.html a291939-3d.html]</code>''' for the display of the sequence which is shown below.
Once that HTML file with the Javascript functions has been loaded (that may take about 15 s), the generated 3D structure is shown as a sequence of colored cubes, with the Collatz sequence elements written inside. The interesting part is the region with red elements in the upper left. Each layer has a different color, and the beginnings of the layers (which form sequence A291939) are highlighted with white edges and a pointer to the corresponding CS sequence number (1, 12, 19, 27, 37 ...). 
The structure can be
* rotated by clicking and moving the mouse,
* zoomed by turning the mouse wheel,
* shifted up, down, left and right by the cursor arrow keys.
Only the first 100 Collatz sequences were processed for this example (parameter <code>SIZE</code> in the <code>makefile</code>). The resulting structure is already rather extended because of the CS sequences with more than 100 elements (for example 27).
The HTML file must be stored on a webserver together with the Javascripts from the [http://threejs.org threejs.org] distribution which are referenced at the beginning of the body of <code>[http://www.teherba.org/threejs/a291939-3d.html a291939-3d.html]</code>.
<pre>
<nowiki>
<!DOCTYPE html>
<html lang="en">
<!--3D visualization of OEIS A291939
    @(#) $Id$
    2018-03-25, dr.georg.fischer@gmail.com
-->
<head>
    <title>A291939.3D</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            color: #ccc;
            font-family:Monospace;
            font-size:13px;
            text-align:center;
            font-weight: bold;
            background-color: #fff;
            margin: 0px;
            overflow: hidden;
        }
        #info {
            color:#ccc;
            position: absolute;
            top: 0px; width: 100%;
            padding: 5px;
        }
        a {
            color: red;
        }
    /*
    */
    </style>
</head>
<body>
    <div id="container"></div>
    <div id="info">
        <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - OEIS A291939 by Georg Fischer
    </div>
    <script src="js/three.min.js"></script>
    <script src="THREE.MeshLine.js"></script>
    <script src="js/renderers/Projector.js"></script>
    <script src="js/controls/OrbitControls.js"></script>
    <script src="js/Detector.js"></script>
    <script src="js/libs/stats.min.js"></script>
    <script src="a291939-3d.js"></script>
    <script>
    var xmax = 0;
    var ymax = 0;
    var zmax = 0;
    </script>
    <script>
    function world(scene, myfont) {
        var center = new THREE.Vector3(0, 0, 0);
        var points = [];
        var elems  = [];
        var layers = []; //
        xmax  = 0;
        ymax  = 0;
        zmax  = 0;
        var i = 0;
        while (i < vec.length) { // determine maximums
            var xcor = vec[i ++]; if (xcor > xmax) { xmax = xcor; }
            var ycor = vec[i ++]; if (ycor > ymax) { ymax = ycor; }
            var zcor = vec[i ++]; if (zcor > zmax) { zmax = zcor; }
            i ++;
        } // while maximums
        var sizex  = parseInt(xmax * 0.66); // slightly shifted to the left
        var sizey  = parseInt(ymax / 2);
        var sizez  = parseInt(zmax / 2);
        var width  = 1; // of the cubes
        var fact = 10;
        i = 0;
        while (i < vec.length) {
            var xcor = vec[i ++];
            var ycor = vec[i ++];
            var zcor = vec[i ++];
            var elem = vec[i ++];
            layers.push(zcor);
            elems.push(elem);
            var x = center.x + (xcor - sizex) * fact;
            var y = center.y + (ycor - sizey) * fact;
            var z = center.z + (zcor - sizez) * fact;
            points.push(new THREE.Vector3(x, -y, z));
        } // while i
   
        var materials = [
            new THREE.MeshBasicMaterial({ color: 0xffffff, overdraw: 0.5 }),
            new THREE.MeshBasicMaterial({ color: 0xffffff, overdraw: 0.5 })
            ];
        var color3 = new THREE.Color(0xffffff);
        var icoll = 0; // index for collisions
        for (i = 0; i < points.length - 1; i ++) {
            var group  = new THREE.Group();
            var elem = elems[i];
            color3.setHSL((0.1 + layers[i] * 0.7) / zmax, 1.0, 0.5);
            var geometry = new THREE.BoxGeometry(width * fact, width * fact, width * fact);
            geometry.translate(points[i].x, points[i].y, points[i].z);
            var material = new THREE.MeshBasicMaterial(
                    { color: color3, opacity: 0.4, transparent: true} );
            var matwhite = new THREE.MeshBasicMaterial(
                    { color: 0xffffff, opacity: 0.4, transparent: false} );
            group.add(new THREE.Mesh(geometry, material));
            if (elem == colls[icoll+ 3]) { // element moves to next layer
                var line = new THREE.LineSegments(new THREE.EdgesGeometry(geometry)
                        , new THREE.LineBasicMaterial({ color: 0xffffff }));
                group.add(line);
                var geop = new THREE.Geometry();
                geop.vertices.push(new THREE.Vector3(points[i].x + fact*0.5, points[i].y + fact*0.5, points[i].z+ fact*0.5));
                var endp = new THREE.Vector3(points[i].x + fact*2, points[i].y + fact*2, points[i].z+ fact*2);
                geop.vertices.push(endp);
                line = new THREE.Line(geop, new THREE.LineBasicMaterial({ color: 0xffffff }))
                group.add(line);
                var geot = new THREE.TextGeometry(colls[icoll + 4],
                        { font: myfont, size: fact/3, height: fact/16, curveSegments: 8 });
                geot.translate
                    ( points[i].x + fact*2
                    , points[i].y + fact*2
                    , points[i].z + fact*2
                    );
                group.add(new THREE.Mesh(geot, matwhite));
                icoll += 5;
                // next layer
            } else {
                var line = new THREE.LineSegments(new THREE.EdgesGeometry(geometry)
                        , new THREE.LineBasicMaterial({ color: color3 }));
                group.add(line);
            }
            var geometry2 = new THREE.TextGeometry(elem,
                    { font: myfont, size: fact/3, height: fact/16, curveSegments: 8 });
            geometry2.translate
                    ( points[i].x - fact / 3
                    , points[i].y
                    , points[i].z - fact / 4
                    );
            group.add(new THREE.Mesh(geometry2, materials));
            scene.add(group);
        } // for i
    } // world
    </script>
    <script>       
        if ( ! Detector.webgl ) {
            Detector.addGetWebGLMessage();
        }
        var stats;
        var camera;
        var controls;
        var scene;
        var renderer;
        var loader = new THREE.FontLoader();
        loader.load( 'fonts/helvetiker_regular.typeface.json', function ( font ) {
            // all remaining main code must be placed in this block
            init( font );
            // render(); // remove when using next line for animation loop (requestAnimationFrame)
            animate();
        } ); // load
        function init( myfont ) {
            scene = new THREE.Scene();
            scene.background = new THREE.Color( 0x000000 ); // 0xcccccc );
            world( scene, myfont );
            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio( window.devicePixelRatio );
            renderer.setSize( window.innerWidth, window.innerHeight );
            var container = document.getElementById( 'container' );
            container.appendChild( renderer.domElement );
            camera = new THREE.PerspectiveCamera( 33, window.innerWidth / window.innerHeight, 1, 10000 );
            camera.position.z = 20 * xmax;
            controls = new THREE.OrbitControls( camera, renderer.domElement );
            controls.addEventListener( 'change', render ); // remove when using animation loop
            // enable animation loop when using damping or autorotation
            controls.enableDamping = true;
            controls.dampingFactor = 0.25;
            controls.enableZoom = true;
           
            if (false) { // lights
                var lights = [];
                lights[ 0 ] = new THREE.PointLight( 0xffffff, 1, 0 );
                lights[ 1 ] = new THREE.PointLight( 0xffffff, 1, 0 );
                lights[ 2 ] = new THREE.PointLight( 0xffffff, 1, 0 );
                lights[ 0 ].position.set( 0, 200, 0 );
                lights[ 1 ].position.set( 100, 200, 100 );
                lights[ 2 ].position.set( - 100, - 200, - 100 );
                scene.add( lights[ 0 ] );
                scene.add( lights[ 1 ] );
                scene.add( lights[ 2 ] );
            } else { // lights                       
                var
                light = new THREE.DirectionalLight( 0xffffff );
                light.position.set( 1, 1, 1 );
                scene.add( light );
                light = new THREE.DirectionalLight( 0x002288 );
                light.position.set( -1, -1, -1 );
                scene.add( light );
                light = new THREE.AmbientLight( 0x222222 );
                scene.add( light );
            } // lights
            stats = new Stats();
            container.appendChild( stats.dom );
            window.addEventListener( 'resize', onWindowResize, false );
        } // init
       
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize( window.innerWidth, window.innerHeight );
        }
        function animate() {
            requestAnimationFrame( animate );
            controls.update(); // required if controls.enableDamping = true, or if controls.autoRotate = true
            stats.update();
            render();
        }
        function render() {
            var time = Date.now() * 0.0001;
            time = 0.0;
            for ( var i = 0; i < scene.children.length; i ++ ) {
                var object = scene.children[i];
                object.rotation.y = time; // * ( i % 2 ? 1 : -1 );
            }
            renderer.render( scene, camera );
        }
    </script>
</body>
</html>
</nowiki>
</nowiki>
</pre>
</pre>

Latest revision as of 09:12, 11 April 2018

First number on layer n of hailstone chain

The sequence had keyword unkn until March 2018, and there were only 5 terms:

  • 1, 12, 19, 27, 37

The original comment was:

If hailstone chains are strictly drawn in numerical order at right angles with consistent 
direction, overlaps occur. The first set of numbers that do not overlap could be considered the 
'first layer'. Once an overlap is needed, all numbers farther up the chain (inclusive) are on a 
higher layer. This is the sequence of the first numbers to appear on layer n.

Hailstone chain is a synonym for Collatz sequences or the 3n+1 problem - see Index entries for sequences related to 3x+1 (or Collatz) problem.

Generating Perl Program

First 5 terms of A291939
First 5 terms of A291939

After some experimenting it became clear that the description is incomplete, and that it is difficult to develop the sequence with 2-dimensional paper and pencil. The Perl program below generates the sequence, and it has a more detailled description at the beginning. The latest version of the program and its auxilliary files for the 3D visualization can be found on a Github repository.


#!perl

# Build b-file of OEIS A291939 from Collatz sequences in A070165
# @(#) $Id$
# 2018-03-25, dr.georg.fischer@gmail.com
#------------------------------------------------------
# Usage:
#	wget https://oeis.org/A070165/a070165.txt
#	perl a291939.pl 0 a070165.txt > b291939.txt
#   perl a291939.pl mode infile > outfile
#       mode = 0: print the raw b-file for A291939
#       mode = 1: insert additional comments for the 3D structure
#       mode = 2: show additional trace data
# Sequence:
#     1, 12, 19, 27, 37, 43, 51, 55, 75, 79, ... 9997
# Explanation:
# Take the Collatz sequences (CSs) from A070165, and build a 3-dimensional 
# structure representing all CSs up to some starting number (10000).
# In that 3D structure, the y direction is downwards, x is to the right, 
# and the "layer" z is outside.
# Process all CSs with increasing starting number coln = 1, 2, 3 ... 10000.
# Begin at the end of any CS (4, 2, 1), and proceeding up to
# the starting number. Name the elements e[1] = 1, e[2] = 2, e[3] = 4 etc.
# Position the trailing element e[1] = 1 at coordinates (x,y,z) = (0,0,0). 
# Investigate all e[i] (i > 1):
# for even e[i] "go right" = store e[i] at (e[i-1].(x+1), e[i-1].y, e[i-1].z), 
# for odd  e[i] "go down"  = store e[i] at (e[i-1].x, e[i-1].(y+1), e[i-1].z),
# whenever that position is not occupied by a different number, 
# otherwise "go out", i.e. increase the layer z by one for all new elements
# to be stored from now on.
# The target sequence A291939 = a(n) consists of the starting 
# values of the CSs which reach a z coordinate of n for the first 
# time.
#--------------------------------------------------------
use strict;
my $mode = 1; # 0 = b-file only, 1 = with 3D coordinates, 2 = more trace output
if (scalar(@ARGV) > 0) {
    $mode = shift(@ARGV);
}
my $bfn = 1; # index for target b-file
my $layer = 1;
my @elems; # maps elements of a CS to their coordinates (x,y,z)
$elems[1] = "0,0,0";
my %coords; # maps (x,y,layer=z) -> elements of a CS
$coords{$elems[1]} = 1;
print <<"GFis";
# b-file for A291939, n=1..1769, generated by perl $0 $mode a070165.txt
1 1
GFis
my @colseq = ();
my $curr_layer = 0;
while (<>) {
    next if ! m{\A\d}; # no digit in column 1 -> skip initial comment lines
	# 9/20: [9, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
	s/\s+//g; # remove all spaces
    my ($pair,$cs) = split(/\:/);
    my ($coln, $count) = split(/\//, $pair); # CS starts at $coln and has $count elements
    $cs =~ s{[\[\]]}{}; # remove square brackets
    @colseq = split(/\,/, $cs);
    if ($mode >= 2) {
        print "# evaluate CS($coln) = " . join(" ", @colseq) . "\n";
    }
    my ($x, $y, $z) = split(/\,/, $elems[1]);
    my $ind = scalar(@colseq) - 1; 
    $ind --; # element's indexes run backwards in contradiction to the explanation above
    while ($ind >= 0) {
        my $elcurr = $colseq[$ind];
        if (defined($elems[$elcurr])) {
            ($x, $y, $z) = split(/\,/, $elems[$elcurr]); 
        } else { # undefined - append to chain
            if (($elcurr & 1) == 0) { # even -> go right
                $x ++;
                &investigate($coln, $elcurr, $x, $y, $curr_layer);
            } else { # odd -> go down
                $y ++;
                &investigate($coln, $elcurr, $x, $y, $curr_layer);
            }
        } # undefined
        $ind --;
    } # while $ind
} # while <>
#--------
sub investigate {
    my ($coln, $elcurr, $x, $y, $z) = @_;
    if (defined($coords{"$x,$y,$z"})) { 
        my $stelem = $coords{"$x,$y,$z"};
        if ($stelem ne $elcurr) { # collision
            $curr_layer ++;
            $z = $curr_layer;
            if ($mode >= 1) {
                print "# collision ,$x,$y,$z,$elcurr,$coln // $stelem, layer=$curr_layer\n";
            }
            $bfn ++; 
            print "$bfn $coln\n"; # print the b-file entry
            &allocate($elcurr, $x, $y,$z);
        } # else same element - ignore
    } else { # undefined
        &allocate($elcurr, $x, $y,$z);
    } # undefined
} # investigate
#--------
sub allocate {
    my ($elcurr, $x, $y, $z) = @_;
    $coords{"$x,$y,$z"} = $elcurr;
    $elems[$elcurr] = "$x,$y,$z";
    if ($mode >= 1) {
        print "# coords ,$x,$y,$z,$elcurr\n";
    }
} # allocate

Input Collatz sequences

The program reads the first 10000 Collatz sequences from a070165.txt:


content of https://oeis.org/A070165/a070165.txt follows:
This file has 10000 rows showing the following for each row:
  a) Starting number for Collatz sequence ending with 1 (a.k.a. 3x+1 sequence).
  b) Number of terms in sequence (a.k.a. number of halving and tripling steps to reach 1).
  c) Actual sequence as a vector.

1/4: [1, 4, 2, 1]
2/2: [2, 1]
3/8: [3, 10, 5, 16, 8, 4, 2, 1]
4/3: [4, 2, 1]
5/6: [5, 16, 8, 4, 2, 1]
6/9: [6, 3, 10, 5, 16, 8, 4, 2, 1]
7/17: [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
8/4: [8, 4, 2, 1]
9/20: [9, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
10/7: [10, 5, 16, 8, 4, 2, 1]
11/15: [11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
12/10: [12, 6, 3, 10, 5, 16, 8, 4, 2, 1]
13/10: [13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
14/18: [14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
15/18: [15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1]
16/5: [16, 8, 4, 2, 1]
17/13: [17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
18/21: [18, 9, 28, 14, 7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
19/21: [19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]
20/8: [20, 10, 5, 16, 8, 4, 2, 1]
...
10000/30: [10000, 5000, 2500, 1250, 625, 1876, 938, 469, 1408, 704, 352, 176, 88, 44, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1]

makefile

With mode=0 the program generates the b-file for A291939 only. With mode=1 it outputs additional comment lines which can be used to build a visualization of the underlaying 3D structure. The generation of the files is controlled by a Unix makefile:


#!make

# draw Collatz sequences, OEIS A070165 -> A291939
# @(#) $Id$
# 2018-03-23, Georg Fischer
#----------
MODE=0
SIZE=100
all: bfile
#----------
wget:
	wget https://oeis.org/A070165/a070165.txt
bfile:
	perl a291939.pl $(MODE) a070165.txt | tee b291939.txt
a291939-3d.js: makefile a291939.pl a070165.txt
	grep -E "^[0-9]" a070165.txt | head -$(SIZE) > cs100.tmp
	perl a291939.pl 1 cs100.tmp > mode1.tmp 
	grep -E "# coords" mode1.tmp | cut -d " " -f 3 >> $@
	echo var vec = [0,0,0,1 > $@
	perl a291939.pl 1 cs100.tmp | grep -E "# coords" | cut -d " " -f 3 >> $@
	echo ]\; >> $@
	echo var colls = [0,0,0,1,1 >> $@
	grep -E "# collision" mode1.tmp | cut -d " " -f 3 >> $@
	echo ,0,0,0,0,0]\; >> $@
	cat $@
deploy: 
	echo either make deploy-windows or make deploy-linux
deploy-windows:
	cp -v *.js *.html c:/users/gfis/xampp/htdocs/threejs
deploy-linux:
	sudo cp -v *.js *.html        /var/www/html/threejs
	sudo chown -R georg:www-data  /var/www/html/threejs/*
	sudo chmod 750                /var/www/html/threejs/* 

3D Visualization

For the visualization, the Javascript library Three.js is used. There is

  • a variable data file a291939-3d.js which is generated by the makefile above, and
  • a fixed frame a291939-3d.html for the display of the sequence which is shown below.

Once that HTML file with the Javascript functions has been loaded (that may take about 15 s), the generated 3D structure is shown as a sequence of colored cubes, with the Collatz sequence elements written inside. The interesting part is the region with red elements in the upper left. Each layer has a different color, and the beginnings of the layers (which form sequence A291939) are highlighted with white edges and a pointer to the corresponding CS sequence number (1, 12, 19, 27, 37 ...). The structure can be

  • rotated by clicking and moving the mouse,
  • zoomed by turning the mouse wheel,
  • shifted up, down, left and right by the cursor arrow keys.

Only the first 100 Collatz sequences were processed for this example (parameter SIZE in the makefile). The resulting structure is already rather extended because of the CS sequences with more than 100 elements (for example 27).

The HTML file must be stored on a webserver together with the Javascripts from the threejs.org distribution which are referenced at the beginning of the body of a291939-3d.html.


<!DOCTYPE html>
<html lang="en">
<!--3D visualization of OEIS A291939 
    @(#) $Id$
    2018-03-25, dr.georg.fischer@gmail.com
-->
<head>
    <title>A291939.3D</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
    <style>
        body {
            color: #ccc;
            font-family:Monospace;
            font-size:13px;
            text-align:center;
            font-weight: bold;
            background-color: #fff;
            margin: 0px;
            overflow: hidden;
        }
        #info {
            color:#ccc;
            position: absolute;
            top: 0px; width: 100%;
            padding: 5px;
        }
        a {
            color: red;
        }
    /*
    */
    </style>
</head>
<body>
    <div id="container"></div>
    <div id="info">
        <a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - OEIS A291939 by Georg Fischer
    </div>
    <script src="js/three.min.js"></script>
    <script src="THREE.MeshLine.js"></script>
    <script src="js/renderers/Projector.js"></script>
    <script src="js/controls/OrbitControls.js"></script>
    <script src="js/Detector.js"></script>
    <script src="js/libs/stats.min.js"></script>
    <script src="a291939-3d.js"></script>
    <script>
    var xmax = 0;
    var ymax = 0;
    var zmax = 0;
    </script>
    <script>
    function world(scene, myfont) {
        var center = new THREE.Vector3(0, 0, 0);
        var points = [];
        var elems  = [];
        var layers = []; // 
        xmax   = 0;
        ymax   = 0;
        zmax   = 0;
        var i = 0;
        while (i < vec.length) { // determine maximums
            var xcor = vec[i ++]; if (xcor > xmax) { xmax = xcor; }
            var ycor = vec[i ++]; if (ycor > ymax) { ymax = ycor; }
            var zcor = vec[i ++]; if (zcor > zmax) { zmax = zcor; }
            i ++;
        } // while maximums
        var sizex   = parseInt(xmax * 0.66); // slightly shifted to the left
        var sizey   = parseInt(ymax / 2);
        var sizez   = parseInt(zmax / 2);
        var width  = 1; // of the cubes
        var fact = 10;
        i = 0;
        while (i < vec.length) {
            var xcor = vec[i ++];
            var ycor = vec[i ++];
            var zcor = vec[i ++];
            var elem = vec[i ++];
            layers.push(zcor);
            elems.push(elem);
            var x = center.x + (xcor - sizex) * fact;
            var y = center.y + (ycor - sizey) * fact;
            var z = center.z + (zcor - sizez) * fact;
            points.push(new THREE.Vector3(x, -y, z));
        } // while i
    
        var materials = [
            new THREE.MeshBasicMaterial({ color: 0xffffff, overdraw: 0.5 }),
            new THREE.MeshBasicMaterial({ color: 0xffffff, overdraw: 0.5 })
            ];
        var color3 = new THREE.Color(0xffffff);
        var icoll = 0; // index for collisions
        for (i = 0; i < points.length - 1; i ++) {
            var group  = new THREE.Group();
            var elem = elems[i];
            color3.setHSL((0.1 + layers[i] * 0.7) / zmax, 1.0, 0.5);
            var geometry = new THREE.BoxGeometry(width * fact, width * fact, width * fact);
            geometry.translate(points[i].x, points[i].y, points[i].z);
            var material = new THREE.MeshBasicMaterial(
                    { color: color3, opacity: 0.4, transparent: true} );
            var matwhite = new THREE.MeshBasicMaterial(
                    { color: 0xffffff, opacity: 0.4, transparent: false} );
            group.add(new THREE.Mesh(geometry, material));
            if (elem == colls[icoll+ 3]) { // element moves to next layer
                var line = new THREE.LineSegments(new THREE.EdgesGeometry(geometry)
                        , new THREE.LineBasicMaterial({ color: 0xffffff }));
                group.add(line);
                var geop = new THREE.Geometry();
                geop.vertices.push(new THREE.Vector3(points[i].x + fact*0.5, points[i].y + fact*0.5, points[i].z+ fact*0.5));
                var endp = new THREE.Vector3(points[i].x + fact*2, points[i].y + fact*2, points[i].z+ fact*2);
                geop.vertices.push(endp);
                line = new THREE.Line(geop, new THREE.LineBasicMaterial({ color: 0xffffff }))
                group.add(line);
                var geot = new THREE.TextGeometry(colls[icoll + 4], 
                        { font: myfont, size: fact/3, height: fact/16, curveSegments: 8 });
                geot.translate
                    ( points[i].x + fact*2
                    , points[i].y + fact*2
                    , points[i].z + fact*2
                    );
                group.add(new THREE.Mesh(geot, matwhite));
                icoll += 5;
                // next layer
            } else {
                var line = new THREE.LineSegments(new THREE.EdgesGeometry(geometry)
                        , new THREE.LineBasicMaterial({ color: color3 }));
                group.add(line);
            }
            var geometry2 = new THREE.TextGeometry(elem, 
                    { font: myfont, size: fact/3, height: fact/16, curveSegments: 8 });
            geometry2.translate
                    ( points[i].x - fact / 3
                    , points[i].y
                    , points[i].z - fact / 4
                    );
            group.add(new THREE.Mesh(geometry2, materials));
            scene.add(group);
        } // for i 
    } // world
    </script>
    <script>        
        if ( ! Detector.webgl ) {
            Detector.addGetWebGLMessage();
        }
        var stats;
        var camera;
        var controls;
        var scene;
        var renderer;
        var loader = new THREE.FontLoader();
        loader.load( 'fonts/helvetiker_regular.typeface.json', function ( font ) {
            // all remaining main code must be placed in this block
            init( font );
            // render(); // remove when using next line for animation loop (requestAnimationFrame)
            animate();
        } ); // load

        function init( myfont ) {
            scene = new THREE.Scene();
            scene.background = new THREE.Color( 0x000000 ); // 0xcccccc );
            world( scene, myfont );
            renderer = new THREE.WebGLRenderer();
            renderer.setPixelRatio( window.devicePixelRatio );
            renderer.setSize( window.innerWidth, window.innerHeight );
            var container = document.getElementById( 'container' );
            container.appendChild( renderer.domElement );
            camera = new THREE.PerspectiveCamera( 33, window.innerWidth / window.innerHeight, 1, 10000 );
            camera.position.z = 20 * xmax;
            controls = new THREE.OrbitControls( camera, renderer.domElement );
            controls.addEventListener( 'change', render ); // remove when using animation loop
            // enable animation loop when using damping or autorotation
            controls.enableDamping = true;
            controls.dampingFactor = 0.25;
            controls.enableZoom = true;
            
            if (false) { // lights
                var lights = [];
                lights[ 0 ] = new THREE.PointLight( 0xffffff, 1, 0 );
                lights[ 1 ] = new THREE.PointLight( 0xffffff, 1, 0 );
                lights[ 2 ] = new THREE.PointLight( 0xffffff, 1, 0 );
                lights[ 0 ].position.set( 0, 200, 0 );
                lights[ 1 ].position.set( 100, 200, 100 );
                lights[ 2 ].position.set( - 100, - 200, - 100 );
                scene.add( lights[ 0 ] );
                scene.add( lights[ 1 ] );
                scene.add( lights[ 2 ] );
            } else { // lights                         
                var 
                light = new THREE.DirectionalLight( 0xffffff );
                light.position.set( 1, 1, 1 );
                scene.add( light );
                light = new THREE.DirectionalLight( 0x002288 );
                light.position.set( -1, -1, -1 );
                scene.add( light );
                light = new THREE.AmbientLight( 0x222222 );
                scene.add( light );
            } // lights
            stats = new Stats();
            container.appendChild( stats.dom );
            window.addEventListener( 'resize', onWindowResize, false );
        } // init
        
        function onWindowResize() {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize( window.innerWidth, window.innerHeight );
        }
        function animate() {
            requestAnimationFrame( animate );
            controls.update(); // required if controls.enableDamping = true, or if controls.autoRotate = true
            stats.update();
            render();
        }
        function render() {
            var time = Date.now() * 0.0001;
            time = 0.0;
            for ( var i = 0; i < scene.children.length; i ++ ) {
                var object = scene.children[i];
                object.rotation.y = time; // * ( i % 2 ? 1 : -1 );
            }
            renderer.render( scene, camera );
        }
    </script>
</body>
</html>