codexprime-sieve-smallest-factors.py
prime-sieve-smallest-factors.py
"""Show the first sieve pass responsible for each composite through 100."""
from math import isqrt
from pathlib import Path
import matplotlib.pyplot as plt
from matplotlib.patches import Patch, Rectangle
def sieve(limit):
smallest = [0]*(limit+1)
for p in range(2, isqrt(limit)+1):
if smallest[p]:
continue
for n in range(p*p, limit+1, p):
if not smallest[n]:
smallest[n] = p
return smallest
def trial_prime(n):
return n > 1 and all(n % d for d in range(2, isqrt(n)+1))
def main():
smallest = sieve(100)
primes = [n for n in range(2, 101) if not smallest[n]]
assert len(primes) == 25 and primes[-1] == 97
for n in range(2, 101):
assert (smallest[n] == 0) == trial_prime(n)
if smallest[n]:
p = smallest[n]
assert n % p == 0 and p <= isqrt(n)
assert p == next(d for d in range(2, n) if n % d == 0)
assert smallest[49] == 7 and smallest[91] == 7
assert 2*3*5*7*11*13+1 == 30031 == 59*509
assert trial_prime(59) and trial_prime(509)
colors = {0:'#d6ecd7', 2:'#d6e6f3', 3:'#f3dfc3', 5:'#e5daf0', 7:'#f0d6d6'}
fig, ax = plt.subplots(figsize=(9.2, 8), dpi=120, facecolor='white')
ax.set(xlim=(0, 10), ylim=(0, 10), aspect='equal')
ax.axis('off')
for n in range(1, 101):
col = (n-1) % 10
row = 9-(n-1)//10
face = '#eeeeee' if n == 1 else colors[smallest[n]]
ax.add_patch(Rectangle((col+.04,row+.04),.92,.92,
facecolor=face, edgecolor='white', lw=1.5))
ax.text(col+.5,row+.51,str(n),ha='center',va='center',fontsize=13,
fontweight='bold' if n in primes else 'normal',
color='#235e32' if n in primes else '#303840')
if n == 1:
ax.plot([col+.2,col+.8],[row+.2,row+.8],color='#8b8b8b',lw=1)
fig.suptitle('Sieve through 100: remove multiples, retain primes',fontsize=17,y=.975)
handles = [Patch(facecolor=colors[0],label='Prime (25 survivors)'),
Patch(facecolor='#eeeeee',label='1: excluded')]
handles += [Patch(facecolor=colors[p],label=f'First removed by {p}') for p in [2,3,5,7]]
fig.legend(handles=handles,loc='lower center',ncol=3,frameon=False,
bbox_to_anchor=(.5,.015),fontsize=11)
fig.subplots_adjust(top=.9,bottom=.135,left=.08,right=.92)
fig.savefig(Path.cwd()/Path(__file__).with_suffix('.png').name,
facecolor='white',transparent=False)
plt.close(fig)
if __name__ == '__main__':
main()