How I can sum numbers until max height? [closed] - python

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 4 years ago.
Improve this question
There is height list it's total 1743 cm. I have 6 cells I want to put this numbers in cells. Max height is 300 cm. I use for loop for it it will take numbers inside height list and plus them until get closer 300 cm
height=[67, 67, 55, 65, 65, 65, 61, 58, 40, 40, 58, 53, 59, 63, 51, 57, 43, 65, 45, 65, 61, 58, 47, 58, 65, 74, 64, 28, 61, 46, 39]
max_height=300 #cm
sum1=0
count=0
for i in height:
sum1=height[count]+sum1
count+=1
if max_height>=sum1>=250:
print(sum1)
sum1=0
print(sum1)
print("\n",sum(height))
I expected it will get 6 sum but get 7 sum.
if you run you will see (254,289,273,261,289,292,85)

It is not full solution but i hope it can help you, main idea is find most nearest value to generate summ 300
height = [67, 67, 55, 65, 65, 65, 61, 58, 40, 40, 58, 53, 59, 63, 51, 57, 43, 65, 45, 65, 61, 58, 47, 58, 65, 74, 64, 28, 61, 46, 39]
elems = [height.pop(0)]
groups = []
while height:
while sum(elems) <= 300:
next_el = 0
for elem in height:
if sum(elems) + elem <= 300 and elem > next_el:
next_el = elem
if next_el:
elems.append(height.pop(height.index(next_el)))
else:
print(sum(elems))
print(elems)
groups.append(elems)
elems = []
break
print('Total: ', sum([sum(x) for x in groups]))
my result is:
273
[67, 74, 67, 65]
300
[65, 65, 65, 65, 40]
300
[65, 64, 63, 61, 47]
297
[61, 61, 59, 58, 58]
281
[58, 58, 57, 55, 53]
292
[51, 46, 45, 43, 40, 39, 28]
Total: 1743

The problem you are trying to solve is computationally complex. It looks like a variant of the Knapsack problem. This is a greedy approach, but it is fairly fast. Because it's greedy it is possible that it may fail to find a solution if one exists.
eg. [120, 120, 100, 80, 65, 65] with max height of 300 has a minimal solution of [[120, 100, 80], [120, 65, 65]], but this algorithm fails to find it, and instead returns [[120, 120], [100, 80, 65], [65]]
height=[67, 67, 55, 65, 65, 65, 61, 58, 40, 40, 58, 53, 59, 63, 51, 57, 43, 65, 45,
65, 61, 58, 47, 58, 65, 74, 64, 28, 61, 46, 39]
max_height=300 #cm
heights = sorted(height, reverse=True)
groups = []
while heights:
# whilst there are still items in heights, create a new group of heights
# with sum no more than 300.
group = []
# greedily fill up each group with largest possible values that can fit
for h in heights:
if sum(group) + h <= 300:
group.append(h)
# remove the values in the group from the list of candidate heights
for g in group:
heights.remove(g)
groups.append(tuple(group))
# output
for g in groups:
print(g, '->', sum(g))
gives:
(74, 67, 67, 65) -> 273
(65, 65, 65, 65, 40) -> 300
(65, 64, 63, 61, 47) -> 300
(61, 61, 59, 58, 58) -> 297
(58, 58, 57, 55, 53) -> 281
(51, 46, 45, 43, 40, 39, 28) -> 292
Note that the first group it found is actually the worst in terms of how close to the limit it is. This goes back to the initial point about this algorithm being greedy and not always finding the solution with the minimum number of groups.

This is an algorithmic problem.
The gotcha here seems to be that if you fill up one cell at a time then your values that may not fit in the latest cell may still fit in a previous cell. So you are not taking full advantage of each cell's size, for example, your first cell contains just 254 which means you can fit a whole extra 46 cms in there (which appears later in your list).
To solve it you need to actually create a representation for the cells (a list and/or counter, although you can always create a sum of the list...), and revisit cells to check if more data fits.

you need to find the combinations that are 6 elements long, and then you want to get is as close to 300 as possible
height=[67, 67, 55, 65, 65, 65, 61, 58, 40, 40, 58, 53, 59, 63, 51, 57, 43, 65, 45, 65, 61, 58, 47, 58, 65, 74, 64, 28, 61, 46, 39]
valid_combs = []
import itertools
for comb in itertools.combinations(height, 6):
#if sum(comb) <= 300 and sum(comb) >+ 290:
if sum(comb) == 300:
valid_combs.append(comb)
print(comb)
this will extract valid 6 length combinations from the set, and then check if it sums to 300. the commented out condition above it is if you want to have it within a range, such as 290 to 300 etc
you can modify the condition as necessary, such as if sum(comb) >= 250

I may have misunderstood the problem initially. But reconsidering the input, your goal is to group the data such that the sum of an unspecified length does not exceed some threshold. Using numpy this may be a good place to start:
edit: this assumes the order of the data needs to preserved, otherwise I would use a quantile based approach.
edit2: without order preservation
import numpy as np
height = np.array(\
[67, 67, 55, 65, 65, 65, 61, 58, 40, 40,\
58, 53, 59, 63, 51, 57, 43, 65, 45, 65,\
61, 58, 47, 58, 65, 74, 64, 28, 61, 46, 39])
height.sort()
threshold = 300
groupings = np.where(np.diff(height.cumsum() // threshold))[0]
ends = np.hstack((groupings, height.size))
starts = np.roll(ends.copy(), 1) + 1
starts[0] = 0
for start, end in zip(starts, ends):
print(f'Grouping: {height[start:end]} sum: {height[start:end].sum()}')
Output:
Grouping: [28 39 40 40 43 45] sum: 235
Grouping: [47 51 53 55] sum: 206
Grouping: [58 58 58 58 59] sum: 291
Grouping: [61 61 63] sum: 185
Grouping: [65 65 65 65] sum: 260
Grouping: [65 67 67 74] sum: 273

My approach is to find and use combinations that fill cells with exactly 300 total height. It is not optimal.
import itertools
height=[67, 67, 55, 65, 65, 65, 61, 58, 40, 40, 58, 53, 59, 63, 51, 57, 43, 65, 45, 65, 61, 58, 47, 58, 65, 74, 64, 28, 61, 46, 39]
def fill_cell(cells, comb):
cells.append(comb) #fill cell
for h in comb:
del height[height.index(h)] #delete elemets from heights array
cells = []
When first required combination is found - I fill_cell and delete used elements from height array.
As we don't have number of items per cell restriction - I will vary this number.
In this case there is combination of 7 items with total height sum - 300:
for comb in itertools.combinations(height, 7):
if sum(comb) == 300:
print(comb) #(55, 40, 40, 53, 45, 28, 39)
fill_cell(cells,comb)
print(len(height)) #24
break
After that I found few more combinations of 5 item and with 300 total height.
for comb in itertools.combinations(height, 5):
if sum(comb) == 300:
print(comb) #(67, 67, 65, 58, 43)
fill_cell(cells,comb)
print(len(height)) #19
break
for comb in itertools.combinations(height, 5):
if sum(comb) == 300:
print(comb)
fill_cell(cells,comb) #(65, 65, 61, 58, 51)
print(len(height)) #14
break
for comb in itertools.combinations(height, 5):
if sum(comb) == 300:
print(comb)
fill_cell(cells,comb) #(59, 63, 57, 47, 74)
print(len(height)) # 9
break
At this point I have 4 cells with directly 300 total height.
I didn't find more combinations with directly 300 total height.
I decided to manually define remaining cells:
print(cells)
print(height)
fill_cell(cells,height[:4])
fill_cell(cells,height[:])
print("Result:")
for c in cells:
print(sum(c), c)
print("Total height: ", sum([sum(c) for c in cells]))
Finally:
Result:
300 (55, 40, 40, 53, 45, 28, 39)
300 (67, 67, 65, 58, 43)
300 (65, 65, 61, 58, 51)
300 (59, 63, 57, 47, 74)
249 [65, 65, 61, 58]
294 [58, 65, 64, 61, 46]
Total height: 1743
Update:
Following code do the same. It iteratively changes combination parameters and reduces height limit if combination with 300 height didn't found:
import itertools
height=[67, 67, 55, 65, 65, 65, 61, 58, 40, 40, 58, 53, 59, 63, 51, 57, 43, 65, 45, 65, 61, 58, 47, 58, 65, 74, 64, 28, 61, 46, 39]
def fill_cell(cells, comb):
cells.append(comb) #fill cell
for h in comb:
del height[height.index(h)] #delete elemets from heights array
cells = []
max_height = 300
max_elements = 7
elements = max_elements
cell_filled = True
while height:
if cell_filled == False:
elements-=1
if elements==0:
elements = max_elements
max_height-=1
for comb in itertools.combinations(height, min(elements,len(height))):
if sum(comb) == max_height:
print(comb)
fill_cell(cells, comb)
print(len(height))
cell_filled = True
break
cell_filled = False
print("Result:")
for c in cells:
print(sum(c), c)
print("Total height: ", sum([sum(c) for c in cells]))

Related

Modifying alternate indices of 3d numpy array

I have a numpy array with shape (140, 23, 2) being 140 frames, 23 objects, and x,y locations. The data has been generated by a GAN and when I animate the movement it's very jittery. I want to smooth it by converting the coordinates for each object so every odd number index to be the mid-point between the even numbered indices either side of it. e.g.
x[1] = (x[0] + x[2]) / 2
x[3] = (x[2] + x[4]) / 2
Below is my code:
def smooth_coordinates(df):
# df shape is (140, 23, 2)
# iterate through each object (23)
for j in range(len(df[0])):
# iterate through 140 frames
for i in range(len(df)):
# if it's an even number and index allows at least 1 index after it
if (i%2 != 0) and (i < (len(df[0])-2)):
df[i][j][0] = ( (df[i-1][j][0]+df[i+1][j][0]) /2 )
df[i][j][1] = ( (df[i-1][j][1]+df[i+1][j][1]) /2 )
return df
Aside from it being very inefficient my input df and output df are identical. Any suggestions for how to achieve this more efficiently?
import numpy as np
a = np.random.randint(100, size= [140, 23, 2]) # input array
b = a.copy()
i = np.ogrid[1: a.shape[0]-1: 2] # odd indicies
i
>>> [ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25,
27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51,
53, 55, 57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77,
79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103,
105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127, 129,
131, 133, 135, 137]
(a == b).all() # testing for equality
>>> True
a[i] = (a[i-1] + a[i+1]) / 2 # averaging positions across frames
(a == b).all() # testing for equality again
>>> False

Problem with adding elements from functions to list (too much memory is using?)

I replace in this code
import matplotlib.pyplot as plt
#parametry dla romeo i julii, zeby byly niezmienne w uczuciach musza byc wieksze od 0
aR = 0.5
aL = 0.7
#pR pL odpowiedzi Romea/Julii na miłość
pR = 0.2
pL = 0.5
x = [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, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]
rom = []
jul = []
def Romeo(n):
if n == 0:
return 1
return Romeo(n - 1)*aR
def Julia(n):
if n == 0:
return 1
return Julia(n - 1)*aL
def alfa(n):
if n == 0:
return 1
return aR*Romeo(n - 1) + pR*Julia(n - 1)
def beta(n):
if n == 0:
return 1
return aL*Julia(n - 1) + pL*Romeo(n - 1)
j = 0
while j < 100:
rom.append(alfa(j))
j+=1
j = 0
while j < 100:
jul.append(beta(j))
j+=1
plt.plot(x, rom, label = "Romeo love")
plt.plot(x, jul, label = "Julia love")
plt.xlabel("Days")
plt.ylabel("Romeo love")
plt.title("Some graph")
plt.legend()
plt.show()
only alfa and beta functions byt this:
import matplotlib.pyplot as plt
#parametry dla romeo i julii, zeby byly niezmienne w uczuciach musza byc wieksze od 0
aR = 0.5
aL = 0.7
#pR pL odpowiedzi Romea/Julii na miłość
pR = 0.2
pL = 0.5
x = [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, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, ]
rom = []
jul = []
def Romeo(n):
if n == 0:
return 1
return Romeo(n - 1)*aR
def Julia(n):
if n == 0:
return 1
return Julia(n - 1)*aL
def alfa(n):
if n == 0:
return 1
return round(aR*alfa(n - 1) + pR*beta(n - 1), 3)
def beta(n):
if n == 0:
return 1
return round(aL*beta(n-1) + pL*alfa(n - 1), 3)
j = 0
while j < 100:
rom.append(alfa(j))
j+=1
j = 0
while j < 100:
jul.append(beta(j))
j+=1
plt.plot(x, rom, label = "Romeo love")
plt.plot(x, jul, label = "Julia love")
plt.xlabel("Days")
plt.ylabel("Romeo love")
plt.title("Some graph")
plt.legend()
plt.show()
And Pycharm does not want to compilate (does not draw this graph) or it will take a lot of time. Ealier it was not a problem. \
I thought that a lot of numbers after point can be a reason and i round every number from list, but it didnt solve the problem.
What I changed by replacing this functions? How can I fix that?
Im pretty sure that the problem is in assigning elements from functions to list [2 while]. But i do not know why.
The current recursive approach is wasteful.
For example, when computing alfa(1) would require alfa(0), beta(0).
When you move on to alfa(2), the code will first compute alfa(1) and beta(1). Then alfa(1) would call alfa(0) and beta(0), while beta(1) would separately call alfa(0), beta(0) again, without recycling what we have computed before. So you need 6 calls for alfa(2).
At alfa(3), you would compute alfa(2) and beta(2), each of which needs 6 calls; so you need 14 calls (if my math is not off).
Imagine how many computations you would need at n == 100; the answer is 2535301200456458802993406410750. Cumulatively, i.e., since you want to plot alfa(1), ..., alfa(100), you need 5070602400912917605986812821300
computations in total, only to produce a single list rom.
You can use memoization to remember the previously calculated results and recycle them.
In python, you can achieve this by using functools.lru_cache (python doc); put
from functools import lru_cache
at the beginning of your code and then put
#lru_cache()
before each function; e.g.,
#lru_cache()
def Romeo(n):
if n == 0:
return 1
return Romeo(n - 1)*aR
You will see the graph almost immediately now.

Faster/lazier way to evenly and randomly split m*n into n group (each has m elements) in python

I want to split m*n elements (e.g., 1, 2, ..., m*n) into n group randomly and evenly such that each group has m random elements. Each group will process k (k>=1) elements at one time from its own group and at the same speed (via some synchronization mechanism), until all group has processed all their own elements. Actually each group is in an independent process/thread.
I use numpy.random.choice(m*n, m*n, replace=False) to generate the permutation first, and then index the permuted result from each group.
The problem is that when m*n is very large (e.g., >=1e8), the speed is very slow (tens of seconds or minutes).
Is there any faster/lazier way to do this? I think maybe this can be done in a lazier way, which is not generating the permuted result in the first time, but generate a generator first, and in each group, generate k elements at each time, and its effect should be identical to the method I currently use. But I don't know how to achieve this lazy way. And I am not sure whether this can be implemented actually.
You can make a generator that will progressively shuffle (a copy of) the list and lazily yield distinct groups:
import random
def rndGroups(A,size):
A = A.copy() # work on a copy (if needed)
p = len(A) # target position of random item
for _ in range(0,len(A),size): # work in chunks of group size
for _ in range(size): # Create one group
i = random.randrange(p) # random index in remaining items
p -= 1 # update randomized position
A[i],A[p] = A[p],A[i] # swap items
yield A[p:p+size] # return shuffled sub-range
Output:
A = list(range(100))
iG = iter(rndGroups(A,10)) # 10 groups of 10 items
s = set() # set to validate uniqueness
for _ in range(10): # 10 groups
g = next(iG) # get the next group from generator
s.update(g) # to check that all items are distinct
print(g)
print(len(s)) # must get 100 distinct values from groups
[87, 19, 85, 90, 35, 55, 86, 58, 96, 68]
[38, 92, 93, 78, 39, 62, 43, 20, 66, 44]
[34, 75, 72, 50, 42, 52, 60, 81, 80, 41]
[13, 14, 83, 28, 53, 5, 94, 67, 79, 95]
[9, 33, 0, 76, 4, 23, 2, 3, 32, 65]
[61, 24, 31, 77, 36, 40, 47, 49, 7, 97]
[63, 15, 29, 25, 11, 82, 71, 89, 91, 30]
[12, 22, 99, 37, 73, 69, 45, 1, 88, 51]
[74, 70, 98, 26, 59, 6, 64, 46, 27, 21]
[48, 17, 18, 8, 54, 10, 57, 84, 16, 56]
100
This will take just as long as pre-shuffling the list (if not longer) but it will let you start/feed threads as you go, thus augmenting the parallelism

Sum of multiple of 3+5 < 100

New to Jupyter Notebook, computing this code to return sum of values that are a multiple of 3 and 5, AND less than 100 in my list range 1, 100. I've got a feeling that I'm truncating the code by removing 3 and 5 from the equation. Not sure how/where to include that.
print(list(range(1, 100)))
multiple35 = 0
for i in range (1, 100):
if i % 15 == 0 and multiple35 <= 100:
multiple35 += i
print(multiple35)
My print line returns the range, Plus the 3 correct multiples less than 100. BUT ALSO prints 150, which is greater than and should be excluded from the result.
[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, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
15
45
90
150
Appreciate your help here.
BUT ALSO prints 150, which is greater than and should be excluded from the result.
The reason is simple. You are testing multiple35 <= 100 before the addition (multiple35 += i). So the sum is printed first and then tested in the next round. Therefore the output ends after the first occurrence that is bigger than 100.
By the way, it is useless to go through all natural numbers and only do anything on each 15th element (because of i % 15 == 0). You can use a tailored range instead:
>>> list(range(15,100,15))
[15, 30, 45, 60, 75, 90]
So a simplified loop which would stop printing when reaching 100, could look like:
multiple35 = 0
for i in range (15, 100, 15):
multiple35 += i
if multiple35 > 100:
break # no reason to continue the loop, the sum will never go back below 100
print(multiple35)
You have to check if the final target will exceed the threshold, because in your loop, when i=75, it satisfies the condition 75%15==0 and also satisfies 75<=100 and since it satisfies both, we let it into the next block which adds 75 to it and gives 150 which exceeds the threshold. The solution is to not even allow a number inside the adding part if when added, crosses the threshold,
There are simpler solutions like above by #Melebius but, I wanted to explain this in the way OP has written
multiple35 = 0
for i in range (1, 100):
if i % 15 == 0 and multiple35+i<=100:
multiple35 += i
print(multiple35)
There are multiple flaws in your logic. I am not going to address them all, but instead suggest an alternate way to solve your issue.
Simply notice that multiples of 3 and 5 are exactly the multiples of 15. So there is no need to range over all numbers from 0 to 100.
for x in range(15, 100, 15):
print(x)
# 15
# 30
# 45
# 60
# 75
# 90
You also mention that you want to sum all numbers. In python, you can sum over any iterator with sum, including a range.
print(sum(range(15, 100, 15)))
# 315
You can use list comprehension also here
values = [i for i in range(1,100) if i%5==0 if i%3==0]
print("Numbers divisible by 3 and 5:",values)
sum_of_numbers = 0
for i,items in enumerate(values):
sum_of_numbers = sum_of_numbers+items
if sum_of_numbers>100:
break
print(values[:i])

Recursion Error. Having trouble understanding the logic with recursive functions

from functools import lru_cache
#lru_cache(maxsize=1000)
def recursiveFunc(x):
if x == 1:
return 1
elif x > 1 :
return recursiveFunc(x) + recursiveFunc(x+1) #This is the part i'm having doubts about.
for x in range(1, 101):
print(x, ":", recursiveFunc(x))
This functions is supposed to generate consecutive numbers starting from 1 to 100 using recursion.
Your problem is that you have to learn very well all the recursion story, it takes time... you have to visualize what the program is executing in every step. My advice is to draw the first times the stack buffer with every call of the function
The solution of your problem is:
def recursiveFunc(x):
if x == 1:
return 1
elif x > 1 :
return 1 + recursiveFunc(x-1) #This is the part I've changed.
for x in range(1, 101):
print(x, ":", recursiveFunc(x))
Why your doesn't work? Cause when the function calls return, return start the new function recursiveFunc(x)... but it's just the same of before! so there is an infinite loop.
Furthermore if you add like recursiveFunc(x+1) and you pass x that are positive you will never made the comparison x == 0 cause x it's growing call after call.
Here I'll try to clear things up for you :)
Writing a function that lists numbers from 1 to n is simple.
If we tried running this function
def recursiveFunc(i):
print(i)
recursiveFunc(i+1)
recursiveFunc(1)
It would print out 1, then 2, 3.... But would never stop.
1
2
3
...
To fix this we add a second parameter
def recursiveFunc(i, n):
if i > n:
return
print(i)
recursiveFunc(i+1)
recursiveFunc(1, 100)
This will escape the function when it passes n, in this case, 100
1
2
...
100
if you wanted to return the series rather than just print it out you could do something like this:
def recursiveFunc(i, n):
if i >= n:
return str(i)
return str(i) + ", " + str(recursiveFunc(i + 1, n))
print(recursiveFunc(1, 100))
Then the output would be
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, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100

Categories