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()