Fill in 2D Numpy array recursively, and compute execution times_ M is a 2D array with dimensions N x N where N is an integer. It is defined as follows: The first column is just the set of numbers 0, 1, ..., N-1. The first row of elements is zero. The rest of the array is computed via recursive formula in which the element is equal to the sum of (a) the one just above and also the element above and to the left. The figure below illustrates this: To compute the value of the element in the red oval, we add the two elements in the blue box:
0 0 0 0 0 0 0
0 3 2 0 0
1 3 3 0 0
4 6 1 0
5 10 10 5 1 0
(1) For any N, define function F that would implement this recursive process, filling in M, by using two for loops to loop over columns and rows. Print your resulting matrix M1 for N = 8. [4] Define another function F2 that achieves the same goal but uses only one for loop to go over rows of M2. That is, for each row, start from the left of it, and fill the row in by going to the right end of the row. Print your resulting matrix M2. [4] Visualize matrix M1 for some value of N, e.g. N = 10, with imshow. [3] In the rest of the task, we measure code efficiency of the two methods as a function of the number of elements in the matrix: Define an array N = [2, 12]. For each value of N, find the matrices M1, M2 (do not print them as they become large), and the time it took your code to do so, t1(N) and t2(N). Print your tN in a clear fashion: [3] (5) Plot the resulting execution times t1 and t2 versus N. Try to make your plot as clear as possible. Analyze your results, and make quantitative conclusions about how large t1 and t2 would be for very large N, e.g. N = 105. [11]