Creating a list of random numbers without duplicates in python - python

so what I am trying to do is create a list of 5 numbers for the game mastermind, and I would like to eliminate all duplicates! The issue is that the code sometimes creates a list with 3 numbers, or 4, or sometimes 5, it seems to be random.
I should also mention we are not allowed to be usin grandom.sample, or random.shuffle
import random
def generatePassword() :
lis = []
for i in range(5) :
#This checks to see if there are duplicate numbers
r = random.randint(1,9)
if r not in lis :
lis.append(r)
i+=1
return lis
def main() :
print(generatePassword())
main()

Use numpy.random.permutation if you are looking for method that works and is faster:
import numpy as np
your_list = list(np.random.permutation(np.arange(0,10))[:5])
>>> your_list
[6, 9, 0, 1, 4]
Alternatively, you can use np.random.choice with replace=False:
your_list = list(np.random.choice(np.arange(0,10), 5, replace=False)

Try using a while loop with a condition that checks for the length of lis
while len(lis) < 5:
instead of your for loop

The function random.sample does what you want:
import random
def generatePassword():
numbers = range(0, 9)
return random.sample(numbers, 5)
def main() :
print(generatePassword())
main()

I do not recommend the solution in this answer - the best option in the standard library is probably random.sample, and there may be more efficient methods using numpy. Both of these options are suggested in other answers.
This method uses random.shuffle to shuffle a list of digits, then selects the first five. This avoids the issue of a theoretically unbounded loop (while len(nums) < 5:), but does not scale well when the range of numbers to choose from (here, 1 to 9) is significantly larger than how many numbers are needed (here, 5).
import random
population = list(range(1, 10))
random.shuffle(population)
print(population[:5])

You don't want to add random, unique integers 5 times; you want to add random, unique integers until your list contains 5 elements. This'll do it:
import random
def generatePassword() :
lis = []
while len(lis) < 5:
#This checks to see if there are duplicate numbers
r = random.randint(1,9)
if r not in lis :
lis.append(r)
return lis

So your problem:
It won't add the same number twice. But since you use a for i in range(5): it will only repeat 5 times, regardless of if it added a unique number or not.
You need to measure the length of the list, so it will always add 5 random, unique numbers to the list.
You have the code mostly right, but all you need to do is replace:
for i in range(5): with: while len(lis) < 5:
Make sure to delete the i += 1 though. It will cause an error if you don't.
Here's the code:
import random
def generatePassword() :
lis = []
while len(lis) < 5:
#This checks to see if there are duplicate numbers
r = random.randint(1,9)
if r not in lis :
lis.append(r)
return lis
def main() :
print(generatePassword())
main()

Related

Range(5) is returning 4 or 5 random integers

I've created a program to generate 5 random integers from 1-10 and add them to an empty set. For some reason, when I run the program, it will sometimes return 4 integers, and other times 5. What is happening here?
import random
set1 = set()
for x in range(5):
integer = random.randint(1,10)
set1.add(integer)
print(set1)
You're using a set, sets can't contain duplicates, if the same number is generated twice it will only occur in the set once, theoretically its possible your output would only have 1 number in it (if the same number was added 5 times).
You should use a list instead:
import random
output = []
for x in range(5):
integer = random.randint(1,10)
output += [integer]
print(output)
The easiest way to generate n random unique numbers is to use random.sample:
>>> import random
>>> set(random.sample(range(1, 11), 5))
set([8, 9, 5, 6, 10])
Note that you should use range(1, 11) if you also want to include the number 10.
Python sets will not show duplication. A simple way to fix your script is to use a list instead of a set. One thing to note however, is if you are going to want to use all the numbers together like 12345, this won't do the trick. The following script will return a list as [1, 2, 3, 4, 5].
list1 = [] # Make an empty list
for x in range(5):
# randomly choose a number from 1 - 10 and append it to our list 5 times
integer = random.randint(1,10)
list1.append(integer)
print(list1) # print the list

Generating n random numbers between i and j python

I want to create a random number using n numbers which are between i and j. For instance, for n=10 and i=1 and j=5, such an output is expected: 2414243211. I did it in R using this code:
paste(floor(runif(10,1,5)),collapse="") #runif create 10 random number between 1 and 5 and floor make them as integer and finally paste makes them as a sequence of numbers instead of array.
I want to do the same in Python. I found random.uniform but it generates 1 number and I don't want to use loops.
import random
import math
math.floor(random.uniform(1,5)) #just generate 1 number between 1 and 5
update:
i and j are integers between 0 and 9, while n could be any integer.
i and j decide which number can be used in the string while n indicates the length of the numeric string.
If I understand your question (not sure I do), and you have Python 3.6, you can use random.choices:
>>> from random import choices
>>> int(''.join(map(str, choices(range(1, 5), k=10))))
2121233233
The random.choices() function does what you want:
>>> from random import choices
>>> n, i, j = 10, 1, 5
>>> population = list(map(str, range(i, j+1)))
>>> ''.join(choices(population, k=n))
'5143113531'
If you consider list-comprehensions being loops (which they actually in many ways are) there you will not be satisfied with this but I will try my luck:
from random import randint
res = ''.join([str(randint(1, 5)) for _ in range(10)])
print(res) #-> 4353344154
Notes:
The result is a string! If you want an integer, cast to int.
randint works incluselively; the start (1) and end (5) might be produced and returned. If you do not want that, modify them (start = 2 and end = 4)
Is there a reason you are using random.uniform (and subsequently math.floor()) and not simply randint?
x = ''.join([str(math.floor(random.uniform(1,5))) for i in range(10)])

pick random item from a list with constantly reapeating items, but not repeat one twice immediately after

I try to constantly show one of 5 possible items randomly (for a 100 times or so), BUT the important thing is that the SAME item is not allowed to be shown immediately after each other. There needs to be (at least) always one other item in between.
Any idea?
Thank you soo much
Please see the sample code below.
import random
import sys
def special_select(d, num_to_show=100):
selected = None
for i in range(num_to_show):
selected = random.choice(list(d.difference([selected])))
print selected
You may try the function like follows.
>> d = set(range(5))
>> special_select(d)
I'd go for:
from itertools import groupby, islice
from random import choice
choices = range(5)
items = (k for k, g in groupby(iter(lambda: choice(choices), None)))
for item in islice(items, 100):
print item
This lets itertools.groupby handle consecutive duplicate values and islice means you can just skim some off, so if you wanted 50 of them, just use:
random50 = list(islice(items, 50))
Remember the previous item and keep picking a new choice until it differs:
import random
def pick_random(options):
last = next = None
while True:
while next == last:
next = random.choice(options)
yield next
last = next
Use this as a generator:
>>> options = (1, 2, 3, 4, 5)
>>> picks = pick_random(options)
>>> for i in range(100):
... print(next(picks))
...
1
5
4
3
2
3
(...)
3
4
5
Using itertools.islice() makes it easier to pick just 100:
from itertools import islice
for pick in islice(pick_random(options), 100):
print(pick)
Not the most efficient one in Python, but what is done here is: when append the next item, the next item is sampled from the pool excluding the previous item.
def rand_chain(n, chos_from):
rd_ls=[]
rd_ls.append(random.choice(chos_from))
for i in range(n-1):
rd_ls.append(random.choice([v for v in chos_from if v!=rd_ls[i]]))
return rd_ls
>>> rand_chain(10, range(10))
[8, 2, 9, 2, 9, 5, 9, 8, 2, 4]
If you use numpy, replace [v for v in chos_from if v!=rd_ls[i]] with more efficient stuff.
Yes. Plenty.
Determine number of times to iterate.
Start a random number generator.
Set variable previous to None or similar.
Start iteration.
Generate next random number and compare it with previous. If it is the same, skip it.

How to delete random items in a list to keep the list in a certain length?

I used pymongo to dump a list of collections in MongoDB. The length of the list is greater than 10000, about 12000 or longer(The length of the list is not a certain number).
However, I need only 10000 instances of the list. I know that a list 'l' is able to slice by l[:10000] or l[len(l)-10000:]. But I think maybe a random way to delete the item in a list is better.
So I want to know how can I delete random items in the list to make its length reduce to 10000 long? Thanks.
Shuffle the list first and then slice it:
from random import shuffle
shuffle(your_lis)
your_lis = your_lis[:10000]
If order matters:
from random import randrange
diff = len(your_lis) - 10000
for _ in xrange(diff):
ind = randrange(len(your_lis))
your_lis.pop(ind) #a quick timing check suggests that `pop` is faster than `del`
If you want to keep order, you can remove random indexes, for instance:
def remove_random(l, count):
for i in range(count):
index = random.randint(0, len(l) - 1)
del l[index]
This function will remove up to count items from list l.
Here is another way:
from random import random
def chop(the_list, length):
while len(the_list) > length:
del the_list[int(random()*length)]
# usage
chop(your_list, 10000)
def random_reduce(list, count):
length = len(l)
for i in range(count):
index = random.randint(0, length - 1)
del list[index]
length = length - 1
It is quite straightforward using numpy (only extracting 4 items for readability):
>>> import numpy as np
>>> l = range(0, 12000)
>>> np.random.choice(np.asarray(l), 4, false)
import random
subsample=random.sample(population,k)

Pick N distinct items at random from sequence of unknown length, in only one iteration

I am trying to write an algorithm that would pick N distinct items from an sequence at random, without knowing the size of the sequence in advance, and where it is expensive to iterate over the sequence more than once. For example, the elements of the sequence might be the lines of a huge file.
I have found a solution when N=1 (that is, "pick exactly one element at random from a huge sequence"):
import random
items = range(1, 10) # Imagine this is a huge sequence of unknown length
count = 1
selected = None
for item in items:
if random.random() * count < 1:
selected = item
count += 1
But how can I achieve the same thing for other values of N (say, N=3)?
If your sequence is short enough that reading it into memory and randomly sorting it is acceptable, then a straightforward approach would be to just use random.shuffle:
import random
arr=[1,2,3,4]
# In-place shuffle
random.shuffle(arr)
# Take the first 2 elements of the now randomized array
print arr[0:2]
[1, 3]
Depending upon the type of your sequence, you may need to convert it to a list by calling list(your_sequence) on it, but this will work regardless of the types of the objects in your sequence.
Naturally, if you can't fit your sequence into memory or the memory or CPU requirements of this approach are too high for you, you will need to use a different solution.
Use reservoir sampling. It's a very simple algorithm that works for any N.
Here is one Python implementation, and here is another.
Simplest I've found is this answer in SO, improved a bit below:
import random
my_list = [1, 2, 3, 4, 5]
how_big = 2
new_list = random.sample(my_list, how_big)
# To preserve the order of the list, you could do:
randIndex = random.sample(range(len(my_list)), how_big)
randIndex.sort()
new_list = [my_list[i] for i in randIndex]
If you have python version of 3.6+ you can use choices
from random import choices
items = range(1, 10)
new_items = choices(items, k = 3)
print(new_items)
[6, 3, 1]
#NPE is correct, but the implementations that are being linked to are sub-optimal and not very "pythonic". Here's a better implementation:
def sample(iterator, k):
"""
Samples k elements from an iterable object.
:param iterator: an object that is iterable
:param k: the number of items to sample
"""
# fill the reservoir to start
result = [next(iterator) for _ in range(k)]
n = k - 1
for item in iterator:
n += 1
s = random.randint(0, n)
if s < k:
result[s] = item
return result
Edit As #panda-34 pointed out the original version was flawed, but not because I was using randint vs randrange. The issue is that my initial value for n didn't account for the fact that randint is inclusive on both ends of the range. Taking this into account fixes the issue. (Note: you could also use randrange since it's inclusive on the minimum value and exclusive on the maximum value.)
Following will give you N random items from an array X
import random
list(map(lambda _: random.choice(X), range(N)))
It should be enough to accept or reject each new item just once, and, if you accept it, throw out a randomly chosen old item.
Suppose you have selected N items of K at random and you see a (K+1)th item. Accept it with probability N/(K+1) and its probabilities are OK. The current items got in with probability N/K, and get thrown out with probability (N/(K+1))(1/N) = 1/(K+1) so survive through with probability (N/K)(K/(K+1)) = N/(K+1) so their probabilities are OK too.
And yes I see somebody has pointed you to reservoir sampling - this is one explanation of how that works.
As aix mentioned reservoir sampling works. Another option is generate a random number for every number you see and select the top k numbers.
To do it iteratively, maintain a heap of k (random number, number) pairs and whenever you see a new number insert to the heap if it is greater than smallest value in the heap.
This was my answer to a duplicate question (closed before I could post) that was somewhat related ("generating random numbers without any duplicates"). Since, it is a different approach than the other answers, I'll leave it here in case it provides additional insight.
from random import randint
random_nums = []
N = # whatever number of random numbers you want
r = # lower bound of number range
R = # upper bound of number range
x = 0
while x < N:
random_num = randint(r, R) # inclusive range
if random_num in random_nums:
continue
else:
random_nums.append(random_num)
x += 1
The reason for the while loop over the for loop is that it allows for easier implementation of non-skipping in random generation (i.e. if you get 3 duplicates, you won't get N-3 numbers).
There's one implementation from the numpy library.
Assuming that N is smaller than the length of the array, you'd have to do the following:
# my_array is the array to be sampled from
assert N <= len(my_array)
indices = np.random.permutation(N) # Generates shuffled indices from 0 to N-1
sampled_array = my_array[indices]
If you need to sample the whole array and not just the first N positions, then you can use:
import random
sampled_array = my_array[random.sample(len(my_array), N)]

Categories

Resources