Python random.choice stuck in loop [duplicate] - python

This question already has answers here:
Listing words randomly without repeating them
(2 answers)
Closed 1 year ago.
I am a complete amateur and know there probably is easier ways to do this, but this isn't an important project, just a superquick code. For some reason it seems like it gets stuck in a loop because random.choice gives the same number over and over again. It works sometimes, I just have to give it a couple of tries, and as I increase the range the more difficult it is.
The code is supposed to allocate a unique number to each "player" every round, without giving the same player the same number twice.
import random
A = []
B = []
C = []
D = []
E = []
F = []
G = []
H = []
I = []
J = []
K = []
lt = [A, B, C, D, E, F, G, H, I, J, K]
lit = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']
ALT = 0
for e in range(5):
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
for i in lt:
while True:
add = random.choice(lst)
if add not in i:
i.append(add)
lst.remove(add)
break
else:
print('fant ingenting')
ALT += 1
print(ALT)
tot = 0
for k in lt:
print(str(lit[tot]) + ' = ' + str(k))
tot += 1

So you have a list of 11 players and you want to assign 11 unique IDs (from 1 to 11) to them. Why not just shuffle() the list of IDs once and be done with it?
lit = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
random.shuffle(lst)
for player, player_id in zip(lit, lst):
print("player", player, "has id", player_id)
If you want to give random IDs multiple times without repeating, a very simple way to do this would be to just rotate them:
lit = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
random.shuffle(lst)
for round in range(5):
print("Round", round)
for player, player_id in zip(lit, lst):
print("> player", player, "has id", player_id)
lst = lst[1:] + lst[:1]
Note that that last step of rotating using lst = lst[1:] + lst[:1] is ok with small lists, but can be really inefficient for large lists, since it creates a new copy of the entire list every time. If you are working with larger lists, you can use a deque:
lit = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K']
lst = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
random.shuffle(lst)
lst = deque(lst)
for round in range(5):
print("Round", round)
for player, player_id in zip(lit, lst):
print("> player", player, "has id", player_id)
lst.rotate(1)
One caveat of this solution is that since players are ordered, the IDs will be distributed each round in order. So, for example, you already know that if the IDs are initially 1, 2, 3 for players A, B, C the player C will get IDs 2 and 1 in the next two rounds. In order to avoid this and get more unpredictable IDs you could also shuffle the list of players first:
random.shuffle(lit)
random.shuffle(lst)
# ... same code as above ...
This way you will not have a predictable "next player" to get a specific ID.

Related

python: sort array when sorting other array

I have two arrays:
a = np.array([1,3,4,2,6])
b = np.array(['c', 'd', 'e', 'f', 'g'])
These two array are linked (in the sense that there is a 1-1 correspondence between the elements of the two arrays), so when i sort a by decreasing order I would like to sort b in the same order.
For instance, when I do:
a = np.sort(a)[::-1]
I get:
a = [6, 4, 3, 2, 1]
and I would like to be able to get also:
b = ['g', 'e', 'd', 'f', 'c']
i would do smth like this:
import numpy as np
a = np.array([1,3,4,2,6])
b = np.array(['c', 'd', 'e', 'f', 'g'])
idx_order = np.argsort(a)[::-1]
a = a[idx_order]
b = b[idx_order]
output:
a = [6 4 3 2 1]
b = ['g' 'e' 'd' 'f' 'c']
I don't know how or even if you can do this in numpy arrays. However there is a way using standard lists albeit slightly convoluted. Consider this:-
a = [1, 3, 4, 2, 6]
b = ['c', 'd', 'e', 'f', 'g']
assert len(a) == len(b)
c = []
for i in range(len(a)):
c.append((a[i], b[i]))
r = sorted(c)
for i in range(len(r)):
a[i], b[i] = r[i]
print(a)
print(b)
In your problem statement, there is no relationship between the two tables. What happens here is that we make a relationship by grouping relevant data from each table into a temporary list of tuples. In this scenario, sorted() will carry out an ascending sort on the first element of each tuple. We then just rebuild our original arrays

Making a list of randomly selected items

I nee to make a list or tuple containing a series of 10 numbers and five letters. randomly select four numbers or letters from the list and print a message saying that any ticket matching these four numbers or letters wins a prize.
code I tried but doesn't know what to write ahead:
possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e']
winner = []
print("winning ticket is...")
while len(winner) > 4:
pulled_item = choice(possibilities)
if pulled_item not in winner:
print(f"we pulled a {pulled_item}!")
winner.append(pulled_item)
pls provide code
which tells both of the number selected and winning number.
If you can use the package random then
from random import randint
possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e']
winning_ticket = ""
print("Winning ticket is...")
for x in range(4):
winning_ticket += str(possibilities[randint(0, len(possibilities)-1)])
print(f"Any ticket matching {winning_ticket} wins a prize!")
You can use the choice function from Pythons inbuilt random library.
from random import choice
possibilities = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e']
# choose four items from the possibilities
chosen = [str(choice(possibilities)) for _ in range(4)]
# print the four chosen items
print(f"Any ticket matching {''.join(chosen)} wins a prize!")
This code makes an array of the four pulled numbers or letter, and then print the entire array at the end.
from random import choice
lottery = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd');
win = [];
for value in range(4):
choose = choice(lottery);
win.append(choose);
print(f"Any ticket matching {win} wins a prize")

Using a list of dictionaries to associate letters with numbers in python [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I am about a week into learning Python and I am very confused about making dictionaries (or if I should even be using a dictionary for this). I've searched all over for a similar problem, but it's possible I just don't understand well enough to recognize an applicable answer.
What I am trying to do is to associate a letter with a unique "score" (this score is a number). So for a toy example, for ABCA with scores of 1, 20, 10, 5...the first A = 1, B = 20, C = 10, last A = 5. The end goal is to then remove the letters with the low "scores"/numbers.
My data in a list is something like this:
x_list = ['ABCDEABCDE10 2 3 4 5 6 7 8 9 1', 'EDCABB6 9 8 8 8 6 9', etc.]
Similar to the toy example, I want A = 10, B = 2, C = 3, etc. in the first string and E = 6, D = 8, C = 8, etc. in the second string. So I think I want to make a dictionary were the letters are keys and numbers are values? And then a list of dictionaries? What I am thinking is something like:
dictionary1 = {A:10, B:2, C:3, D:4, E:5, A:6, B:7, C:8, D:9, E:1}
dictionary2 = {E:9, D:8, C:8, A:8, B:6, B:9}
dictionary_list = (dictionary1, dictionary2)
And then be able to remove all of the values lower than 5 from the original list.
final_list = []
for each_list in dictionary_list:
if value > 5 in each_list:
final_list.append(each_list)
final_list = [[A,A,B,C,D], [E,D,C,A,B,B]]
I've tried looping through x_list with for loops to get the result, but I can't figure out how to get the numbers to line up with the values without using a dictionary.
Any help is very much appreciated!
(This is also my first time posting so please let me know if I make any newbie errors either in coding or if I shouldn't be using dictionaries at all for this.)
*Edited to improve clarity
I'm not sure I understand the problem you are trying to solve correctly, so let me try to rephrase it. Let's focus on a single list for now. If I understand correctly, you are looking at lists that look like this.
l = ['A', 'B', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'E', 1, 2, 3, 4, 5, 6, 7, 8, 9, 1]
Here I assume that we have exactly as many letters (or strings) as we have numbers (or integers).
You then want to associate each letter in the first half of the list with a number from the second half of the list. So you want to look at pairs of letters and numbers. As is it turns out, Python supports tuples as a data type and pairs are just tuples with two elements. To match up the letters with the numbers, you could do the following:
letters = l[:len(l)//2] # = ['A', 'B', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'E']
numbers = l[len(l)//2:] # = [1, 2, 3, 4, 5, 6, 7, 8, 9, 1]
pairs = list(zip(letters, numbers)) # = [('A', 1), ('B', 2), ('C', 3), ('D', 4), ('E', 5), ('A', 6), ('B', 7), ('C', 8), ('D', 9), ('E', 1)]
In the first two lines I use slices (see for example here) to split the list into two halves. Then I use zip to create pairs from the resulting lists.
To then get all letters that are associated with an integer less than k, you could do the following.
k = 5 # or whatever you choose
result = [letter for letter, number in pairs if number < k] # = ['A', 'B', 'C', 'D', 'E']
Here, I am using a list comprehension to generate the result list.
To do all of this on a list of lists, you can wrap the code in a function:
def f(input_list, threshold):
letters = input_list[:len(input_list)//2]
numbers = input_list[len(input_list)//2:]
pairs = list(zip(letters, numbers))
return [letter for letter, number in pairs if number < threshold]
You can then use another list comprehension to apply the function to each list in a list of lists.
l = [['A', 'B', 100, 2], ['C', 'D', 12, 42]]
threshold = 32
result = [f(input_list, threshold) for input_list in l] # = [['B'], ['C']]
Finally, dictionaries are probably not the right data structure for this particular problem. In a dictionary, you associate keys with values and each key can only have exactly one value. In your example above, you have two different occurrences of the letter 'A' and you associate them with the numbers 1 and 6, respectively. Therefore, using dictionaries wouldn't be very natural. (That said, you could define the value associated with the key 'A' to be a list or set of numbers, but I don't think that would necessarily lead to a better solution of your problem.)
You can convert each character to an ascii integer value using ord. In ascii, all of the characters values are right next to eachother, so something like ord('B') > ord('A') will always be true. You can use this to filter the list. e.g.
>>> [c for c in 'ABCDEFGHIJK' if ord(c) > ord('E')]
['F', 'G', 'H', 'I', 'J', 'K']
In dictionary, key are immutable. so, you can't create dictionary with repeated keys. Instead of that, you can create list of dictionary like this,
[{'A': [2, 7], 'B': [3, 8], 'C': [4, 9], 'D': [5, 1], 'E': [6]}, {'E': [9], 'D': [8], 'C': [8], 'A': [8], 'B': [6, 9]}]
you can achieve it by following code ;
x_list = [['ABCDEABCDE1', 2, 3, 4, 5, 6, 7, 8, 9, 1], ['EDCABB6', 9, 8, 8, 8, 6, 9]]
result=[]
for i in x_list:
string=i[0]
print(string)
l=i[1:]
print
d={}
for i,j in zip(string,l):
d.setdefault(i,[]).append(j)
result.append(d)
print(result)
after that, to get filtered list ;
final_result=[]
for i in result:
l1=[]
for key,val in i.items():
for v in val:
if v>=5:
l1.append(key)
final_result.append(l1)
print(final_result)
output:
[['A', 'B', 'C', 'D', 'E'], ['E', 'D', 'C', 'A', 'B', 'B']]

Placing values of a smaller list at appropriate places in a larger list

I need to enter the values of a smaller list at appropriate locations in a larger list, e.g.,
l_1 = ['a', 'c', 'e', 'd']
v_1 = [5, 6, 8, 10]
l_2 = ['a', 'ab', 'c', 'd', 'e']
I am looking to generate a v_2 of values (initialized as zeros of size l_2) which takes v_1 at locations where l_1 belongs to l_2. So, for the above example, I would like to get
v_2 = [5, 0, 6, 10, 8]
Clearly, l_1 is a subset of l_2, i.e., l_2 will always have the quantities of l_1.
I found the first answer here helpful to determine the location of l_1 in l_2 and am looking to modify it to suit my case. Ideally, I would like to have 3 inputs
a = l_1
b = l_2
val = v_1
def ismember(a, b, val):
bind = {}
for i, elt in enumerate(b):
if elt not in bind:
bind[elt] = i
return [bind.get(itm, None) for itm in a]
And I need to get the return statement modified so that appropriate entries of v_1 are entered into the padded v_2, which, can be initialized as np.zeros((len(b),1)) within the function
It is much easier to construct a dict for lookups using the values from l_1 as keys and v_1 as values. For example:
>>> l_1 = ['a', 'c', 'e', 'd']
>>> v_1 = [5, 6, 8, 10]
>>> l_2 = ['a', 'ab', 'c', 'd', 'e']
then
>>> d = dict(zip(l_1, v_1))
>>> [d.get(i, 0) for i in l_2]
[5, 0, 6, 10, 8]

Trouble find value in list of lists

I have two lists. The first is a_list and is like this:
a_list = [1,2,3]
The second is b_list, and it's a list with lists in it. It's like this:
b_list = [['a',1,'b'],['c',2,'g'],['e',3,'5']
What I'm trying to do is use a_list to find the correct b_list and print the value[2] in the b_list.
My code looks like:
for a in a_list:
for b in b_list:
if b[1] == a:
print b[2]
The actually a_list has 136 values in it. And the real b_list has 315 lists in it.
I had initially written code to index the b item and remove it from b_list if b[1] == a.
I've taken that code out in order to solve the real problem.
There is no need to loop over a_list; a simple in test would suffice:
for b in b_list:
if b[1] in a_list:
print b[2]
This would perform better if you made a_list a set:
a_set = set(a_list)
for b in b_list:
if b[1] in a_set:
print b[2]
Either way, this code prints:
b
g
5
for your example data.
If I understood correctly what you want to do:
a_list = [1,2,3,5]
b_list = [['a',1,'b'],['c',2,'g'],['e',3,'5'],
['d',4,'h'],['Z',5,'X'],['m',6,'i']]
print 'a_list ==',a_list
print '\nb_list before :\n',b_list
print '\nEnumerating b_list in reversed order :'
L = len(b_list)
print (' i el L-i b_list[L-i] \n'
' -------------------------------------')
for i,el in enumerate(b_list[::-1],1):
print ' %d %r %d %r' % (i,el,L-i,b_list[L-i])
L = len(b_list)
for i,el in enumerate(b_list[::-1],1):
if el[1] in a_list:
del b_list[L-i]
print '\nb_list after :\n',b_list
result
a_list == [1, 2, 3, 5]
b_list before :
[['a', 1, 'b'], ['c', 2, 'g'], ['e', 3, '5'],
['d', 4, 'h'], ['Z', 5, 'X'], ['m', 6, 'i']]
Enumerating b_list in reversed order :
i el L-i b_list[L-i]
-------------------------------------
1 ['m', 6, 'i'] 5 ['m', 6, 'i']
2 ['Z', 5, 'X'] 4 ['Z', 5, 'X']
3 ['d', 4, 'h'] 3 ['d', 4, 'h']
4 ['e', 3, '5'] 2 ['e', 3, '5']
5 ['c', 2, 'g'] 1 ['c', 2, 'g']
6 ['a', 1, 'b'] 0 ['a', 1, 'b']
b_list after :
[['d', 4, 'h'], ['m', 6, 'i']]
The reason why it is necessary to iterate in b_list in reversed order is the one said by abarnert and explained hereafter by the doc:
Note: There is a subtlety when the sequence is being modified by the
loop (this can only occur for mutable sequences, i.e. lists). An
internal counter is used to keep track of which item is used next, and
this is incremented on each iteration. When this counter has reached
the length of the sequence the loop terminates. This means that if the
suite deletes the current (or a previous) item from the sequence, the
next item will be skipped (since it gets the index of the current item
which has already been treated). Likewise, if the suite inserts an
item in the sequence before the current item, the current item will be
treated again the next time through the loop. This can lead to nasty
bugs that can be avoided by making a temporary copy using a slice of
the whole sequence, e.g.,
for x in a[:]:
if x < 0: a.remove(x)
http://docs.python.org/2/reference/compound_stmts.html#the-for-statement

Categories