Generate list of 5 non-repeating integers - python

I'm new to Python and I am trying to generate a list of 4 random numbers with integers between 1 and 9. The list must contain no repeating integers.
The issue I am having is that the program doesn't output exactly 4 numbers everytime. Sometimes it generates 3 numbers or 2 numbers and I can't figure out how to fix it.
My code:
import random
lst = []
for i in range(5):
r = random.randint(1,9)
if r not in lst: lst.append(r)
print(lst)
Is there a way to do it without the random.sample? This code is part of a larger assignment for school and my teacher doesn't want us using the random.sample or random.shuffle functions.

Your code generates 5 random numbers, but they are not necessarily unique. If a 2 is generated and you already have 2 in list you don't append it, while you should really be generating an alternative digit that hasn't been used yet.
You could use a while loop to test if you already have enough numbers:
result = [] # best not to use list as a variable name!
while len(result) < 5:
digit = random.randint(1, 9)
if digit not in result:
result.append(digit)
but that's all more work than really needed, and could in theory take forever (as millions of repeats of the same 4 initial numbers is still considered random). The standard library has a better method for just this task.
Instead, you can use random.sample() to take 5 unique numbers from a range() object:
result = random.sample(range(1, 10), 5)
This is guaranteed to produce 5 values taken from the range, without duplicate digits, and it does so in 5 steps.

Use random.sample:
import random
random.sample(range(1, 10), 4)
This generates a list of four random values between 1 to 9 with no duplicates.

Your issue is, you're iterating 5 times, with a random range of 1-9. That means you have somewhere in the neighborhood of a 50/50 chance of getting a repeat integer, which your conditional prevents from being appended to your list.
This will serve you better:
def newRunLst():
lst = []
while len(lst) < 4:
r = random.randint(1,9)
if r not in lst: lst.append(r)
print lst

if random list needed is not too small (compared to the total list) then can
generate an indexed DataFrame of random numbers
sort it and
select from the top ... like
(pd.DataFrame([(i,np.random.rand()) for i in range(10)]).sort_values(by=1))[0][:5].sort_index()

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

how to print 100 random numbers of "set" in python and add them to empty set()

how to print 100 random numbers of "set" in python, means I have to take 100 random numbers from given range and add it to an empty set(). I need solution in Python.I have tried in following way but its not taking 100 numbers exact.
import random
s=set()
for i in range(200):
s.add((random.randint(0,101)))
print(s)
print(len(s))
This will create a set that always has 100 elements (given the input range is equal to or larger than 100).
import random
set(random.sample(range(1337), 100))
as comments said the set can't contain duplicated numbers, so you need to execute a while loop until you get the number of elements you need in your set.
Then add a random number
import random
s=set()
while len(s) < 100:
s.add((random.randint(0,200)))
print(s)
print(len(s))
set() can only contain unique items. If you try adding an item to set() that already exists, it will be silently ignored:
>>> s = set()
>>> s.add(1)
>>> s
{1}
>>> s.add(1)
>>> s
{1}
In your original code you're actually trying to "Add 200 random numbers to a set from 0 to 100". Conceptually this is wrong, because it's not possible to get 200 unique random numbers from a range of 0 - 100. You can only get up to 100 unique random numbers from that range.
The other issue with your code is what you're randomly choosing the number in each iteration, without checking if it has been added before.
So, in order to take N random numbers from a range of 0 to M, you would have to do the following:
import random
s = set()
N = 100 # Number of items that will be appended to the set
M = 200 # Maximum random number
random_candidates = list(range(M))
for _ in range(N):
numbers_left = len(random_candidates)
# Choose a random number and remove it from the candidate list
number = random_candidates.pop(random.randrange(numbers_left))
s.add(number)
The above will work well for small ranges. If you expect M to be a large number, then generating a large random_candidates array will not be very memory effective.
In that case it would be better to randomly generate a number in a loop until you find one that was not chosen before:
import random
s = set()
N = 100 # Number of items that will be appended to the set
M = 2000 # Maximum random number
for _ in range(N):
random_candidate = random.randrange(M)
while random_candidate in s:
random_candidate = random.randrange(M)
s.add(random_candidate)
sets don't allow duplicate values (that's part of sets defininition...), and statically you will get duplicate values when calling random.randint(). The solution here is obviously to use a while loop:
while len(s) < 100:
s.add(random.randint(0, 101))
Note that with those values (100 ints in the 0..101 range) you won't get much variations since you're selecting 100 distinct values out of 102.
Also note that - as quamrana rightly mentions in a comment - if the range of possible values (randint() arguments) is smaller than the expected set length, the loop will never terminate.

Unwanted random iterator in For-Loop

I tried to create a function for generating a set number (numbersToChoose) of values between two other values (startFrom and stopAt) but for some reason the iterator (?) in the second for-loop (line 7), in this case a seems to be randomly generated even though I don't see any reason for that.
def chooseRandomNumbers(numbersToChoose, startFrom, stopAt):
numbers = [(randrange(startFrom, stopAt))] # initialize empty list of numbers
for n in range(numbersToChoose-1): # choose random number from 1 to 49 a total of 7 times
x = randrange(startFrom, stopAt+1)
for a in numbers: # check for already existing elements in "numbers"
if x == numbers[a]: # if new random number already exists in "numbers" discard and run loop again
n -= 1 # decreases n by 1 so a new (hopefully not already existing) number can be generated
else: # generated numbers does not exist in "numbers" yet
numbers.append(x) # appends randomly chosen number to list "numbers"
return numbers # returns said list "numbers"
Any advice on how to deal with this is greatly appreciated.
Also please tell me if possible anything else bad in the code (just started python).
Your code to check if generated number is already in the list is wrong.
for a in numbers, in this loop, you are using numbers[a] while a is a member of list but not the index of the member.
use in to test if a number is in list:
from random import randrange
def chooseRandomNumbers(numbersToChoose, startFrom, stopAt):
numbers = []
for n in range(numbersToChoose):
x = randrange(startFrom, stopAt+1)
if not x in numbers:
numbers.append(x)
else:
n -= 1
return numbers
or simply:
from random import sample
def chooseRandomNumbers(numbersToChoose, startFrom, stopAt):
return sample(range(startFrom,stopAt+1),numbersToChoose)

combining strings of n length in a list python

Whenever I use permutations, I have to create a list of the permutations because it returns a 'itertools.permutations object at 0x0190A5D0'. Then after I create the list, I have to combine the strings in each list so that I have the permutation I wanted originally. If it is a number, then I have to make them all integers again.
Two part question:
1) Is there an easier way to do a permutation and create the list of numbers?
2) Below I've created a permutation of a number with 5 digits. However, where I have '12345', I want the user to input their own number. Thus the formatting string b=['s%s%s...] will have to be n %s and n x. Anyone know how to do this?
(FYI my program is trying to find the next largest number with the same digits given a user's input so 12345 next largest is 12354)
Both questions were answered below please see both responses. Thanks!!!
def nexthighest():
from itertools import permutations
numb = str(12345)
a = list(permutations(numb))
b = ['%s%s%s%s%s' % xxxxx for xxxxx in a] #<-- this is where the n length problem occurs
c = list(map(int,b))
for i in c:
if i >12345:
print(i)
break
You don't need to build all those lists. Just loop over the return value from permutations, joining and parsing as an integer each permutation as it comes by:
def nexthigher(n):
for p in permutations(sorted(str(n))):
i = int("".join(p))
if i > n:
return i
I can answer part two for you:
b = ["".join(x) for x in a]

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