答案1
(请参阅最后的更新以了解替代输出)
使用 Lualatex 和 tikz 完成,只需将代码从这个 javascript 小提琴
这是数字 36 的示例结果:
生成方式如下:
\begin{tikzpicture}[y=-1cm]
\draw[red] (0,0) rectangle (5,5);
\primediagram{36}{2.5}{2.5}{2.5};
\end{tikzpicture}
该宏\primediagram
接收四个参数,第一个参数是要表示的数字,其余三个参数是要生成的图表的中心和半径。
一个更有趣的例子:
由 tikz 循环生成:
\begin{tikzpicture}[y=-1cm]
\foreach \y in {0,...,6} {
\foreach \x in {0,...,4} {
\draw(3.1*\x,3.1*\y) rectangle +(3,3);
\pgfmathtruncatemacro{\number}{\x+5*\y}
\primediagram{\number}{3.1*\x+1.55}{3.1*\y+1.55}{1.3};
\node[black!50, below right] at (3.1*\x, 3.1*\y) {\number};
}
}
\end{tikzpicture}
完整代码
为了生成这些图形,需要三个文件:
primediagram.sty
这个只是定义了与 lua 代码接口的 tex 宏:
% This is primediagram.sty
\directlua{dofile("primediagram.lua")}
\newcommand{\primediagram}[4]{
\directlua{draw(#1,#2,#3,#4)}
}
primediagram.lua
这包含实际执行计算并输出\draw
tikz 所需命令的 lua 代码。它是上述内容的直接 lua 翻译JavaScript 代码。
-- This is primediagram.lua
local smallfirst = false
local off2 = 0
function circle(cx, cy, s)
tex.print(string.format("\\fill (%f, %f) circle(%f);", cx, cy, s))
end
function draw(N, cx, cy, s)
if N==0 then
return
end
if N==1 then
circle(cx,cy,s)
else
local f, r, d, oy, x, y
f = primefactor(N)
if f == 2 then
oy = 0;
if N % 4 == 0 then
f = 4;
r = 2 * s / (f + 2);
d = f * s / (f + 2);
else
f = 2;
r = 0.75 * 2 * s / (2 + 2);
d = 2 * s / (2 + 2);
end
else
r = 2 * s / (f + 2);
d = f * s / (f + 2);
oy = d / 2 * (1 - math.cos(math.pi / f));
end
for i = 0, f do
x = math.sin(math.pi + 2 * math.pi * (i + 0.5) / f + off2);
y = math.cos(math.pi + 2 * math.pi * (i + 0.5) / f + off2);
draw(N / f, cx + x * d, cy - y * d + oy, r);
end
end
end
local primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109,
113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257,
263, 269, 271}
function primefactor(N)
local ans = N;
for i,pi in pairs(primes) do
if N % pi == 0 then
ans = pi;
if smallfirst then
return ans
end
end
end
return ans
end
主要文件
您只需包含sty
包,例如:
\documentclass{article}
\usepackage{nopageno}
\usepackage[margin=1cm]{geometry}
\usepackage{tikz}
\usepackage{primediagram}
\begin{document}
\begin{tikzpicture}[y=-1cm]
\foreach \y in {0,...,6} {
\foreach \x in {0,...,4} {
\draw(3.1*\x,3.1*\y) rectangle +(3,3);
\pgfmathtruncatemacro{\number}{\x+5*\y}
\primediagram{\number}{3.1*\x+1.55}{3.1*\y+1.55}{1.3};
\node[black!50, below right] at (3.1*\x, 3.1*\y) {\number};
}
}
\end{tikzpicture}
\end{document}
由此得到如图所示的图形。
更新:轮换子组
如果每个子组都按照绘制的角度进行旋转,输出结果会有所不同,更漂亮,更接近原帖所展示的结果。我还删除了点的黑色填充:
这是新的代码(只需primediagram.lua
替换):
-- This is primediagram.lua
local smallfirst = false
local off2 = 0
function circle(cx, cy, s)
tex.print(string.format("\\draw (%f, %f) circle(%f);", cx, cy, s))
end
function draw(N, cx, cy, s, a)
if N==0 then
return
end
if N==1 then
circle(cx,cy,s)
else
local f, r, d, x, y, off
off = 0
a = a or 0
tex.print(string.format("\\begin{scope}[xshift=%fcm, yshift=-%fcm, rotate=%f]", cx, cy, a))
f = primefactor(N)
if f == 2 then
oy = 0;
if N % 4 == 0 then
f = 4;
r = 2 * s / (f + 2);
d = f * s / (f + 2);
else
f = 2;
r = 0.75 * 2 * s / (2 + 2);
d = 2 * s / (2 + 2);
off = math.pi/2
end
else
r = 2 * s / (f + 2);
d = f * s / (f + 2);
end
for i = 0, f-1 do
local angle = math.pi + 2 * math.pi * (i + 0.5) / f + off
x = math.sin(angle);
y = math.cos(angle);
draw(N / f, x * d, y * d , r, angle*180/math.pi+180);
end
tex.print("\\end{scope}")
end
end
local primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43,
47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109,
113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181,
191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257,
263, 269, 271}
function primefactor(N)
local ans = N;
for i,pi in pairs(primes) do
if N % pi == 0 then
ans = pi;
if smallfirst then
return ans
end
end
end
return ans
end
答案2
这是一个tikzmath
解决方案。
\documentclass[varwidth=147mm]{standalone}
\usepackage{tikz}
\usetikzlibrary{math}
\tikzmath{
% draw diagram for \n centered at (\x,\y) with radius \r
function primediagram(\n,\x,\y,\r){
int \n, \largest;
\largest = \n;
if \n != 4 then {% 4 is considered as particular case
% find the largest prime divisor
for \d in {61,59,53,47,43,41,37,31,29,23,19,17,13,11,7,5,3,2}{
if \d < \n && \largest == \n && abs(\n/\d - div(\n,\d)) < .001 then{
\largest = \d;
};
};
};
% if \n is prime or 4, draw the circles, else recursion ...
\step = 360/\largest;
for \i in {1,...,\largest}{
\a = (\n==2||\n==8||\n==32)? 0:((\n==4)? 45:90); % aesthetic adjustment
\newx = \n==1 ? 0 : \x+cos(\a+\i*\step)*\r;
\newy = \n==1 ? 0 : \y+sin(\a+\i*\step)*\r;
if \largest == \n then{
{\fill (\newx,\newy) circle(1.8*\r/\largest);};
}
else {
primediagram(\n/\largest,\newx,\newy,\r/\largest);
};
};
};
}
\begin{document}
\foreach \n in {1,...,35}{%
\begin{tikzpicture}[scale=.7]
\path (2.1,-2.1) rectangle (-2.1,2.1);
\draw (2,-2) rectangle (-2,2) node[red,below right]{\n};
\tikzmath{primediagram(\n,0,0,1);};
\end{tikzpicture}\linebreak[0]%
}
\end{document}