From 77f3888b71a73fb3d65820940ae18da850b2a1de Mon Sep 17 00:00:00 2001 From: Kenneth P <41343159+ken437@users.noreply.github.com> Date: Mon, 18 May 2020 16:54:08 -0400 Subject: [PATCH] Pi digit extraction algorithm (#1996) * added pi digit extraction formula * updating DIRECTORY.md * fixed typo in a comment * updated bbp_formula.py * Update maths/bbp_formula.py Co-authored-by: Christian Clauss * Update maths/bbp_formula.py Co-authored-by: Christian Clauss * Update bbp_formula.py * Update and rename bbp_formula.py to bailey_borwein_plouffe.py * updating DIRECTORY.md * calculate * "".join(bailey_borwein_plouffe(i) for i in range(1, 12)) * Update bailey_borwein_plouffe.py * Update bailey_borwein_plouffe.py Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + maths/bailey_borwein_plouffe.py | 87 +++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 maths/bailey_borwein_plouffe.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 711d0a9e9..bef7bca86 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -300,6 +300,7 @@ * [Average Mean](https://github.com/TheAlgorithms/Python/blob/master/maths/average_mean.py) * [Average Median](https://github.com/TheAlgorithms/Python/blob/master/maths/average_median.py) * [Average Mode](https://github.com/TheAlgorithms/Python/blob/master/maths/average_mode.py) + * [Bailey Borwein Plouffe](https://github.com/TheAlgorithms/Python/blob/master/maths/bailey_borwein_plouffe.py) * [Basic Maths](https://github.com/TheAlgorithms/Python/blob/master/maths/basic_maths.py) * [Binary Exp Mod](https://github.com/TheAlgorithms/Python/blob/master/maths/binary_exp_mod.py) * [Binary Exponentiation](https://github.com/TheAlgorithms/Python/blob/master/maths/binary_exponentiation.py) diff --git a/maths/bailey_borwein_plouffe.py b/maths/bailey_borwein_plouffe.py new file mode 100644 index 000000000..783466886 --- /dev/null +++ b/maths/bailey_borwein_plouffe.py @@ -0,0 +1,87 @@ +def bailey_borwein_plouffe(digit_position: int, precision: int = 1000) -> str: + """ + Implement a popular pi-digit-extraction algorithm known as the + Bailey-Borwein-Plouffe (BBP) formula to calculate the nth hex digit of pi. + Wikipedia page: + https://en.wikipedia.org/wiki/Bailey%E2%80%93Borwein%E2%80%93Plouffe_formula + @param digit_position: a positive integer representing the position of the digit to extract. + The digit immediately after the decimal point is located at position 1. + @param precision: number of terms in the second summation to calculate. + A higher number reduces the chance of an error but increases the runtime. + @return: a hexadecimal digit representing the digit at the nth position + in pi's decimal expansion. + + >>> "".join(bailey_borwein_plouffe(i) for i in range(1, 11)) + '243f6a8885' + >>> bailey_borwein_plouffe(5, 10000) + '6' + >>> bailey_borwein_plouffe(-10) + Traceback (most recent call last): + ... + ValueError: Digit position must be a positive integer + >>> bailey_borwein_plouffe(0) + Traceback (most recent call last): + ... + ValueError: Digit position must be a positive integer + >>> bailey_borwein_plouffe(1.7) + Traceback (most recent call last): + ... + ValueError: Digit position must be a positive integer + >>> bailey_borwein_plouffe(2, -10) + Traceback (most recent call last): + ... + ValueError: Precision must be a nonnegative integer + >>> bailey_borwein_plouffe(2, 1.6) + Traceback (most recent call last): + ... + ValueError: Precision must be a nonnegative integer + """ + if (not isinstance(digit_position, int)) or (digit_position <= 0): + raise ValueError("Digit position must be a positive integer") + elif (not isinstance(precision, int)) or (precision < 0): + raise ValueError("Please input a nonnegative integer for the precision") + + # compute an approximation of (16 ** (n - 1)) * pi whose fractional part is mostly accurate + sum_result = ( + 4 * _subsum(digit_position, 1, precision) + - 2 * _subsum(digit_position, 4, precision) + - _subsum(digit_position, 5, precision) + - _subsum(digit_position, 6, precision) + ) + + # return the first hex digit of the fractional part of the result + return hex(int((sum_result % 1) * 16))[2:] + + +def _subsum( + digit_pos_to_extract: int, denominator_addend: int, precision: int +) -> float: + # only care about first digit of fractional part; don't need decimal + """ + Private helper function to implement the summation + functionality. + @param digit_pos_to_extract: digit position to extract + @param denominator_addend: added to denominator of fractions in the formula + @param precision: same as precision in main function + @return: floating-point number whose integer part is not important + """ + sum = 0.0 + for sum_index in range(digit_pos_to_extract + precision): + denominator = 8 * sum_index + denominator_addend + exponential_term = 0.0 + if sum_index < digit_pos_to_extract: + # if the exponential term is an integer and we mod it by the denominator before + # dividing, only the integer part of the sum will change; the fractional part will not + exponential_term = pow( + 16, digit_pos_to_extract - 1 - sum_index, denominator + ) + else: + exponential_term = pow(16, digit_pos_to_extract - 1 - sum_index) + sum += exponential_term / denominator + return sum + + +if __name__ == "__main__": + import doctest + + doctest.testmod()