Skip to content

Instantly share code, notes, and snippets.

@serif
Last active August 7, 2021 04:48
Show Gist options
  • Save serif/2e38325f4987e0c9c62d9ce4c6c8df9b to your computer and use it in GitHub Desktop.
Save serif/2e38325f4987e0c9c62d9ce4c6c8df9b to your computer and use it in GitHub Desktop.
Rational approximation of irrational numbers
#!/usr/bin/env python3
# Find rational approximations of irrational numbers
import math
from typing import List
from timeit import timeit
class Ratio:
""" a/q approximation of an irrational """
def __init__(self, a: int, q: int, t: float) -> None:
self.a: int = a # numerator
self.q: int = q # denominator
self.t: float = t # target
self.v: float = a / q # value
self.d: float = math.fabs(self.t - self.v) # delta
def __str__(self) -> str:
rational: str = f'{self.a}/{self.q}'.rjust(14)
delta: str = f' ∆ {self.d:.14f}'
value: str = f' {self.v:.14f}'
# efficiency: digits of fraction vs decimal precision
d_in: int = len(str(self.a)) + len(str(self.q))
d_out: float = int(f'{self.d:e}'.split('-')[1])
d_out += (1 - float(int(f'{self.d:e}'[0])) * 0.1)
s_in: str = str(d_in).rjust(2)
s_out: str = f'{d_out:2.1f}'.ljust(4)
gain: float = d_out - d_in
s_gain: str = ' ' if gain >= 0 else ''
s_gain += f"{gain:2.1f}{'+' * int(math.ceil(5 * gain))}"
efficiency = f' {s_in}:{s_out} {s_gain}'
return rational + delta + value + efficiency
def bar(cur: int = 1,
fin: int = 1_000_000,
width: int = 30,
clear: bool = False) -> None:
""" Progress bar """
if clear:
print(' ' * (width + len(f' {fin:,}')))
return
lhs: int = int(round((cur / fin * width)))
rhs: int = width - lhs
stat: str = f' {cur:,}'
print(f'{"■" * lhs}{"□" * rhs}{stat}', end='\r')
def search(target: float) -> None:
""" Find a/q approximations of an irrational where ∆ < 1/q² """
best_delta: float = 1.0
print(f'\nTarget: {target}\n')
header: str = ' Rational Delta vs Target'
header += ' Decimal Value in:out +/-'
print(header)
for q in range(1, 1_000_000 + 1):
if q % 10_000 == 0:
bar(q)
tolerance = 1 / q ** 2
floor: int = math.floor(q * target)
for a in [floor, floor + 1]:
r = Ratio(a, q, target)
if r.d <= tolerance and r.d < best_delta:
best_delta = r.d
print(r)
bar(q)
bar(clear=True)
def main() -> None:
test_count: int = 1
# Irrational numbers
pi: float = math.pi
tau: float = math.tau
e: float = math.e
root2: float = math.sqrt(2)
root5: float = math.sqrt(5)
splat: float = pi + tau + e + root2 + root5
print('splat = pi + tau + e + root2 + root5 =', splat)
# Put an irrational number here ↓
elapse: float = timeit(lambda: search(pi), number=test_count)
print(f'Total elapse: {elapse}\nAverage time: {elapse / test_count}')
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment