How do I get random numbers that fully cover a range? - python

I'm trying to have random integers between (1,5) but the catch is displaying all of the values from the random integer in a for loop ranging of 10 loops. I only have access to randint() and random() methods in 'random class'.
from random import randint
eventList = []
taskList = []
dayList = []
def getEventList():
eventList.sort()
return eventList
def getTaskList():
return taskList
def getDayList():
return dayList
def generateData():
while len(getTaskList()) < 10:
# Need to implement a way to stretch the random int while having all the integers present
randomEvent = randint(1, 5)
randomTask = randint(10, 30)
randomDay = randint(1, 9)
eventList.append(randomEvent)
dayList.append(randomDay)
if randomTask not in getTaskList():
taskList.append(randomTask)

Based on clarifications in my other answer, I think you meant to ask "how to I get random numbers that fully cover a range?"
You are using randint, and just calling it extra times hoping to get all the values. But depending on random chance, that can take a while.
It would be better to just take all the values you want, e.g. list(range(1,6))
and then just rearrange that with random.shuffle
https://docs.python.org/3/library/random.html#random.shuffle
import random
values = list(range(1, 6))
random.shuffle(values)
print(values)
Obviously, if this is what you want to do, but your prof says you can't use that function, then you should use that function, get working code, and only THEN write your own replacement for the standard library. (so you only have to debug one or the other)
So write your own version of shuffle. I'll leave the details to you, but it should be pretty easy to use randint to pick one of the available entries, removing it, and so on.

In order to sort things, you need "things" to sort. Therefore you must group together all three numbers in a single entity, and then sort those entities. I suggest you use a Task class, and let it handle things like generating a unique task number. Avoid global variables.
Here is a minimal reproduction (based on your code) of just taking your three numbers in a tuple, combining them with the builtin "zip" and sorting that:
from random import randint
def generateData():
eventList = []
taskList = []
dayList = []
for _ in range(10):
randomEvent = randint(1, 5)
randomTask = randint(10, 30)
randomDay = randint(1, 9)
eventList.append(randomEvent)
taskList.append(randomTask)
dayList.append(randomDay)
return sorted(zip(eventList, taskList, dayList))
for task in generateData():
print(task)
Also note that python convention for variable names is a little different, but I left that alone and used your names.

Related

How do I create a random number based on a seed without using random.seed()?

I know that you can do this:
random.seed(100)
r = random.randint(1, 3)
But is it possible to do something more similar to this?
r = random.randint(1, 3, seed=100)
Also I am aware that the code above would give the same output every time, that is the behavior I want.
Create your own RNG with the seed you want, and call its randint method:
r = random.Random(100).randint(1, 3)
Put random() inside a wrapper. I can't write Python, so this is pseudocode:
myRandom(lo, hi, mySeed)
random.seed(mySeed)
return random.randint(lo, hi)
I will leave converting that to correct Python as an exercise.

creating a variable number of variables and populating them from a list

I'm trying to create a few variables at the same time.
I want the user to specify a value and as a result create that many unique variables, populate them with values from an already created list and then print the results.
I can do this if I specify the number of variables each time, but I want the program to ask the user for the number instead.
This works as I would like; but, it assumes I know the number of Hands to create
Deck = ["AC","AH","AS","AD","KC","KH","KS","KD"]
Hand1 = [Deck.pop(),Deck.pop()]
Hand2 = [Deck.pop(),Deck.pop()]
...
HandN = [Deck.pop(),Deck.pop()]
print("Hand1: ", Hand1)
print("Hand2: ", Hand2)
...
print("HandN: ", HandN)
I want to do the same but instead ask the user for how many hands to create, Something like:
Deck = ["AC","AH","AS","AD","KC","KH","KS","KD"]
NumberOfHands = int(input("How Many Hands? "))
for each in range(1,NumberOfHands+1):
Hand+str(each) = [DeckList.pop(),DeckList.pop]
for each in range(1,NumberOfHands+1):
print(Hand+str(each))
The way I thought this would have worked results in a syntax error.
I've seen some other questions that look similar and hint at doing this with a dictionary; but, its not making sense to me on how to apply it for this type of scenario...
Thanks for your help!
Here is how to do it with a dict:
hands = {f"Hand{each + 1}": [Deck.pop(), Deck.pop()] for each in range(NumberOfHands)}
This will create a dict of size NumberOfHands containing all the hands from 1 to NumberOfHands
You can do it
But you shuldn't
Deck = ["AC","AH","AS","AD","KC","KH","KS","KD"]
NumberOfHands = int(input("How Many Hands? "))
for n in range(1,NumberOfHands+1):
globals()["Hand" + str(n)] = [Deck.pop(),Deck.pop()]
for n in range(1,NumberOfHands+1):
print( globals()['Hand{}'.format(n)])
example:
How Many Hands? 4
['KD', 'KS']
['KH', 'KC']
['AD', 'AS']
['AH', 'AC']
this is a dirty approach of how to do it, but seriously, consider to create only one variable and store them in a list, and access them by index, something safer.
Consider this nice blog to read: http://stupidpythonideas.blogspot.com/2013/05/why-you-dont-want-to-dynamically-create.html

My shuffle algorithm crashes when more trials than objects

I am trying to make an experiment where a folder is scanned for images. For each trial, a target is shown and some (7) distractor images. Afterward, in half the trials people are shown the target image and in the other half, they are shown an image that wasn't in the previous display.
My current code sort of works, but only if there are fewer trials than objects:
repeats = 20
# Scan dir for images
jpgs = []
for path, dirs, files in os.walk(directory):
for f in files:
if f.endswith('.jpg'):
jpgs.append(f)
# Shuffle up jpgs
np.random.shuffle(jpgs)
# Create list with target and probe object, Half random, half identical
display = []
question = []
sameobject = []
position = np.repeat([0,1,2,3,4,5,6,7], repeats)
for x in range(1,(repeats*8)+1):
display.append(jpgs[x])
if x % 2 == 0:
question.append(jpgs[-x])
sameobject.append(0)
else:
question.append(jpgs[x])
sameobject.append(1)
# Concatonate objects together
together = np.c_[display,question,position,sameobject]
np.random.shuffle(together)
for x in together:
# Shuffle and set image
np.random.shuffle(jpgs)
myList = [i for i in jpgs if i != together[trial,0]]
myList = [i for i in myList if i != together[trial,1]]
# Set correct image for target
myList[int(together[trial,2])] = together[trial,0]
First of all, I am aware that this is horrible code. But it gets the job done coarsely. With 200 jpgs and a repeat of 20, it works. If repeat is set to 30 it crashes.
Here is an example with repeat too high:
File "H:\Code\Stims\BetaObjectPosition.py", line 214, in <module>
display.append(jpgs[x])
IndexError: list index out of range
Is there a way to update my code in a way that allows more trials while all objects are used as evenly as possible (one object should not be displayed 3 times while another is displayed 0) over an entire experiment?
Full, reproducible example
Bonus points if anyone can see an obvious way to balance the way the 7 distractor images are selected too.
Thanks for taking your time to read this. I hope you can help me onwards.
The solution that changes your code the least should be to change each call of jpgs[x] to jpgs[x % len(jpgs)]1. This should get rid of the IndexError; it basically wraps the list index "around the edges", making sure it's never to large. Although I'm not sure how it will interact with the jpgs[-x] call.
An alternative would be to implement a class that produces a longer sequence of objects from a shorter one.
Example:
from random import shuffle
class InfiniteRepeatingSequence(object):
def __init__(self, source_list):
self._source = source_list
self._current = []
def next(self):
if len(self._current) == 0:
# copy the source
self._current = self._source[:]
shuffle(self._current)
# get and remove an item from a list
return self._current.pop()
This class repeats the list indefinitely. It makes sure to use each element once before re-using the list.
It can easily be turned into an iterator (try changing next to __next__). But be careful since the class above produces an infinite sequence of elements.
1 See "How does % work in Python?" for an explanation about the modulo operator.
Edit: Added link to modulo question.

Picking random integer not in a list between range

I need to randomly pick an integer between two integers but that integer can't be in a list.
This is how I am doing it:
bannedReturningCustomersIndex = []
index = next(iter(set(range(0, 999)) - set(bannedReturningCustomersIndex)))
#some code..
bannedReturningCustomersIndex.append(index)
The problem is that I'm not pickig the integer randomly, I'm picking them 1 by 1 from the beginning...
Use random.choice after converting to a list:
import random
bannedReturningCustomersIndex = []
valid_indexes = list(set(range(0, 999)) - set(bannedReturningCustomersIndex))
bannedReturningCustomersIndex.append(random.choice(valid_indexes))
Even though the previous answer is correct, I'd like to propose the following approch, which is more readable, flexible and separates the logic from your main code.
import random
def iterRandNonBannedCustomers(banned_idx, c_idx=range(0, 999)):
c_idx = list(c_idx)
random.shuffle(c_idx)
return filter(lambda i: i not in banned_idx, c_idx)
The function returns an iterator over all non-banned customers. Use it, for example, like this:
for customer in iterRandNonBannedCustomers(bannedReturningCustomersIndex):
# do stuff

Python random.randrange producing everything but the step

When using this following code:
import random
sticks = 100
randomstep = random.randint(1, 6)
expertmarbles = random.randrange(1, sticks, 2**randomstep)
the output is producing everything excluding the step, so for example i would like this to output a random from these numbers: 2,4,8,16,32,64. However it will output everything but these numbers. Can anyone offer any advice, the reason i'm using variables here is because the amount of sticks will decrease.
Instead of using random.randrange you could use random.choice (docs):
import random
min_power = 1
max_power = 6
print(random.choice([2**i for i in range(min_power, max_power + 1)]))
You can try this
def myRand(pow_min,pow_max):
return 2**random.randint(pow_min,pow_max)
I would suggest you to use this instead of random.choice, which requires you to generate a list, which is unnecessary.

Categories