In your example, 1 and 2 are twice as likely as any other number, because of the 22 possible results of n%22, 3 through 20 all only have one result that yields them, but 1 gets generated by 1 and 21, and 2 gets generated by 2 and 22. You could adapt your algorithm by adding "if the result is greater than the range of values you're picking from (i.e. 21 or 22 in your case), try again with a new number."
>>> import collections, random, pprint
>>> pprint.pprint(
sorted(
list(
collections.Counter(
[
((random.randint(1,10000000) % 22) % 20)
for x in range(10000000)
]
).items()
)
)
)
[(0, 908920),
(1, 908264),
(2, 454167),
(3, 456019),
(4, 454551),
(5, 455183),
(6, 454127),
(7, 454308),
(8, 454939),
(9, 454602),
(10, 454963),
(11, 453117),
(12, 454046),
(13, 453812),
(14, 456243),
(15, 455025),
(16, 454101),
(17, 455072),
(18, 454409),
(19, 454132)]