Skip to content

Instantly share code, notes, and snippets.

@psiborg
Last active October 14, 2024 03:56
Show Gist options
  • Select an option

  • Save psiborg/1c25f74dfbf4b2087de5fa3245f290ef to your computer and use it in GitHub Desktop.

Select an option

Save psiborg/1c25f74dfbf4b2087de5fa3245f290ef to your computer and use it in GitHub Desktop.
Calculate Pi in parallel using Gauss-Legendre, Chudnovsky, and Bailey-Borwein-Plouffe (BBP) algorithms
#!/usr/bin/env python3
# ./pi_calc.py --digits 5000
import decimal
from decimal import Decimal
from math import factorial
import multiprocessing
import time
import argparse
# Set precision for Decimal calculations
decimal.getcontext().prec = 1020 # Adjust for extra precision
# Function to calculate Pi using Gauss-Legendre Algorithm
def gauss_legendre(precision):
decimal.getcontext().prec = precision + 2 # extra digits for rounding
a = Decimal(1)
b = Decimal(1) / Decimal(2).sqrt()
t = Decimal(0.25)
p = Decimal(1)
for _ in range(int(precision**0.5)):
a_next = (a + b) / 2
b = (a * b).sqrt()
t -= p * (a - a_next) ** 2
a = a_next
p *= 2
pi = (a + b) ** 2 / (4 * t)
return +pi # unary plus applies the precision
# Function to calculate Pi using Chudnovsky Algorithm
def chudnovsky(precision):
decimal.getcontext().prec = precision + 2 # extra digits for rounding
C = 426880 * Decimal(10005).sqrt()
K = 6
M = 1
X = 1
L = 13591409
S = L
for k in range(1, precision):
M = (K ** 3 - 16 * K) * M // k ** 3
L += 545140134
X *= -262537412640768000
S += Decimal(M * L) / X
K += 12
pi = C / S
return +pi # unary plus applies the precision
# Function to calculate Pi using Bailey-Borwein-Plouffe (BBP) Algorithm
def bbp(precision):
decimal.getcontext().prec = precision + 2 # extra digits for rounding
pi = Decimal(0)
for k in range(precision):
pi += (Decimal(1) / (16 ** k)) * (
Decimal(4) / (8 * k + 1) -
Decimal(2) / (8 * k + 4) -
Decimal(1) / (8 * k + 5) -
Decimal(1) / (8 * k + 6)
)
return +pi # unary plus applies the precision
# Wrapper for multiprocessing
def parallel_calculate(algorithm, precision, label):
start = time.time()
pi = algorithm(precision)
duration = time.time() - start
return label, pi, duration
def display_result(result):
label, pi, duration = result
print(f"\n{label:<15} completed in {duration:<.4f} seconds.")
print(f"First 100 digits: {str(pi)[:100]}...")
# Function to find the first mismatch position between two strings
def find_first_mismatch(str1, str2):
mismatch_index = None
for i in range(len(str1)):
if str1[i] != str2[i]:
mismatch_index = i
break
return mismatch_index
def main():
# Argument parsing for command line arguments
parser = argparse.ArgumentParser(description="Calculate Pi to a specified number of digits.")
parser.add_argument('-d', '--digits', type=int, default=1000, help="Number of digits of Pi to calculate (default: 1000)")
args = parser.parse_args()
precision = args.digits
print(f"Calculating Pi to {precision} digits...\n")
algorithms = [
(gauss_legendre, precision, "Gauss-Legendre"),
(chudnovsky, precision, "Chudnovsky"),
(bbp, precision, "BBP")
]
pool = multiprocessing.Pool(processes=3)
# Trigger each calculation in parallel with real-time updates
results = []
for algorithm, prec, label in algorithms:
async_result = pool.apply_async(parallel_calculate, (algorithm, prec, label), callback=display_result)
results.append(async_result)
# Ensure all results are gathered and displayed
for result in results:
result.wait()
pool.close()
pool.join()
# Final Summary using BBP as the reference for comparison
print("\nSummary of Results:")
print(f"{'Algorithm':<15} {'Time (s)':<10} {'Match to BBP':<12} {'First Mismatch (if any)'}")
print("-" * 60)
# Use BBP as the base for comparison
bbp_result = [res for res in results if res.get()[0] == "BBP"][0].get()[1]
bbp_str = str(bbp_result)
for result in results:
label, pi, duration = result.get()
if label != "BBP":
pi_str = str(pi)
if pi_str == bbp_str:
match = "Yes"
mismatch_info = "N/A"
else:
match = "No"
mismatch_index = find_first_mismatch(pi_str, bbp_str)
mismatch_info = f"Position {mismatch_index}" if mismatch_index is not None else "Unknown"
else:
match = "N/A" # BBP is the reference
mismatch_info = "N/A"
print(f"{label:<15} {duration:<10.4f} {match:<12} {mismatch_info}")
if __name__ == "__main__":
main()
Calculating Pi to 1000 digits...
Gauss-Legendre completed in 0.5167 seconds.
First 100 digits: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825...
Chudnovsky completed in 0.0318 seconds.
First 100 digits: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825...
BBP completed in 0.1429 seconds.
First 100 digits: 3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825...
Summary of Results:
Algorithm Time (s) Match to BBP First Mismatch (if any)
------------------------------------------------------------
Gauss-Legendre 0.5167 Yes N/A
Chudnovsky 0.0318 Yes N/A
BBP 0.1429 N/A N/A
-OR-
Summary of Results:
Algorithm Time (s) Match to BBP First Mismatch (if any)
------------------------------------------------------------
Gauss-Legendre 0.5167 No Position 948
Chudnovsky 0.0318 Yes N/A
BBP 0.1429 N/A N/A
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment