This tells us to, at each step, replace each line segment with the spiked shape shown in the generator. Notice that the generator itself is made up of 4 copies of the initiator. In step 1, the single line segment in the initiator is replaced with the generator. For step 2, each of the four line segments of step 1 is replaced with a scaled copy of the generator:
This process is repeated to form Step 3. Again, each line segment is replaced with a scaled copy of the generator.
Notice that since Step 0 only had 1 line segment, Step 1 only required one copy of Step 0.
Since Step 1 had 4 line segments, Step 2 required 4 copies of the generator.
Step 2 then had 16 line segments, so Step 3 required 16 copies of the generator.
Step 4, then, would require 16 × 4 = 64 copies of the generator.
The shape resulting from iterating this process is called the Koch curve , named for Helge von Koch who first explored it in 1904.
import numpy as np
import matplotlib.pyplot as plt
DEPTH = 4 # EDIT: 0, 1, 2, 3, 4, 5 ... then re-run
SNOWFLAKE = False # EDIT: True glues three Koch curves into a snowflake
ROT = np.array([[0.5, -np.sqrt(3) / 2], [np.sqrt(3) / 2, 0.5]]) # 60 degree turn
def koch(points, depth):
# The generator: every segment becomes four segments with a spike in the middle.
for _ in range(depth):
new = [points[0]]
for p, q in zip(points[:-1], points[1:]):
d = (q - p) / 3
new += [p + d, p + d + ROT @ d, p + 2 * d, q]
points = np.array(new)
return points
if SNOWFLAKE:
corners = np.array([[0.0, 0.0], [1.0, 0.0], [0.5, np.sqrt(3) / 2], [0.0, 0.0]])
pieces = [koch(np.array([corners[i], corners[i + 1]]), DEPTH) for i in range(3)]
curve = np.vstack(pieces)
else:
curve = koch(np.array([[0.0, 0.0], [1.0, 0.0]]), DEPTH)
fig, ax = plt.subplots(figsize=(7.2, 4.0))
ax.plot(curve[:, 0], curve[:, 1], lw=1.1, color="#0b6e4f")
ax.set_aspect("equal")
ax.axis("off")
ax.set_title(f"Koch {'snowflake' if SNOWFLAKE else 'curve'}, step {DEPTH}", fontsize=13)
plt.show()
print(f"{'step':>5}{'segments':>12}{'= 4^step':>11}{'each is':>12}{'total length':>15}")
for k in range(DEPTH + 1):
print(f"{k:>5}{4 ** k:>12,}{'4^' + str(k):>11}{'1/' + str(3 ** k):>12}"
f"{(4 / 3) ** k:>15.4f}")
print("\nevery step multiplies the length by 4/3, so the curve gets longer without limit")
print(f"but it never leaves the box it started in - at step {DEPTH} it is "
f"{(4 / 3) ** DEPTH:.3f} units long inside a segment of length 1")