The itertools.permutations method is perfect for what you are trying to achieve.
First generate a list of numbers/digits that can be used.
digits = list(range(10 ** (n - 1), 10 ** n))
Then multiply that list by p in order to get p copies of each number.
digits *= p
Finally, use itertools to generate a list of all the permutations (or combinations) possible with a length of p.
for values in itertools.permutations(digits, p):
output.append([values, np.prod(values)])
The finished function is shown below:
import itertools
import numpy as np
def product(n, p=2):
output = []
# Generate a list containing every possible digit.
digits = list(range(10 ** (n - 1), 10 ** n))
# Multiply the list by <p> so that we have <p> copies of each digit.
digits *= p
# Iterate over each possible permutation of the digits.
for values in itertools.permutations(digits, p):
output.append([values, np.prod(values)])
return output
Alternatively, using numpy and sympy you can make the function much faster (albeit at the cost of having the output be an array rather than list).
Here is the code for a numpy + sympy implementation that is faster (citation needed).
import numpy as np
from sympy.utilities.iterables import multiset_permutations as perm
def product2(n, p=2):
digits = np.arange(10 ** (n - 1), 10 ** n)
digits = np.repeat(digits, p)
permutations = list(perm(digits, size=p))
output = np.zeros((len(permutations), n + 1))
for i in range(len(permutations)):
for j in range(n):
output[i][j] = permutations[i][j]
output[i][-1] = np.prod(permutations[i])
return output
productList.append(...)in the 5th line?