Picking random items from a list twice? - python

I have lists;
cards = [11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10]
player_cards = []
And i have a function will deal cards.
def deal_cards():
player_cards.append(random.choice(cards))
player_cards.append(random.choice(cards))
I'm trying to make a blackjack game. And just want to pick random cards for the player twice using for loop or some another method. So how can i make it?

Use sample from the random module. There, you can specify the number of elements you want to pick at random from a given set. Elements are sampled without replacement.
player_cards = random.sample(cards,2)

Simple code based on Simon Hawe solution can be as below:
import random
cards = [11, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10, 12, 13, 14, 15, 16]
def deal_cards():
return random.sample(cards, 2)
player_cards = deal_cards()
print(f'Player have a cards: {player_cards} from possible list {cards}')

Related

Unexpected None printed after shuffling list

from random import *
day = list(range(1, 29))
day = day[3:29]
shuffleday = shuffle(day)
print(shuffleday)
The result is None. What's wrong?
random.shuffle does not return anything. It modifies the input list.
import random
day=list(range(1,29))
print(day)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
day=day[3:29]
print(day)
# [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28]
shuffleday=random.shuffle(day)
print(shuffleday)
# None
print(day)
# [27, 9, 14, 15, 7, 17, 28, 10, 23, 21, 16, 12, 6, 11, 22, 25, 24, 20, 5, 19, 13, 4, 18, 8, 26]
random.shuffle modifies day. It does not return anything.
This is per convention in Python, where functions which modify the arguments given to them return None.
If you wish to generate a new list shuffled, without modifying the original, you may wish to use random.sample.
from random import *
day = list(range(1, 29))
shuffleday = sample(day[3:29], len(day2))
print(shuffleday)
If you simply wish to get a random element from a list, you can use random.choice.
from random import *
day = list(range(1, 29))
randomday = choice(day[3:29])
print(randomday)
To get a random number from day list use choice instead of shuffle, it will return a value of random element from day[3:29]:
from random import *
day = list(range(1, 29))
day = day[3:29]
shuffleday = choice(day)
print(shuffleday)
The value of shuffleday is None because the shuffle function does not return a new list, it just changes the order of the elements in the list supplied as an argument.
This will return a value of random number. Choose one:
shuffleday = choice(range(3,29))
or
shuffleday = randint(3,29)
or
shuffleday = randrange(3,29)
in fact the shuffle function does not return anything, it shuffles the elements in place (in day for your case).
try print(day) instead
random.shuffle modifies the list in place, it doesn't return a value. so you can just replace your second last line with
shuffle(day)
then simply print shuffle without making your new variable.
Additionally I would say it's better practice to just import what you need from random, rather than doing a * import and cluttering your namespace (especially considering you are new to the language and unlikely to know the name of every function in the random library). In this case you can do:
from random import shuffle

Refine Probability Distribution Script

I have this python script that helps me with generating random combinations based upon odds, it could be used for harness racing when we have to pick the winning horse in 7 consecutive races.
Now I would like to refine this script a bit more to my wishes if possible. As already said the script now spits out random numbers based upon odds. Now I would like that to be done in a bit more distinguished manner. I don't want combinations with too many numbers corresponding with low odds. In other words, I want the script to select randomly 4 of the 7 races and come back with a random number with higher odds (+20%) for each of the 4 races. For the 3 remaining races it must give me numbers each with less than a 10% probability.
This all goes way beyond my own python knowledge so I was hoping to receive some help here.
Thx in advance for all useful input.
import bpy
import random
scene = bpy.context.scene
def get_random_value(race):
"""
Function which returns a String containing random values based on the given probability distribution
"""
# Generates an Array [1,2,3,...,amount_of_race_participants]
numberList = [x+1 for x in range(len(race))]
temp = ' '
for x in random.choices(numberList, weights=race, k=1):
temp = temp + str(x) + ","
temp1 = temp[0:-1] + ''
return temp1
def generate_text(iteration, race):
"""
Creates a Text Object with random content and a location based on the input value
Also gives race odds to the get_random_value function, which is used for the probability distribution
"""
# Create Curve with text content
font_curve = bpy.data.curves.new(type="FONT",name="Font Curve")
font_curve.body = get_random_value(race)
# Put Curve into Object and link it to the scene
font_obj = bpy.data.objects.new(name="Font Object", object_data=font_curve)
scene.collection.objects.link(font_obj)
# Set location of Object
font_obj.location = (0,-iteration,0)
return font_obj
def generate_n_texts():
"""
Creates a variable amount of Texts objects and returns them
"""
races = [
[5, 58, 4, 4, 8, 3, 3, 2, 5, 1, 6, 1 ],
[3, 4, 6, 5, 19, 4, 4, 2, 2, 21, 11, 19 ],
[12, 3, 6, 14, 6, 8, 4, 3, 18, 2, 1, 23 ],
[15, 4, 23, 4, 29, 4, 3, 5, 8, 5 ],
[9, 2, 12, 3, 4, 43, 5, 4, 3, 7, 1, 7 ],
[3, 3, 2, 4, 3, 61, 3, 2, 7, 11, 1 ],
[10, 3, 13, 31, 6, 14, 6, 9, 4, 2, 2 ]
]
font_objs = []
# enumerate goes through the array race and gives us an element and its index
for iteration, race in enumerate(races):
font_objs.append((generate_text(iteration, race), race))
# Font Objects includes tuples of font objects and their respective probability distribution
# like this [(obj, race), (obj, race), ...]
return font_objs
# Generate font_objs list before definint recalculate_text() so the function can use the list
font_objs = generate_n_texts()
def recalculate_text(scene):
"""
Sets the bodies of the font_objs list to a random number
"""
#Remove seed to get a different number for every frame, even when revisiting a frame
random.seed(scene.frame_current)
for obj, race in font_objs:
obj.data.body = get_random_value(race)
def register():
bpy.app.handlers.frame_change_post.append(recalculate_text)
def unregister():
bpy.app.handlers.frame_change_post.remove(recalculate_text)
register()

Find lists which together contain all values from 0-23 in list of lists python

I have a list of lists. The lists within these list look like the following:
[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23],
[9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]
Every small list has 8 values from 0-23 and there are no value repeats within a small list.
What I need now are the three lists which have the values 0-23 stored. It is possible that there are a couple of combinations to accomplish it but I do only need one.
In this particular case the output would be:
[0,2,5,8,7,12,16,18], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15]
I thought to do something with the order but I'm not a python pro so it is hard for me to handle all the lists within the list (to compare all).
Thanks for your help.
The following appears to work:
from itertools import combinations, chain
lol = [[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]]
for p in combinations(lol, 3):
if len(set((list(chain.from_iterable(p))))) == 24:
print(p)
break # if only one is required
This displays the following:
([0, 2, 5, 8, 7, 12, 16, 18], [1, 3, 4, 17, 19, 6, 13, 23], [9, 22, 21, 10, 11, 20, 14, 15])
If it will always happen that 3 list will form numbers from 0-23, and you only want first list, then this can be done by creating combinations of length 3, and then set intersection:
>>> li = [[0,2,5,8,7,12,16,18], [0,9,18,23,5,8,15,16], [1,3,4,17,19,6,13,23], [9,22,21,10,11,20,14,15], [2,8,23,0,7,16,9,15], [0,5,8,7,9,11,20,16]]
>>> import itertools
>>> for t in itertools.combinations(li, 3):
... if not set(t[0]) & set(t[1]) and not set(t[0]) & set(t[2]) and not set(t[1]) & set(t[2]):
... print t
... break
([0, 2, 5, 8, 7, 12, 16, 18], [1, 3, 4, 17, 19, 6, 13, 23], [9, 22, 21, 10, 11, 20, 14, 15])
Let's do a recursive solution.
We need a list of lists that contain these values:
target_set = set(range(24))
This is a function that recursively tries to find a list of lists that match exactly that set:
def find_covering_lists(target_set, list_of_lists):
if not target_set:
# Done
return []
if not list_of_lists:
# Failed
raise ValueError()
# Two cases -- either the first element works, or it doesn't
try:
first_as_set = set(list_of_lists[0])
if first_as_set <= target_set:
# If it's a subset, call this recursively for the rest
return [list_of_lists[0]] + find_covering_lists(
target_set - first_as_set, list_of_lists[1:])
except ValueError:
pass # The recursive call failed to find a solution
# If we get here, the first element failed.
return find_covering_lists(target_set, list_of_lists[1:])

Attribute Error: Nonetype has no attribute 'group'

Traceback (most recent call last):
File "redact.py", line 100, in <module>
match = int(re.match(r'\d+', number).group())
AttributeError: 'NoneType' object has no attribute 'group'
input = (1, 2)(5, 2)(14, 2)(17, 2)(1, 3)(5, 3)(14, 3)(17, 3)(1, 4)(5, 4)(8, 4)(9, 4)(10, 4)(11, 4)(14, 4)(17, 4)(20, 4)(21, 4)(22, 4)(23, 4)(1, 5)(2, 5)(3, 5)(4, 5)(5, 5)(8, 5)(9, 5)(10, 5)(11, 5)(14, 5)(17, 5)(20, 5)(21, 5)(22, 5)(23, 5)(1, 6)(5, 6)(8, 6)(9, 6)(10, 6)(11, 6)(14, 6)(17, 6)(20, 6)(23, 6)(1, 7)(5, 7)(8, 7)(9, 7)(14, 7)(17, 7)(20, 7)(21, 7)(22, 7)(23, 7)(1, 8)(5, 8)(8, 8)(9, 8)(10, 8)(11, 8)(14, 8)(17, 8)(20, 8)(21, 8)(22, 8)(23, 8)
output = >>>error above
This is the error message I am getting after executing the following code:
xcoord = []
regex = ur"\b[0-9,]{1,}[,]\s\b" #regex for x coordinates
result = re.findall(regex,str1)
for number in result: #get x numbers from coordinate
match = int(re.match(r'\d+', number).group())
xcoord.append(match) #now got array of numbers for x
maxValueX = max(xcoord) #biggest x value
ycoord = []
regex = ur"\b[,]\s[0-9,]{1,}\b" #regex for y coordinates
result = re.findall(regex,str1)
for number in result: #get y numbers from coordinate
match = int(re.match(r'\d+', number).group())
ycoord.append(match) #now got array of numbers for y
maxValueY = max(ycoord) #biggest y value
print maxValueX
print maxValueY
The string it is searching through is: "5', ', 5', ', 6', ', 3',". On two different online regex generators the above regex works perfectly for the string. Why is it working for the X coordinate but NOT the Y one? The code is exactly the same!
(I am trying to obtain the integers from the string by the way).
Thanks
No regex needed:
str1 = "(8, 5)(9, 5)(10, 5)(11, 5)(14, 5)(17, 5)(20, 5)(21, 5)(22, 5)(23, 5)(1, 6)(5, 6)(8, 6)(9, 6)(10, 6)(11, 6)(14, 6)(17, 6)(20, 6)(23, 6)(1, 7)(5, 7)(8, 7)(9, 7)(14, 7)(17, 7)(20, 7)(21, 7)(22, 7)(23, 7)(1, 8)(5, 8)(8, 8)(9, 8)(10, 8)(11, 8)(14, 8)(17, 8)(20, 8)(21, 8)(22, 8)(23, 8)"
xcoord = [int(element.split(",")[0].strip()) for element in str1[1:-1].split(")(")]
ycoord = [int(element.split(",")[1].strip()) for element in str1[1:-1].split(")(")]
maxValueX = max(xcoord); maxValueY = max(ycoord)
print maxValueX;
print maxValueY;
What is the content of variable str1 ???
re.match(r'\d+', number) is returning None because number does not match with your regular expression, review the content of the variable result that depends on str1.
Each regular expression has to be tested step by step, try some regex web tool like this to test them.
Change your regex to: regex = "\b([0-9]+)\,\s\b"
This will create two lists one with all first elements of and one with all the second.
x_cord = map(int,re.findall("(?<=\()\d+",str1))
y_cord= map(int,re.findall("(?<=\,\s)\d+",str1))
x_cord
[8, 9, 10, 11, 14, 17, 20, 21, 22, 23, 1, 5, 8, 9, 10, 11, 14, 17, 20, 23, 1, 5, 8, 9, 14, 17, 20, 21, 22, 23, 1, 5, 8, 9, 10, 11, 14, 17, 20, 21, 22, 23]
y_cord
[5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8]

Sample k random permutations without replacement in O(N)

I need a number of unique random permutations of a list without replacement, efficiently. My current approach:
total_permutations = math.factorial(len(population))
permutation_indices = random.sample(xrange(total_permutations), k)
k_permutations = [get_nth_permutation(population, x) for x in permutation_indices]
where get_nth_permutation does exactly what it sounds like, efficiently (meaning O(N)). However, this only works for len(population) <= 20, simply because 21! is so mindblowingly long that xrange(math.factorial(21)) won't work:
OverflowError: Python int too large to convert to C long
Is there a better algorithm to sample k unique permutations without replacement in O(N)?
Up to a certain point, it's unnecessary to use get_nth_permutation to get permutations. Just shuffle the list!
>>> import random
>>> l = range(21)
>>> def random_permutations(l, n):
... while n:
... random.shuffle(l)
... yield list(l)
... n -= 1
...
>>> list(random_permutations(l, 5))
[[11, 19, 6, 10, 0, 3, 12, 7, 8, 16, 15, 5, 14, 9, 20, 2, 1, 13, 17, 18, 4],
[14, 8, 12, 3, 5, 20, 19, 13, 6, 18, 9, 16, 2, 10, 4, 1, 17, 15, 0, 7, 11],
[7, 20, 3, 8, 18, 17, 4, 11, 15, 6, 16, 1, 14, 0, 13, 5, 10, 9, 2, 19, 12],
[10, 14, 5, 17, 8, 15, 13, 0, 3, 16, 20, 18, 19, 11, 2, 9, 6, 12, 7, 4, 1],
[1, 13, 15, 18, 16, 6, 19, 8, 11, 12, 10, 20, 3, 4, 17, 0, 9, 5, 2, 7, 14]]
The odds are overwhelmingly against duplicates appearing in this list for len(l) > 15 and n < 100000, but if you need guarantees, or for lower values of len(l), just use a set to record and skip duplicates if that's a concern (though as you've observed in your comments, if n gets close to len(l)!, this will stall). Something like:
def random_permutations(l, n):
pset = set()
while len(pset) < n:
random.shuffle(l)
pset.add(tuple(l))
return pset
However, as len(l) gets longer and longer, random.shuffle becomes less reliable, because the number of possible permutations of the list increases beyond the period of the random number generator! So not all permutations of l can be generated that way. At that point, not only do you need to map get_nth_permutation over a sequence of random numbers, you also need a random number generator capable of producing every random number between 0 and len(l)! with relatively uniform distribution. That might require you to find a more robust source of randomness.
However, once you have that, the solution is as simple as Mark Ransom's answer.
To understand why random.shuffle becomes unreliable for large len(l), consider the following. random.shuffle only needs to pick random numbers between 0 and len(l) - 1. But it picks those numbers based on its internal state, and it can take only a finite (and fixed) number of states. Likewise, the number of possible seed values you can pass to it is finite. This means that the set of unique sequences of numbers it can generate is also finite; call that set s. For len(l)! > len(s), some permutations can never be generated, because the sequences that correspond to those permutations aren't in s.
What are the exact lengths at which this becomes a problem? I'm not sure. But for what it's worth, the period of the mersenne twister, as implemented by random, is 2**19937-1. The shuffle docs reiterate my point in a general way; see also what Wikipedia has to say on the matter here.
Instead of using xrange simply keep generating random numbers until you have as many as you need. Using a set makes sure they're all unique.
permutation_indices = set()
while len(permutation_indices) < k:
permutation_indices.add(random.randrange(total_permutations))
I had one implementation of nth_permutation (not sure from where I got it) which I modified for your purpose. I believe this would be fast enough to suit your need
>>> def get_nth_permutation(population):
total_permutations = math.factorial(len(population))
while True:
temp_population = population[:]
n = random.randint(1,total_permutations)
size = len(temp_population)
def generate(s,n,population):
for x in range(s-1,-1,-1):
fact = math.factorial(x)
d = n/fact
n -= d * fact
yield temp_population[d]
temp_population.pop(d)
next_perm = generate(size,n,population)
yield [e for e in next_perm]
>>> nth_perm = get_nth_permutation(range(21))
>>> [next(nth_perm) for k in range(1,10)]
You seem to be searching for the Knuth Shuffle! Good luck!
You could use itertools.islice instead of xrange():
CPython implementation detail: xrange() is intended to be simple and
fast Implementations may impose restrictions to achieve this. The C
implementation of Python restricts all arguments to native C longs
(“short” Python integers), and also requires that the number of
elements fit in a native C long. If a larger range is needed, an
alternate version can be crafted using the itertools module:
islice(count(start, step), (stop-start+step-1+2*(step<0))//step).

Categories