Unwanted random iterator in For-Loop - python

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)

Related

Finding limited number sequence in list

This is a slight rework of a previous question that wasn't quite solved. I am working on a python challenge to create a game of Texas Holdem.
As one of my functions, I am creating a function to check for a "straight" hand.
The simple explanation is that the function will take in a list of 7 cards, sort their [integer] values, and check to see if there is a sequence of 5 or longer.
My function DOES work, but it seems a tad more complicated than is necessary:
def is_straight(hand): #Hand is a list of 7 card objects with integer value attributes
#sorts the list of cards in the hand based on their [integer] values
hand.sort(key = attrgetter("value"))
#While loop to check for sequences in list of cards
counter = 0
sequence = 1
sequences_list = []
while counter < len(hand)-1:
#Iterate through the list of int values and, if the value of 1 index == value + 1 of next index, increment sequence number for every sequence
if hand[counter+1].value == hand[counter].value + 1:
sequence += 1
else:
#When sequence breaks, append the number of the sequence to list and reset sequence counter
sequences_list.append(sequence)
sequence = 1
counter += 1
#after while loop, append the last sequence
sequences_list.append(sequence)
#Check that sequence has reached 5 or higher
return max(sequences_list) >= 5
My question is: How could I achieve the same result with a much more condensed code? is there a module (or some other function/trick) that can shortcut this process a bit for more efficient/readable code?
For example, could I import a sequence counter module, break down my "hand" list into a list of values, and return a check that there is a sequence of numbers counting up that has a length of 5 or greater?
E.G.:
import "imaginary_sequence_counter"
def is_straight(hand):
values = [x.value for x in hand]
values.sort()
seq_list = lst(imaginary_sequence_counter(values)
return max(seq_list) >= 5
That's a bit of a silly example, but I hope it clarifies what I'm looking for.
Many thanks for any advice!
As the values are in a small range (1 to 13 I suppose), you can use some binary bit magic.
Let every bit in a single number represent a distinct card value, then "turn on" the bits that correspond to each card in the given hand. Finally check whether there are 5 consecutive 1-bits:
def is_straight(hand):
bitmask = 0
for card in hand:
bitmask |= 1 << card.value
return "11111" in bin(bitmask)
You could use itertools.groupby to count consecutive increasing sequences from the sorted set of card values in the hand:
from itertools import groupby
hand = [1,2,2,3,5,6,7,8,9]
straights = [len(list(g)) for _,g in
groupby(enumerate(sorted(set(hand))),key=lambda h:h[1]-h[0]) ]
print(straights) # [3, 5]
This works by grouping card values based on the difference with their index in the sorted set. Consecutive cards will have the same difference with their index and will end up in the same group. So the length of each group is the length of the sequence.

How can I generate an array of 8 unique numbers in python?

I'm trying to create a function that, one by one, generates 8 random numbers from 1 to 8 and adds them to an array, checking if they are unique each time before adding them. However, it doesn't work as expected and, while it creates an array consisting of 8 elements, the numbers are not all unique. My code:
import random #Allows the program to generate random numbers
correctSequence = [] #Defines array
def generateSequence(correctSequence): #Defines function, passes array as parameter
selection = random.randint(1,8) #Creates a random number and adds it to the array so there is a starting point for the for loop (Ln 10)
correctSequence.append(str(selection))
while len(correctSequence) < 8: #The while loop will continue to run until the array consists of 8 objects
selection = random.randint(1,8) #Generates a random number
for i in range(len(correctSequence)): #Loops through each value in the array
if correctSequence[i] == selection: #This line checks to see if the value already exists in the array
print("Duplicate") #This line is meant to print "Duplicate" when a duplicate value is generated
else:
correctSequence.append(str(selection)) #If the value doesnt already exist in the array, it will be added
print("Valid") #This line is meant to print "Valid" when a unique value is generated and added to the array
return correctSequence
#Main body of program
generateSequence(correctSequence) #The function is called
print(correctSequence) #The array is printed
I think the problem occurs somewhere around line 10 as the program seems to go straight to the else statement but I can't see why this would happen.
Additionally, when I run the program, the array, when printed, always seems to repeat the same 2 or 3 numbers multiple times, I don't know if this relates to the already existing issue but it could help to explain what's going on.
8 random numbers from 1 to 8 and adds them to an array, checking if they are unique each time before adding them
There is nothing "random" here. All you need to do is to shuffle the numbers 1 to 8.
import random
nums = list(range(1, 9))
random.shuffle(nums)
print(nums)
# [2, 6, 4, 3, 1, 7, 8, 5]
For the way you want to do it:
import random #Allows the program to generate random numbers
correctSequence = [] #Defines array
def generateSequence(correctSequence): #Defines function, passes array as parameter
while len(correctSequence) < 8:
selection = random.randint(1,8) #Creates a random number and adds it to the array so there is a starting point for the for loop (Ln 10)
correctSequence.append(selection) if selection not in correctSequence else None
return correctSequence
generateSequence(correctSequence) #The function is called
print(correctSequence) #The array is printed
But there are better ways for example:
import random #Allows the program to generate random numbers
def generateSequence():
return random.sample([1,2,3,4,5,6,7,8], 8)
print(generateSequence())
or change the return to return random.sample(range(1, 9), 8) as mentioned above for extra conciseness.

Generate list of 5 non-repeating integers

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()

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.

Random sampling from a set of integers

I am working with python 3.2 and I spent a lot of time trouble shooting this, and I still can't seem to wrap my brain around it.
number = random.randint ( x0 ,xn )
I'm generating a random number. It's purpose is to make my code come at me differently everytime.
For example I have 10 variables of text that I have written. I have solved the problem of not having these variables appear in the same order at each program run.
The issue I have is that they now appear randomly everytime. It picks one out of 10 everytime, instead the first time 10 and next 9. I can't seem to find out how to exclude the previous ones.
thelist = [0]
while i < x
if number in thelist:
>>>repeat<<<
else:
thelist.append (number)
if ( number == x0 ):
>>>something<<<
elif ( number == x1 ):
>>>something<<<
This is what I would imagine the code would look like, everytime you loop one more number gets appended to the list, so that everytime it picks a number already in the list it repeats the loop again until it then has used all the numbers that random.randint can pull.
Here's a shuffle function:
import random
max = 15
x = list(range(max+1))
for i in range(max, 0, -1):
n = random.randint(0, i)
x[n], x[i] = x[i], x[n]
This starts with a sorted list of numbers [0, 1, ... max].
Then, it chooses a number from index 0 to index max, and swaps it with index max.
Then, it chooses a number from index 0 to index max-1, and swaps it with index max-1.
And so on, for max-2, max-3, ... 1
As yosukesabai rightly notes, this has the same effect as calling random.sample(range(max+1), max+1). This picks max + 1 unique random values from range(max+1). In other words, it just shuffles the order around. Docs: http://docs.python.org/2/library/random.html#random.sample
If you wanted something more along the lines of your proposed algorithm, you could do:
import random
max = 15
x = range(max+1)
l = []
for _ in range(max+1):
n = random.randint(0,max)
while n in l:
n = random.randint(0,max)
l.append(n)
From what I understand of your description and sample code, you want thelist to end up with every integer between x0 and xn in a random order. If so, you can achieve that very simply with random.shuffle(), which shuffles a list in place:
import random
x0 = 5
xn = 15
full_range = list(range(x0, xn))
print(full_range)
random.shuffle(full_range)
print(full_range)

Categories

Resources