Railway circuit: Difference between revisions

→‎{{header|Python}}: Reorganized code to be faster and to handle angles other than 30 degrees
(→‎{{header|Python}}: Reorganized code to be faster and to handle angles other than 30 degrees)
Line 1,378:
import numpy as np
from numpy import sin, cos, pi
 
# ANGDIV can't be 2, though
ANGDIV = 12
ANG = 2*pi/ANGDIV
 
def draw_all(sols):
Line 1,387 ⟶ 1,391:
for d in s:
x0, y0 = xend[-1], yend[-1]
a = turn*pi/6ANG
cs, sn = cos(a), sin(a)
ang = a + d*pi/2
cx, cy = x0 + cos(ang), y0 + sin(ang)
 
da = np.linspace(ang, ang + d*pi/6ANG, 510)
xs = cx - cos(da)
ys = cy - sin(da)
Line 1,404 ⟶ 1,408:
ax.set_aspect(1)
 
ls = len(sols)
if ls == 0: return
 
ifw, (lsh := lenmin(sols)(abs(w*2 - h*3) >+ 1:w*h - ls, w, h)
for w, h =in min((abs(w*2, - h*3)(ls + abs(lsw - w*h1), //w, h)
for w, h in range((w1, (ls + w - 1)//w))[1:]
for w in range(1, ls + 1)))[1:]
 
fig, ax = plt.subplots(h, w, squeeze=False)
for a in ax.ravel(): a.set_axis_off()
elif ls == 0:
return
else:
w = h = 1
fig, ax = plt.subplots(h, w)
ax = [[ax]]
 
for i, s in enumerate(sols):
draw_track(ax[i//w][, i%w], s)
 
plt.show()
 
 
def match_up(this, that, equal_lr, seen):
l_lo,if l_hinot =this 0,or 0not that: return
 
r_lo, r_hi = 0, 0
l_nn = len(this[0][-1])
r_nn2 = len(that)n*2
 
l_lo, l_hi, r_lo, r_hi = 0, 0, 0, 0
 
def record(sm):
for _ in range(len(q)n2):
seen[qm] = True
m = (m&1) << (n2 - 1) | if(m l ==>> r:1)
 
axif = [[ax]]equal_lr:
m q ^= q[(1:]<<n2) +- q[:1]
for w_ in range(1, ls + 1))n2)[1:]
seen[m] = record(s)True
m = (m&1) << if(n2 s- in1) seen:| continue(m >> 1)
 
l_n, r_n = len(this), len(that)
tol = 1e-3
 
Line 1,444 ⟶ 1,458:
 
for a in this[l_lo:l_hi]:
m_left = a[-2]<<n
for b in that[r_lo:r_hi]:
if np.abs(a[1]m +:= m_left | b[-2]) <not in tolseen:
yieldif np.abs(a[-1] + b[-12]) < tol:
record(tuple(-a for a in s)m)
record(int(f'{m:b}'[::-1], base=2))
yield(a[-1] + b[-1])
 
l_lo, r_lo = l_hi, r_hi
l_lo = l_hi
 
def track_combo(left, right):
n = (left + right)//2
n1 = left + right - n
 
alphas = np.exp(1j*np.pi/6ANG*np.arange(12ANGDIV))
res = [[] for i in range(right + 1)]
def half_track(m, n):
alphas = np.exp(1j*np.pi/6*np.arange(12))
 
def half_track(m):
turns = tuple(1 - 2*(m>>i & 1) for i in range(n))
rcnt = np.cumsum(turns)%12ANGDIV
asum = np.sum(alphas[rcnt])
want = asum/alphas[rcnt[-1]]
return np.abs(asum), asum, want, m, turns
 
res = [[] for i_ in range(right + 1)]
for i in range(1<<n):
b = i.bit_count()
if b <= right:
res[b].append(half_track(i, n))
 
for v in res: v.sort()
returnif resn1 == n:
l_loreturn =res, l_hires
 
res1 = [[] for _ in range(right + 1)]
for i in range(1<<n1):
b = i.bit_count()
wif = hb <= 1right:
res1[b].append(half_track(i, n1))
 
for v in res: v.sort()
return res, res1
 
def railway(n):
assert(n&1 == 0)
seen = {}
 
def record(s):
for q in [s, tuple(reversed(s))]:
for _ in range(len(q)):
seen[q] = True
q = q[1:] + q[:1]
 
for l in range(n//2, n + 1):
r = n - l
assert(if not l >= r): continue
 
if (l - r)%12ANGDIV == 0:
resres_l, res_r = track_combo(l, r)
 
for i, this in enumerate(resres_l):
if 2*i < r: continue
that = resres_r[r - i]
for s in match_up(this, that, l == r, seen):
if s in seen: continue
yield s
record(s)
 
if l == r:
record(tuple(-a for a in s))
 
 
sols = []
Line 1,507 ⟶ 1,521:
sols.append(s)
 
# draw at most 40 solutions
draw_all(sols[:40])</lang>