In [1]:
    
# Import modules
import time
import math
import numpy as np
import scipy
import matplotlib.pyplot as plt
    
In [2]:
    
def linear_congruential_generator(x, a, b, m):
    x = (a * x + b) % m
    u = x / m
    return u, x, a, b, m
def stdrand(x):
    return linear_congruential_generator(x, pow(7, 5), 0, pow(2, 31) - 1)[:2]
def halton(p, n):
    b = np.zeros(math.ceil(math.log(n + 1) / math.log(p)))
    u = np.zeros(n)
    for j in range(n):
        i = 0
        b[0] = b[0] + 1
        while b[i] > p - 1 + np.finfo(float).eps:
            b[i] = 0
            i += 1
            b[i] += 1 
        u[j] = 0
        for k in range(1, b.size + 1):
            u[j] = u[j] + b[k-1] * pow(p, -k)
    return u
    
In [3]:
    
def monte_carlo_process_std(toss):
    x = time.time()
    hit = 0
    for i in range(toss):
        u1, x = stdrand(x)
        u2, x = stdrand(x)
        if pow(u1, 2) + pow(u2, 2) < 1.0:
            hit += 1
    return hit * 4.0 / toss
pi = monte_carlo_process_std(2000000)
print('pi = %.10f, err = %.10f' %(pi, abs(pi - np.pi)))
    
    
In [4]:
    
def monte_carlo_process_customized(toss):
    x0 = time.time()
    args = (x0, 13, 0, 31)
    hit = 0
    for i in range(toss):
        u1, *args = linear_congruential_generator(*args)
        u2, *args = linear_congruential_generator(*args)
        if pow(u1, 2) + pow(u2, 2) < 1.0:
            hit += 1
    return hit * 4.0 / toss
pi = monte_carlo_process_customized(2000000)
print('pi = %.10f, err = %.10f' %(pi, abs(pi - np.pi)))
    
    
In [5]:
    
def monte_carlo_process_quasi(toss):
    hit = 0
    px = halton(2, toss)
    py = halton(3, toss)
    for i in range(toss):
        u1 = px[i]
        u2 = py[i]
        if pow(u1, 2) + pow(u2, 2) < 1.0:
            hit += 1
    return hit * 4.0 / toss
pi = monte_carlo_process_quasi(2000000)
print('pi = %.10f, err = %.10f' %(pi, abs(pi - np.pi)))