how to iterate through output of function in Python - python

I'm attempting to calculate the value based on a sequence of numbers, and find out how that value compares to if the sequence of numbers was randomly generated. To calculate the value of the sequence I have the random_lineup_value function, which works fine. To randomly generate 1000 sequences and calculate the value of each I have the random_lineup_values function.
However, the issue is that I can't compare the original value to these randomly generated values, because the output of the function is not subscriptable- or I cannot reference specific positions in this output. I'm trying to iterate through the ordered output of random_lineup_values until the initial stored value is equal to the number stored in the list of 1000 values. I've tried to return the output of random_lineup_value as a list, to use yield instead of return in the function, and to assign the output to a variable when I call the function and then try to iterate through positions of the list stored in the new variable, but none of these approaches work in Python. Any idea how to do this in Python?
import random
def lineup_value(order):
value = 0
for i in range(0,6):
if order[i] >= order[8]:
value = value + 4
if order[i] >= order[7]:
value = value + 4
for i in range(0,5):
if order[i] >= order[6]:
value = value + 4
for i in [0, 1, 3]:
if order[i] >= order[5]:
value = value + 4
if order[i] >= order[4]:
value = value + 4
if order[3] >= order[2]:
value = value + 4
if order[1] >= order[2]:
value = value + 4
return value
def random_lineup_values(order):
bootstrapped_values = []
for j in range(0, 1000):
random.shuffle(order)
bootstrapped_values.append(lineup_value(order))
return list(bootstrapped_values.sort)
random.seed(10)
batting_order = [115, 101, 76, 125, 120, 135, 88, 94, 97]
value = lineup_value(batting_order)
print(str(value) + ' is the value of the lineup')
sorted1 = random_lineup_values(batting_order)
print(sorted1)
p = 0
number = sorted1[p]
while int(number) < int(value):
p = p + 1 print(p)
print(str((1000 - p) / 10) + ' percent of random lineup values would be better than the lineup')

The error is here:
x = list(bootstrapped_values.sort)
by removing .sort it works.
x = list(bootstrapped_values)
here is the function
def random_lineup_values(order):
bootstrapped_values = []
for j in range(0, 1000):
random.shuffle(order)
bootstrapped_values.append(lineup_value(order))
x = list(bootstrapped_values) # <== Exception has occurred: TypeError 'builtin_function_or_method' object is not iterable
return x
The error was this:
Exception has occurred: TypeError 'builtin_function_or_method' object is not iterable

I suspect the issue you have is that you're only sorting and keeping track of the scores, not the order list that produced them. If you keep both things together in the same place, you can do what you want. (You also have a more glaring issue solved by #D.L's answer, but I suspect that issue was caused by an attempted solution to the larger issue which I'm addressing.)
Try this, which builds and sorts a list of (score, order) 2-tuples.
def random_lineup_values(order):
bootstrapped_values = []
for j in range(0, 1000):
random.shuffle(order)
bootstrapped_values.append(lineup_value(order), order.copy())
bootstrapped_values.sort()
return bootstrapped_values
Note that I'm adding a copy() of the order list in the tuple because the shuffling to build new orders happens in place. This may also mean you'll want to copy the original lineup too at the start of the function, so you don't destroy the order of the list you're passed as an argument (but I don't fully understand the calling code, so I'm not sure if you care about that or not).

I didn't initially understand your question, but based on your code, I guessed that you wanted to run a list that contained a batting order through a function that determined that order's "value", and compare that to 1000 randomly generated different order's values. I made a couple of slight adjustments, and now it does what I believe you were trying to accomplish.
import random
def lineup_value(order):
value = 0
for i in range(0,6):
if order[i] >= order[8]:
value = value + 4
if order[i] >= order[7]:
value = value + 4
for i in range(0,5):
if order[i] >= order[6]:
value = value + 4
for i in [0, 1, 3]:
if order[i] >= order[5]:
value = value + 4
if order[i] >= order[4]:
value = value + 4
if order[3] >= order[2]:
value = value + 4
if order[1] >= order[2]:
value = value + 4
return value
def random_lineup_values(order, value):
bootstrapped_values = []
newBootStrapped_values = []
for j in range(0, 1000):
random.shuffle(order)
bootstrapped_values.append(lineup_value(order))
for j in range(0, 1000):
if(bootstrapped_values[j] > value):
newBootStrapped_values.append(bootstrapped_values[j])
return list(sorted(newBootStrapped_values))
random.seed(10)
batting_order = [115, 101, 76, 125, 120, 135, 88, 94, 97]
value = lineup_value(batting_order)
print(str(value) + ' is the value of the original lineup')
sorted1 = random_lineup_values(batting_order, value)
print(str(len(sorted1)) + ' is the number of better value lineups out of 1000')
print(str(len(sorted1) / 10) + ' percent of random lineup values would be better than the lineup')
You'll notice that the biggest adjustment I made was adding an extra for loop in your random_lineup_values function. The first for loop gets the values of the 1000 random orders. The second for loop gets all of the values that are greater than the original order value (which should be 68 from the array that you provided) and appends them to a new list. The number of elements in that list out of 1000 times 100 (sorted1/10, to simplify) will get you the percentage of better batting orders. I did not change any logic for obtaining the batting order value, so if there is something wrong there, it will have to do with the logic of the lineup_value function, but as far as I know, it is functioning correctly. The output that I got after running this code was:
68 is the value of the original lineup
113 is the number of better value lineups out of 1000
11.3 percent of random lineup values would be better than the original lineup

Related

Function which eliminates specific elements from a list in an efficient way

I want to create a function (without using libraries) which takes as input three integer numbers (>0) (a,b,c) , for example:
a = 6
b = 6
c = 3
and returns a list containing c elements (so in this case the returned list should contain 3 elements) taken from a list of a numbers (so in this case the initial list is [1,2,3,4,5,6]). The c elements of the returned list have to be the ones that managed to remain in the initial list after removing a number every b positions from the list of a elements until len(return_list) = c.
So for a = 6, b = 6 and c = 3 the function should do something like this:
1) initial_list = [1,2,3,4,5,6]
2) first_change = [2,3,4,5,6] #the number after the 6th (**b**) is 1 because after the last element you keep counting returning to the first one, so 1 is canceled
3) second_change = [2,4,5,6] #you don't start to count positions from the start but from the first number after the eliminated one, so new number after the 6th is 3 and so 3 is canceled
4) third_change = [2,4,5] #now the number of elements in the list is equal to **c**
Notice that if, when counting, you end up finishing the elements from the list, you keep the counting and return to the first element of the list.
I made this function:
def findNumbers(a,b,c):
count = 0
dictionary = {}
countdown = a
for x in range(1,a+1):
dictionary[x] = True
while countdown > c:
for key,value in dictionary.items():
if value == True:
count += 1
if count == b+1:
dictionary[key] = False
count = 0
countdown -= 1
return [key for key in dictionary.keys() if dictionary[key] == True]
It works in some cases, like the above example. But it doesn't work everytime.
For example:
findNumbers(1000,1,5)
returns:
[209, 465, 721, 977] #wrong result
instead of:
[209, 465, 721, 849, 977] #right result
and for bigger numbers, like:
findNumbers(100000, 200000, 5)
it takes too much time to even do its job, I don't know if the problem is the inefficiency of my algorithm or because there's something in the code that gives problems to Python. I would like to know a different approach to this situation which could be both more efficient and able to work in every situation. Can anyone give me some hints/ideas?
Thank you in advance for your time. And let me know if you need more explanations and/or examples.
You can keep track of the index of the last list item deleted like this:
def findNumbers(a, b, c):
l = list(range(1, a + 1))
i = 0
for n in range(a - c):
i = (i + b) % (a - n)
l.pop(i)
return l
so that findNumbers(6, 6, 3) returns:
[2, 4, 5]
and findNumbers(1000, 1, 5) returns:
[209, 465, 721, 849, 977]
and findNumbers(100000, 200000, 5) returns:
[10153, 38628, 65057, 66893, 89103]
I thought I could be recursive about the problem, so I wrote this:
def func(a,b,c):
d = [i+1 for i in range(a)]
def sub(d,b,c):
if c == 0: return d
else:
k = b % len(d)
d.pop(k)
d = d[k:] + d[:k]
return sub(d,b,c-1)
return sub(d,b,a-c)
so that func(6,6,3) returns: [2, 4, 5] successfully and func(1000,1,5) returns: [209, 465, 721, 849, 977] unfortunately with an error.
It turns out that for values of a > 995, the below flag is raised:
RecursionError: maximum recursion depth exceeded while calling a Python object
There was no need to try func(100000,200000,5) - lesson learnt.
Still, rather than dispose of the code, I decided to share it. It could serve as a recursive thinking precautionary.

Fill order from smaller packages?

The input is an integer that specifies the amount to be ordered.
There are predefined package sizes that have to be used to create that order.
e.g.
Packs
3 for $5
5 for $9
9 for $16
for an input order 13 the output should be:
2x5 + 1x3
So far I've the following approach:
remaining_order = 13
package_numbers = [9,5,3]
required_packages = []
while remaining_order > 0:
found = False
for pack_num in package_numbers:
if pack_num <= remaining_order:
required_packages.append(pack_num)
remaining_order -= pack_num
found = True
break
if not found:
break
But this will lead to the wrong result:
1x9 + 1x3
remaining: 1
So, you need to fill the order with the packages such that the total price is maximal? This is known as Knapsack problem. In that Wikipedia article you'll find several solutions written in Python.
To be more precise, you need a solution for the unbounded knapsack problem, in contrast to popular 0/1 knapsack problem (where each item can be packed only once). Here is working code from Rosetta:
from itertools import product
NAME, SIZE, VALUE = range(3)
items = (
# NAME, SIZE, VALUE
('A', 3, 5),
('B', 5, 9),
('C', 9, 16))
capacity = 13
def knapsack_unbounded_enumeration(items, C):
# find max of any one item
max1 = [int(C / item[SIZE]) for item in items]
itemsizes = [item[SIZE] for item in items]
itemvalues = [item[VALUE] for item in items]
# def totvalue(itemscount, =itemsizes, itemvalues=itemvalues, C=C):
def totvalue(itemscount):
# nonlocal itemsizes, itemvalues, C
totsize = sum(n * size for n, size in zip(itemscount, itemsizes))
totval = sum(n * val for n, val in zip(itemscount, itemvalues))
return (totval, -totsize) if totsize <= C else (-1, 0)
# Try all combinations of bounty items from 0 up to max1
bagged = max(product(*[range(n + 1) for n in max1]), key=totvalue)
numbagged = sum(bagged)
value, size = totvalue(bagged)
size = -size
# convert to (iten, count) pairs) in name order
bagged = ['%dx%d' % (n, items[i][SIZE]) for i, n in enumerate(bagged) if n]
return value, size, numbagged, bagged
if __name__ == '__main__':
value, size, numbagged, bagged = knapsack_unbounded_enumeration(items, capacity)
print(value)
print(bagged)
Output is:
23
['1x3', '2x5']
Keep in mind that this is a NP-hard problem, so it will blow as you enter some large values :)
You can use itertools.product:
import itertools
remaining_order = 13
package_numbers = [9,5,3]
required_packages = []
a=min([x for i in range(1,remaining_order+1//min(package_numbers)) for x in itertools.product(package_numbers,repeat=i)],key=lambda x: abs(sum(x)-remaining_order))
remaining_order-=sum(a)
print(a)
print(remaining_order)
Output:
(5, 5, 3)
0
This simply does the below steps:
Get value closest to 13, in the list with all the product values.
Then simply make it modify the number of remaining_order.
If you want it output with 'x':
import itertools
from collections import Counter
remaining_order = 13
package_numbers = [9,5,3]
required_packages = []
a=min([x for i in range(1,remaining_order+1//min(package_numbers)) for x in itertools.product(package_numbers,repeat=i)],key=lambda x: abs(sum(x)-remaining_order))
remaining_order-=sum(a)
print(' '.join(['{0}x{1}'.format(v,k) for k,v in Counter(a).items()]))
print(remaining_order)
Output:
2x5 + 1x3
0
For you problem, I tried two implementations depending on what you want, in both of the solutions I supposed you absolutely needed your remaining to be at 0. Otherwise the algorithm will return you -1. If you need them, tell me I can adapt my algorithm.
As the algorithm is implemented via dynamic programming, it handles good inputs, at least more than 130 packages !
In the first solution, I admitted we fill with the biggest package each time.
I n the second solution, I try to minimize the price, but the number of packages should always be 0.
remaining_order = 13
package_numbers = sorted([9,5,3], reverse=True) # To make sure the biggest package is the first element
prices = {9: 16, 5: 9, 3: 5}
required_packages = []
# First solution, using the biggest package each time, and making the total order remaining at 0 each time
ans = [[] for _ in range(remaining_order + 1)]
ans[0] = [0, 0, 0]
for i in range(1, remaining_order + 1):
for index, package_number in enumerate(package_numbers):
if i-package_number > -1:
tmp = ans[i-package_number]
if tmp != -1:
ans[i] = [tmp[x] if x != index else tmp[x] + 1 for x in range(len(tmp))]
break
else: # Using for else instead of a boolean value `found`
ans[i] = -1 # -1 is the not found combinations
print(ans[13]) # [0, 2, 1]
print(ans[9]) # [1, 0, 0]
# Second solution, minimizing the price with order at 0
def price(x):
return 16*x[0]+9*x[1]+5*x[2]
ans = [[] for _ in range(remaining_order + 1)]
ans[0] = ([0, 0, 0],0) # combination + price
for i in range(1, remaining_order + 1):
# The not found packages will be (-1, float('inf'))
minimal_price = float('inf')
minimal_combinations = -1
for index, package_number in enumerate(package_numbers):
if i-package_number > -1:
tmp = ans[i-package_number]
if tmp != (-1, float('inf')):
tmp_price = price(tmp[0]) + prices[package_number]
if tmp_price < minimal_price:
minimal_price = tmp_price
minimal_combinations = [tmp[0][x] if x != index else tmp[0][x] + 1 for x in range(len(tmp[0]))]
ans[i] = (minimal_combinations, minimal_price)
print(ans[13]) # ([0, 2, 1], 23)
print(ans[9]) # ([0, 0, 3], 15) Because the price of three packages is lower than the price of a package of 9
In case you need a solution for a small number of possible
package_numbers
but a possibly very big
remaining_order,
in which case all the other solutions would fail, you can use this to reduce remaining_order:
import numpy as np
remaining_order = 13
package_numbers = [9,5,3]
required_packages = []
sub_max=np.sum([(np.product(package_numbers)/i-1)*i for i in package_numbers])
while remaining_order > sub_max:
remaining_order -= np.product(package_numbers)
required_packages.append([max(package_numbers)]*np.product(package_numbers)/max(package_numbers))
Because if any package is in required_packages more often than (np.product(package_numbers)/i-1)*i it's sum is equal to np.product(package_numbers). In case the package max(package_numbers) isn't the one with the samllest price per unit, take the one with the smallest price per unit instead.
Example:
remaining_order = 100
package_numbers = [5,3]
Any part of remaining_order bigger than 5*2 plus 3*4 = 22 can be sorted out by adding 5 three times to the solution and taking remaining_order - 5*3.
So remaining order that actually needs to be calculated is 10. Which can then be solved to beeing 2 times 5. The rest is filled with 6 times 15 which is 18 times 5.
In case the number of possible package_numbers is bigger than just a handful, I recommend building a lookup table (with one of the others answers' code) for all numbers below sub_max which will make this immensely fast for any input.
Since no declaration about the object function is found, I assume your goal is to maximize the package value within the pack's capability.
Explanation: time complexity is fixed. Optimal solution may not be filling the highest valued item as many as possible, you have to search all possible combinations. However, you can reuse the possible optimal solutions you have searched to save space. For example, [5,5,3] is derived from adding 3 to a previous [5,5] try so the intermediate result can be "cached". You may either use an array or you may use a set to store possible solutions. The code below runs the same performance as the rosetta code but I think it's clearer.
To further optimize, use a priority set for opts.
costs = [3,5,9]
value = [5,9,16]
volume = 130
# solutions
opts = set()
opts.add(tuple([0]))
# calc total value
cost_val = dict(zip(costs, value))
def total_value(opt):
return sum([cost_val.get(cost,0) for cost in opt])
def possible_solutions():
solutions = set()
for opt in opts:
for cost in costs:
if cost + sum(opt) > volume:
continue
cnt = (volume - sum(opt)) // cost
for _ in range(1, cnt + 1):
sol = tuple(list(opt) + [cost] * _)
solutions.add(sol)
return solutions
def optimize_max_return(opts):
if not opts:
return tuple([])
cur = list(opts)[0]
for sol in opts:
if total_value(sol) > total_value(cur):
cur = sol
return cur
while sum(optimize_max_return(opts)) <= volume - min(costs):
opts = opts.union(possible_solutions())
print(optimize_max_return(opts))
If your requirement is "just fill the pack" it'll be even simpler using the volume for each item instead.

Python loop returning wrong value when multiplying digits in a string

This function is supposed to take a string of numbers(snum) and then the index it is supposed to start at (indx) and then starting at that (indx) and multiply the next (dig) amount of numbers and return the value. This is current funciton should return 72 but it is returning 41472. Thank you!
def product(dig, indx, snum):
length = int(len(snum))
int(indx)
int(dig)
total = int(snum[indx])
for k in range((indx + 1), length):
for i in range(0, dig):
total = total * int(snum[k])
else:
return total
x = product(3, 5, '72890346')
print(x)
Following should do it :
def product(dig, indx, snum):
mul = 1
for s in snum[indx : indx+dig+1]: #Note the `dig+1`
mul *= int(s) #multiply the number
return mul
Driver code :
x = product(3, 5, '72890346')
print(x)
#72
In your code, the logic has few problems. You do not need two loops. Here, we are using slicing operation to get characters between indx and indx+dig, and then converting the string we got to int and multiplying.

Building MIN-HEAP in Python

I've got to build a complete MIN-HEAP implementation in Python, without using built-in heap functions.
So I've got definitions of parent, left child and right child, which take in account that python number list elements from 0:
from random import randint
import math
def parent(i):
x = int(l.index(l[i])) #########3
y = int(math.floor(x/2))
return y-1
def lchild(i):
x = int(l.index(l[i]))
y = int(math.floor(x*2))
return y-1
def rchild(i):
x = int(l.index(l[i]))
y = int(math.floor(x*2 + 1))
return y-1
then I have a part of code that generates (pseudo)random list for me into the list l:
l = []
dl = int(input)
for i in range (0, dl):
x = int(randint(1,100))
l.append(x)
and until this point everything works good. then I have a function bkop for making the table l into a min-heap.
def bkop(l):
j = 0
for i in range(0, len(l)):
if int(l[i]) < int(parent(l[i])): #########2
l[i], l[parent(i)] = l[parent(i)], l[i]
j = j+1
if j != 0:
bkop(l)
then I want to run a program and see the results:
bkop(l) #########1
print l
The program crashes with an error list index out of range pointing to the 3 lines, that i've marked with #########. I've started writing this about a month ago and i'm pretty sure, that parent, lchild and rchild worked at that time. Do you know, what's wrong?
EDIT1:
Ok, so I've fixed the parent/lchild/rchild definitions. I've checked, they return correct values. Here is the code:
def parent(i):
x = i + 1
y = x//2
return y-1
def lchild(i):
x = i + 1
y = x*2
return y-1
def rchild(i):
x = i + 1
y = x*2 + 1
return y-1
The function generating random list is pretty much intact. Then I have this bkop function (that makes a min-heap out of the l list). I use print before and after on purpose, to se if it works... and it doesn't. It returns the same list both times, no compile errors or anything. Any idea how to fix it?
print(l)
def bkop(l):
j = 0
for i in range(0, len(l)):
if l[i] < parent(i):
l[i], l[parent(i)] = l[parent(i)], l[i]
j = j+1
if j != 0:
bkop(l)
bkop(l)
print l
EDIT2:
Ok, so I've fixed bkop as you've suggested:
print bkop(l)
def bkop(l):
j = 0
for i in range(1, len(l)):
if l[i] < l[parent(i)]:
l[i], l[parent(i)] = l[parent(i)], l[i]
j = j+1
if j != 0:
bkop(l)
bkop(l)
print bkop(l)
But when I run it, I get this first a randomly-generated table (as I should), and instead of a min-heap table, I get None value, as below:
[34, 9, 94, 69, 77, 33, 56]
None
Any ideas? Maybe I should do it another way around, not comparing l[i] to parent, but l[i] to it's left and right children?
So I've got the whole thing working with the help from Wombatz (Thank you very much!). Here is the working code for the future users:
from random import randint
def parent(i):
x = i + 1
y = x//2
return y-1
def lchild(i):
x = i + 1
y = x*2
return y-1
def rchild(i):
x = i + 1
y = x*2 + 1
return y-1
l = []
dl = int(input())
for i in range (0, dl):
x = int(randint(1,100))
l.append(x)
print l
def bkop(l):
j = 0
for i in range(1, len(l)):
if l[i] < l[parent(i)]:
l[i], l[parent(i)] = l[parent(i)], l[i]
j = j+1
if j != 0:
bkop(l)
bkop(l)
print l
When run, I've put 13 in input for the list length and i've got the result:
13
[30, 62, 9, 100, 75, 73, 57, 82, 2, 76, 2, 50, 41] #before heapify
[2, 2, 30, 62, 9, 41, 57, 100, 82, 76, 75, 73, 50] #after heapify
At first: there is a problem with your parent, lchild and rchild functions:
l[index] gets the value for a given index.
l.index(...) gets the index for a given value.
Your are trying to get the index for a value at a specific index. That's like x + 1 - 1. If your items are unique then that computed index will always be the same as the index you started with. When there are duplicates your functions will calculate the parent, left and right child of the first occurence of the value at that index.
That is probably not what you want. So set x to i or remove the x completely.
The actual problem is the following:
Your parent, lchild and rchild functions are desined to work on an index but you are passing the value of l at index i to the function: parent(l[i])
Since the values in the list may be much higher than the possible index range you are getting list index out of range. You probably want to pass the index directly.
Furthermore:
parent, lchild and rchild return incorrect values. Test these function without all the other stuff!
I assume the results should look like this:
parent(1) == parent(2) == 0
lchild(0) == 1
rchild(0) == 2
Your definition returns different values.
Some minor things:
All these random int casts are useless. Casting an int to int does nothing. The only line which really need the cast is: ``dl = int(input)`
int(math.floor(x/2)). Use integer division x // 2 instead
int(math.floor(x*2)). Integer times 2 is an integer. No need to floor it.
Edit
Two things are wrong with your bkop function.
l[i] < parent(i) You compare the value to an index. I think you want to campare the value at i to its parent value, not the parent index.
for i == 0 you compare the value at index 0 to the value at parent(0) == -1. This wraps around the list and is probably not what you want. As index 0 has no parent you shouldn't try to compare it to its parent.
Edit2
bkop does not return the list but modifies it in-place. Just print(l) at the end. You will see that the original list was modified.

Python - List Binary Search without bisect

I have a list and I want to binary_search a key(number).
My code is below but I don't have a clue what to do where the bold text on code is:
(What to do with this? Is an other function? int imid = midpoint(imin, imax))
List = []
x = 1
#Import 20 numbers to list
for i in range (0,20):
List.append (i)
print (List)
key = input("\nGive me a number for key: ")
def midpoint(imin, imax):
return point((imin+imax)/2)
def binary_search(List,key,imin,imax,point):
while (imax >= imin):
int imid = midpoint(imin, imax)
if(List[imid] == key):
return imid;
elif (List[imid] < key):
imin = imid + 1;
else:
imax = imid - 1;
return KEY_NOT_FOUND;
print (binary_search(key))
midpoint(imin, imax)
binary_search(List,key,imin,imax,point)
It doesn't seem to be doing anything for you; remove the call to midpoint, and point, and just have
def binary_search(List,key,imin,imax,point):
while (imax >= imin):
imid = (imin + imax) / 2
(However, there are some things wrong with your code, and it won't work with just that change;
You create a list called List then try to append to an uninitialized variable called myList
You 'import 20 random' numbers, but range() is not random, it's a simple sequence 1, 2, 3, 4...
range already returns a list, no need to count through it and copy it, just use it
You call binary_search with an empty List, a key, and three uninitialized variables
binary_search assumes the list is sorted, which it is, but if the comment about 'random numbers' was correct, it wouldn't be.
)

Categories

Resources