diff --git a/MonteCarloIntegration/Python/monte_carlo_integration.py b/MonteCarloIntegration/Python/monte_carlo_integration.py index b2a0d07787ced33a1cb721af3d486b21fe4b5d2f..1d9a46d1cfeaaeca812e84d6f28faeeb21f9441a 100644 --- a/MonteCarloIntegration/Python/monte_carlo_integration.py +++ b/MonteCarloIntegration/Python/monte_carlo_integration.py @@ -2,11 +2,31 @@ import random import sys +import time from typing import (Callable, Tuple) +import numpy as np + Point = Tuple[float, float] +def simple_timer(function): + """ + This is a simple timer meant to be used as a decorator. + """ + + def wrapper(): + start_time = time.perf_counter_ns() + + function() + + end_time = time.perf_counter_ns() + time_in_sec = (end_time - start_time) / 10**9 + + print(f"Total Time: {time_in_sec:>4.2f}") + + return wrapper + def generate_random_points(f: Callable, lower_limit: float, @@ -90,7 +110,7 @@ def not_so_naive_main(): print(f"{integral_result:16.8f}") - +@simple_timer def main_without_a_table_flip(): """ This main demonstrates the impact of the number of points on Monte Carlo @@ -104,12 +124,13 @@ def main_without_a_table_flip(): print("| {:^16} | {:^20} |".format("# Points", "Est. f(x)")) max_num_points = 2 ** max_magnitude - point_sequence = list(generate_random_points(math_f, limit_a, limit_b, max_num_points)) + point_sequence = generate_random_points(math_f, limit_a, limit_b, max_num_points) + all_y_values = list((y for x, y in point_sequence)) for magnitude in range(0, max_magnitude + 1): num_points = 2 ** magnitude - f_of_x_values = (y for x, y in point_sequence[:num_points]) + f_of_x_values = all_y_values[:num_points] integral_result = ((limit_b - limit_a) / float(num_points) * @@ -118,7 +139,41 @@ def main_without_a_table_flip(): print(f"| {num_points:>16} | {integral_result:^20.8f} |") +@simple_timer +def main_with_numpy(): + """ + This main demonstrates the impact of the number of points on Monte Carlo + integration + """ + + _, limit_a, limit_b, max_magnitude = __parse_cmd_line_args() + + math_f = lambda x: x**2 + + print("| {:^16} | {:^20} |".format("# Points", "Est. f(x)")) + + max_num_points = 2 ** max_magnitude + all_x_values = np.random.uniform(low=limit_a, high=limit_b, size=max_num_points) + all_y_values = math_f(all_x_values) + + for magnitude in range(0, max_magnitude + 1): + num_points = 2 ** magnitude + + f_of_x_values = all_y_values[:num_points] + + integral_result = ((limit_b - limit_a) / + float(num_points) * + sum(f_of_x_values)) + + # integral_result = ((limit_b - limit_a) / + # float(num_points) * + # f_of_x_values.sum()) + + print(f"| {num_points:>16} | {integral_result:^20.8f} |") + + if __name__ == "__main__": # naive_main() # not_so_naive_main() main_without_a_table_flip() + # main_with_numpy()