def f_a_v2(m, n):
    M = numpy.ones((m, n))

    x = numpy.arange(m).reshape((m, 1))
    y = numpy.arange(n).reshape((1, n))

    M = M * x
    M = M * y

    return M

Creating an all-ones array

M = numpy.ones((m, n))

Generating ranges for row and column indices

x = numpy.arange(m).reshape((m, 1))
y = numpy.arange(n).reshape((1, n))
  1. numpy.arange(m)

    Creates a numpy array of integers from 0 to m - 1.

    Example: if m = 3, numpy.arange(3) is [0, 1, 2].

  2. .reshape((m, 1))

    Reshapes the 1D array into a 2D array with m rows and 1 column.

    For m = 3, x becomes:

    $$ \begin{bmatrix} 0\\ 1\\ 2 \end{bmatrix} $$

    So, x is shape (3, 1).

  3. numpy.arange(n).reshape((1, n))

    $$ [0\ 1\ 2\ 3] $$

    So, y has shape (1, 4).


Elementwise multiplication with broadcasting

M = M * x
M = M * y

Hence, the result is a 2D array of shape (m, n) whose element at (i, j) is i * j.


Final return