"""Compute statistics on the digits of pi. This uses precomputed digits of pi from the website of Professor Yasumasa Kanada at the University of Tokoyo: http://www.super-computing.org/ Currently, there are only functions to read the .txt (non-compressed, non-binary) files, but adding support for compression and binary files would be straightforward. This focuses on computing the number of times that all 1, 2, n digits sequences occur in the digits of pi. If the digits of pi are truly random, these frequencies should be equal. """ # Import statements from __future__ import division, with_statement import numpy as np from matplotlib import pyplot as plt try : #python2 from urllib import urlretrieve except ImportError : #python3 from urllib.request import urlretrieve # Top-level functions def fetch_pi_file(filename): """This will download a segment of pi from super-computing.org if the file is not already present. """ import os, urllib ftpdir="ftp://pi.super-computing.org/.2/pi200m/" if os.path.exists(filename): # we already have it return else: # download it urlretrieve(ftpdir+filename,filename) def compute_one_digit_freqs(filename): """ Read digits of pi from a file and compute the 1 digit frequencies. """ d = txt_file_to_digits(filename) freqs = one_digit_freqs(d) return freqs def compute_two_digit_freqs(filename): """ Read digits of pi from a file and compute the 2 digit frequencies. """ d = txt_file_to_digits(filename) freqs = two_digit_freqs(d) return freqs def reduce_freqs(freqlist): """ Add up a list of freq counts to get the total counts. """ allfreqs = np.zeros_like(freqlist[0]) for f in freqlist: allfreqs += f return allfreqs def compute_n_digit_freqs(filename, n): """ Read digits of pi from a file and compute the n digit frequencies. """ d = txt_file_to_digits(filename) freqs = n_digit_freqs(d, n) return freqs # Read digits from a txt file def txt_file_to_digits(filename, the_type=str): """ Yield the digits of pi read from a .txt file. """ with open(filename, 'r') as f: for line in f.readlines(): for c in line: if c != '\n' and c!= ' ': yield the_type(c) # Actual counting functions def one_digit_freqs(digits, normalize=False): """ Consume digits of pi and compute 1 digit freq. counts. """ freqs = np.zeros(10, dtype='i4') for d in digits: freqs[int(d)] += 1 if normalize: freqs = freqs/freqs.sum() return freqs def two_digit_freqs(digits, normalize=False): """ Consume digits of pi and compute 2 digits freq. counts. """ freqs = np.zeros(100, dtype='i4') last = next(digits) this = next(digits) for d in digits: index = int(last + this) freqs[index] += 1 last = this this = d if normalize: freqs = freqs/freqs.sum() return freqs def n_digit_freqs(digits, n, normalize=False): """ Consume digits of pi and compute n digits freq. counts. This should only be used for 1-6 digits. """ freqs = np.zeros(pow(10,n), dtype='i4') current = np.zeros(n, dtype=int) for i in range(n): current[i] = next(digits) for d in digits: index = int(''.join(map(str, current))) freqs[index] += 1 current[0:-1] = current[1:] current[-1] = d if normalize: freqs = freqs/freqs.sum() return freqs # Plotting functions def plot_two_digit_freqs(f2): """ Plot two digits frequency counts using matplotlib. """ f2_copy = f2.copy() f2_copy.shape = (10,10) ax = plt.matshow(f2_copy) plt.colorbar() for i in range(10): for j in range(10): plt.text(i-0.2, j+0.2, str(j)+str(i)) plt.ylabel('First digit') plt.xlabel('Second digit') return ax def plot_one_digit_freqs(f1): """ Plot one digit frequency counts using matplotlib. """ ax = plt.plot(f1,'bo-') plt.title('Single digit counts in pi') plt.xlabel('Digit') plt.ylabel('Count') return ax