Refine Probability Distribution Script - python

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

Related

Picking random items from a list twice?

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}')

my list value changing incorrectly - python

im trying to generate mastercard card number.
requirements :
first element must be 5
second element must be between 1 and 5
last element must be lcheck digit returned from luhn algorithm.
i have check digit function with luhn algorithm, so far everything is okay.
but when i give parameter my card number to generateCheckDigit function in generateMasterCard function, my card number is returned as multiplied by 2, one element apart during the luhn algorithm.
sorry for my bad english
here is the codes:
def generateCheckDigit(numbers):
if len(numbers)%2 == 1:
for i in range(0,len(numbers),2):
numbers[i] *= 2
else:
for i in range(1,len(numbers),2):
numbers[i] *= 2
check_digit = (sum(numbers)*9) % 10
return check_digit
def generateMasterCard():
card_number = [5, rd.randint(1,5)]
for i in range(13):
card_number.append(rd.randint(0,9))
print(f"first number : {card_number}")
check_digit = generateCheckDigit(card_number)
card_number.append(check_digit)
return card_number
output :
first number : [5, 4, 1, 4, 0, 8, 4, 8, 0, 4, 2, 8, 8, 2, 9]
[10, 4, 2, 4, 0, 8, 8, 8, 0, 4, 4, 8, 16, 2, 18, 4]
You can import copy and use generateCheckDigit(copy.copy(card_number)) as
Alexey Larionov sais in comments "In Python if you pass to a function some complicated value, like class instance, list, dictionary, etc, then your function can freely modify it. In your case, you do operation numbers[i] *= 2 and it changes the list you passed". Passing a copy allows you to avoid this.

Is there a way to print the contents added to a knapsack for this 0/1 Greedy algorithm?

My most recent lab assignment has me trying to implement a Greedy algorithm for the 0/1 Knapsack problem, and print out the contents of the knapsack along with the total value of the knapsack. So far, I was able to get it to output the total value of the knapsack without issue, but I'm having trouble with outputting what items went into the knapsack.
#class definitions for the greedy approach
class Item:
def __init__(self,weight,value):
self.weight = weight
self.value = value
self.price_kg = value / weight
def __repr__(self):
return f"Item(weight={self.weight}, value={self.value},v/w={self.price_kg})\n"
class Knapsack:
def __init__(self,max_weight,items):
self.max_weight = max_weight
self.items = items
self.contents = list()
def fillGreedy(self):
self.items.sort(key=lambda x: x.price_kg, reverse=True)#sorts the items by weight/value
for i in self.items:
self.contents.append(i)#Tries putting the item in the bag
if sum(i.weight for i in self.contents) > self.max_weight:
self.contents.remove(i)#Removes the item it is too heavy for the bag
elif sum(i.weight for i in self.contents) == self.max_weight:#finds an optimal configuration for the bag
return sum(i.value for i in self.contents)
return sum(i.value for i in self.contents)
#main method
max_weights = [10, 13, 15, 30, 30]
weights = [
[4, 5, 7],
[6, 5, 7, 3, 1],
[2, 3, 5, 5, 3, 7],
[10, 13, 17, 15],
[5, 4, 7, 6, 3, 4, 2, 1, 7, 6]
]
values = [
[2, 3, 4],
[7, 3, 4, 4, 3],
[3, 4, 10, 9, 6, 13],
[21, 17, 30, 23],
[3, 1, 3, 2, 1, 3, 2, 3, 1, 4]
]
for i in range(len(max_weights)):
items = list()
for j in range(len(weights[i])):
items.append(Item(weights[i][j], values[i][j])) #adds the contents of the arrays to the Items list
i
ks = Knapsack(max_weights[i], items)
v1 = ks.fillGreedy()
print(f"Total value = {v1}")
#print(items)
So far, I tried printing out the contents of the ks and v1 objects, but that only gives the memory addresses of the objects. I tried printing out the 'items' list itself after iterating through the fillGreedy method, but it prints out all the contents of the list and not the ones in the knapsack itself. I also tried doing something in the fillGreedy method that would print the item that was just added, but it ended up causing conflicts. I'm unsure where to continue from here. Is there a way to print out the items of the knapsack using this approach?
Welcome to the site.
You already have a collection of the selected items inside the Knapsack object, so you could iterate over ks.contents and print out the contents or whatever is needed from there...
for item in ks.contents:
print(item)

Finding where a given number falls in a partition

Suppose I have a sorted array of integers say
partition = [0, 3, 7, 12, 18, 23, 27]
and then given a value
value = 9
I would like to return the interval on which my value sits. For example
bounds = function(partition, value)
print(bounds)
>>>[7,12]
Is there a function out there that might be able to help me or do I have to build this from scratch?
Try numpy.searchsorted(). From the documentary:
Find indices where elements should be inserted to maintain order.
import numpy as np
partition = np.array( [0, 3, 7, 12, 18, 23, 27] )
value = 9
idx = np.searchsorted(partition,value)
bound = (partition[idx-1],partition[idx])
print(bound)
>>>>(7,12)
The advantage of searchsorted is that it can give you the index for multiple values at once.
The bisect module is nice for doing this efficiently. It will return the index of the higher bound.
You'll need to do some error checking if the value can fall outside the bounds:
from bisect import bisect
partition = [0, 3, 7, 12, 18, 23, 27]
value = 9
top = bisect(partition, value)
print(partition[top-1], partition[top])
# 7 12
def function(partition,value):
for i in range(len(partition)):
if partition[i]<value and partition[i+1]>value:
print [partition[i],partition[i+1]]
partition = [0, 3, 7, 12, 18, 23, 27,5,10]
value=9
function(partition,value)

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:])

Categories