DunningKruger

Controlling pi digit generation

Dec 1st, 2025
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.53 KB | None | 0 0
  1. from mpmath import mp
  2.  
  3. def build_coeffs(m, prec):
  4.     """
  5.    Build coefficients c_r = r! / (2r+1)!! using the recurrence
  6.        c_0 = 1
  7.        c_r = c_{r-1} * r / (2r+1)
  8.    at precision 'prec'.
  9.    """
  10.     old_dps = mp.dps
  11.     mp.dps = prec
  12.  
  13.     coeffs = [mp.mpf('1')]
  14.     for r in range(1, m):
  15.         coeffs.append(coeffs[-1] * r / (2*r + 1))
  16.  
  17.     mp.dps = old_dps
  18.     return coeffs
  19.  
  20. def step(x, coeffs):
  21.     """
  22.    One iteration of the recurrence:
  23.        x_{n+1} = x_n + sin(x_n) * sum_{r=0}^{m-1} c_r * (cos x_n + 1)^r
  24.    evaluated via Horner's rule in t = cos(x) + 1.
  25.    """
  26.     c, s = mp.cos_sin(x)
  27.     t = c + 1
  28.  
  29.     total = coeffs[-1]
  30.     for a in reversed(coeffs[:-1]):
  31.         total = total * t + a
  32.  
  33.     return x + s * total
  34.  
  35. def matching_digits(x, dps=None):
  36.     """
  37.    Count how many decimal digits of x match pi (ignoring the '3.' prefix).
  38.    """
  39.     if dps is None:
  40.         dps = mp.dps
  41.  
  42.     s_x  = mp.nstr(x, dps)
  43.     s_pi = mp.nstr(mp.pi, dps)
  44.  
  45.     # Compare characters from the start
  46.     L = min(len(s_x), len(s_pi))
  47.     i = 0
  48.     while i < L and s_x[i] == s_pi[i]:
  49.         i += 1
  50.  
  51.     # Subtract 2 to ignore '3.' (the integer part and decimal point)
  52.     return max(0, i - 2)
  53.  
  54. def run_pi_from_x0(m, x0_str, num_steps, safety=30):
  55.     """
  56.    Run the pi recurrence starting from x0 (as a string) and parameter m.
  57.    The convergence order is 2m+1.
  58.  
  59.    We use the error model
  60.        N_{n+1} ≈ (2m+1) N_n + log10((2m+1)! / (m!)^2)
  61.    to:
  62.      - estimate how many digits N_n we get after each step,
  63.      - choose mp.dps for each step,
  64.      - choose how many digits to use when building the coefficients.
  65.    """
  66.     # --- Initial estimate for digits in x0 ---
  67.     mp.dps = 80
  68.     x = mp.mpf(x0_str)
  69.     N0 = -mp.log10(abs(x - mp.pi))      # N_0 ≈ -log10|x0 - pi|
  70.  
  71.     print(f"Initial x0 = {x0_str}")
  72.     #print(f"Estimated N0 ≈ {float(N0):.3f} correct digits")
  73.  
  74.     # --- Digit-growth constants ---
  75.     a = 2*m + 1
  76.     B = mp.log10(mp.factorial(2*m + 1)) - 2*mp.log10(mp.factorial(m))
  77.     #print(f"Convergence order = {a},  B ≈ {float(B):.6f}")
  78.     print(f"Convergence order = {a}")
  79.  
  80.     # --- Predict max digits over all steps (to size coeff precision) ---
  81.     N_tmp = N0
  82.     max_N = N0
  83.     for _ in range(num_steps):
  84.         N_tmp = a * N_tmp + B
  85.         if N_tmp > max_N:
  86.             max_N = N_tmp
  87.  
  88.     coeff_prec = int(max_N + safety)
  89.     if coeff_prec < 80:
  90.         coeff_prec = 80
  91.  
  92.     print(f"Building coefficients at precision = {coeff_prec} dps")
  93.     coeffs = build_coeffs(m, coeff_prec)
  94.  
  95.     # --- Main iteration loop ---
  96.     N = N0
  97.     for step_index in range(1, num_steps + 1):
  98.         # Predicted digits after this step
  99.         N = a * N + B
  100.  
  101.         # Working precision for this step
  102.         dps_for_step = int(mp.ceil(N + safety))
  103.         if dps_for_step < 50:
  104.             dps_for_step = 50
  105.         if dps_for_step > coeff_prec:
  106.             dps_for_step = coeff_prec
  107.  
  108.         mp.dps = dps_for_step
  109.         x = step(x, coeffs)
  110.  
  111.         # Actual digits that match pi at this precision
  112.         actual = matching_digits(x, dps_for_step)
  113.  
  114.         print(
  115.             f"Step {step_index}: "
  116.             f"dps={dps_for_step}, "
  117.             f"predicted ~{int(N)} digits, "
  118.             f"actual {actual} matching digits"
  119.         )
  120.  
  121.     return x
  122.  
  123. # Example usage:
  124. m = 31          # convergence order = 2m+1 = 63
  125. x0 = "3.0"      # starting approximation
  126. num_steps = 4   # how many iterations to run
  127. pi_approx = run_pi_from_x0(m, x0, num_steps)
  128.  
Advertisement
Add Comment
Please, Sign In to add comment