If the maximum values aren't too large to store the complete set of possibilities in memory (and it won't take forever to generate them), random.sample
and itertools.product
can be used effectively here:
import itertools
import random
def make_random_cities(num_cities, max_x, max_y):
return random.sample(list(itertools.product(range(max_x+1), range(max_y+1))), num_cities)
If the product
of the inputs gets too large though, you could easily exceed main memory; in that case, your approach of looping until you get sufficient unique results is probably the best approach.
You could do samples of each range
independently and then combine them together, but that would add uniqueness constraints to each axis, which I'm guessing you don't want.
For this specific case (unique numbers following a predictable pattern), you could use a trick to make this memory friendly while still avoiding the issue of arbitrarily long loops. Instead of taking the product
of two range
s, you'd generate a single range
(or in Py2, xrange
) that encodes both unique values from the product
in a single value:
def make_random_cities(num_cities, max_x, max_y):
max_xy = (max_x+1) * (max_y+1)
xys = random.sample(range(max_xy), num_cities)
return [divmod(xy, max_y+1) for xy in xys]
This means you have no large intermediate data to store (because Py3 range
/Py2 xrange
are "virtual" sequences, with storage requirements unrelated to the range of values they represent, and random.sample
produces samples without needing to read all the values of the underlying sequence).