(This answer is based on studying the earlier answer to this question, by user Jairo A. del Rio. It's probably redundant, but I had starting typing this a few hours ago so might as well finish it up…)
One of the TeX/LaTeX packages for drawing is TikZ, about which I don't know much, except that normally you can draw diagrams by specifying the positions of nodes, and drawing paths between them (see The Morse code of TikZ in TUGboat), with various shortcuts.
Here we probably don't want to (compute and) specify the position of each position manually: for this, TikZ has some support for algorithmic graph drawing, in particular its “tree layout” is suitable here. For example, you can obtain the following result:

with the following input:
\documentclass[tikz]{standalone}
\usetikzlibrary{graphs,graphdrawing}
\usegdlibrary{trees}
\begin{document}
\begin{tikzpicture}
\graph[tree layout, grow=left]{
2 -> 1;
3 -> 10 -> 5;
4 -> 2;
5 -> 16 -> 8; 8 -> 4 -> 2;
64 -> 32 -> 16;
};
\end{tikzpicture}
\end{document}
(Just showed a variety of options for how we can specify the edges: we can chain multiple of them, whitespace doesn't matter, duplicate edges are ignored, etc.)
To get something closer to the image in the question:
Note the slight problem in the above graph, which is that the 2→1 edge is drawn going leftwards: I think the reason is that, because “2” was the first node mentioned (in the “2 —> 1” edge), it is treated as the root of the tree. To avoid this, we can either declare the node “1” beforehand, or start with an invisible edge like “1 -> [draw=none] 1; ” so that it becomes the root of the tree.
Though we have avoided having to specify the positions by using graph drawing (tree layout), it would further be convenient to avoid having to specify all the edges of the Collatz sequence. This can be done easily with Lua, if using LuaTeX (see below).
There's another wrinkle: if we try to fill in the “…” in \graph[tree layout, grow=left]{…}
using Lua or a macro, we run into issues of expansion (by the time TeX sees \graph
it needs to be able to find already-expanded text ahead). To work around this, we can use an appropriate sequence of \expandafter
s (see this or this question), or we can simply output the whole thing from Lua.
From these ideas, we can assemble a solution. Put the following in a file called collatz.lua
:
function collatz_edges(limit)
-- Returns edges for the numbers 1 to `limit` under the Collatz function.
-- E.g. for limit = 6, returns the following string (without linebreaks):
-- 1 -> [draw=none] 1;
-- 2 -> 1;
-- 3 -> 10; 10 -> 5; 5 -> 16; 16 -> 8; 8 -> 4; 4 -> 2;
-- 6 -> 3;
local edges = {'1 -> [draw=none] 1;'}
local next = {}
next[1] = 1
for x = 2, limit do
-- All edges x -> y
while not next[x] do
if x % 2 == 0 then y = x // 2 else y = 3 * x + 1 end
table.insert(edges, string.format('%s -> %s; ', x, y))
next[x] = y
x = y
end
end
return table.concat(edges)
end
function collatz_graph(limit)
return string.format([[
\begin{tikzpicture}
\graph[tree layout, grow=left]{%s};
\end{tikzpicture}]], collatz_edges(limit))
end
And then your .tex
document can be:
\documentclass[tikz]{standalone}
\usetikzlibrary{graphs,graphdrawing}
\usegdlibrary{trees}
\directlua{dofile('collatz.lua')}
\begin{document}
\directlua{tex.sprint(collatz_graph(25))}
\end{document}
Result:

Edit: Without the optimization to avoid duplicate edges using next
, it takes significantly longer even at say n=40. I tried another optimization: that of printing each edge immediately with tex.sprint
instead of accumulating all these strings and printing them once at the end, but it makes no noticeable difference even at n=10000 (running time of about 3 minutes). I guess that most of the time is taken inside TikZ itself (after Lua puts the edges into the TeX stream), and joining strings on the Lua side is relatively fast.