codexcompound-growth-limit.py
compound-growth-limit.py
"""Exact discrete proportional updates over one fixed interval approach e."""
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
def main():
counts = np.arange(1, 1001)
accumulated = np.exp(counts*np.log1p(1/counts))
assert np.isclose(accumulated[0], 2)
assert np.isclose(accumulated[1], 2.25)
assert np.all(np.diff(accumulated) > 0)
assert np.all(accumulated < np.e)
logarithms = counts*np.log1p(1/counts)
assert np.all(1/(1+1/counts) <= logarithms)
assert np.all(logarithms <= 1)
fig, ax = plt.subplots(figsize=(8.4, 4.7), dpi=120)
blue, orange = "#176997", "#b55d20"
ax.semilogx(counts, accumulated, color=blue, lw=2.5,
label=r"$(1+1/n)^n$")
ax.axhline(np.e, color=orange, ls="--", lw=1.7,
label=r"Limit $e=2.7182818\ldots$")
for n, label, xtext, ytext in [(1, "2", 1.5, 2.08), (2, "2.25", 3, 2.21),
(10, "2.59374", 15, 2.49),
(100, "2.70481", 80, 2.60),
(1000, "2.71692", 360, 2.66)]:
value=accumulated[n-1]
ax.scatter(n, value, color=blue, s=35, zorder=4)
ax.annotate(f"n = {n}: {label}", (n, value), xytext=(xtext, ytext), fontsize=10,
arrowprops={"arrowstyle": "->", "color": blue})
ax.set(xlim=(.85, 1200), ylim=(1.96, 2.79),
xlabel="Number of equal updates n (logarithmic scale)",
ylabel="Final value from a starting value of 1")
ax.set_title("More steps, smaller increments, one fixed interval", fontsize=14, pad=13)
ax.legend(loc="lower right", frameon=False, fontsize=11)
ax.grid(alpha=.17, which="both")
fig.text(.5, .025, "Each update multiplies the current value by 1 + 1/n; the total is below e at every finite n.",
ha="center", fontsize=10)
fig.subplots_adjust(left=.095, right=.975, top=.86, bottom=.18)
fig.savefig(Path.cwd() / Path(__file__).with_suffix(".png").name, facecolor="white")
plt.close(fig)
if __name__ == "__main__":
main()