This question already has answers here:
Sum of elements of subarrays is equal to a target value in Python
(4 answers)
Closed 1 year ago.
I have the following variables:
arr = [50, 100, 100, 100, 200, 200]
inp = 450
in the inp variable, I receive data from the user, the user can enter any value between the minimum array and the maximum amount of values in the array (50 and 750).
I want to return the values in the array that make up the amount equal to the value in the variable inp
In this situation inp = 450 there are two variants: 50 + 100 + 100 + 200 or 50 + 200 + 200. I'm only interested in one of them.
how can i continue the following code:
import sys
arr = [50, 100, 100, 100, 200, 200]
inp = 450
sum = 0
res = []
for x in arr:
if x == inp:
res = x
print(res)
sys.exit()
sum = sum+x
res.append(x)
if sum == inp:
print(res)
sys.exit()
I can solve the problem if I make six loops, but if the length of the array changes I have to intervene on the source code. I'm looking for a recursive solution.
I would use the itertools.combinations API. You could also use a generator to allow finding all of the values, or choose to stop at the first:
import itertools
def find_comb_for_sum(data, total):
for i in range(len(data)):
for comb in itertools.combinations(data, i + 1):
if sum(comb) == total:
yield comb
This will find all combinations of the array starting from least amount of entries per combination to most. Reverse the range range(len(data))[::-1] if you'd like to do the opposite.
Some test cases:
arr = [50, 100, 100, 100, 200, 200]
inp = 450
comb = find_comb_for_sum(arr, inp)
print(f"Matching combination for {arr} summing to {inp} is: {next(comb, None)}")
arr = [50, 100, 100, 100, 200, 200]
inp = 444
comb = find_comb_for_sum(arr, inp)
print(f"Matching combination for {arr} summing to {inp} is: {next(comb, None)}")
Matching combination for [50, 100, 100, 100, 200, 200] summing to 450 is: (50, 200, 200)
Matching combination for [50, 100, 100, 100, 200, 200] summing to 444 is: None
Related
I have a list, say list_1 = [5,12,22,37,65,100], now I want to copy the elements of this list to another list say list_2 which will also have the same number of elements, i.e in this case = 6. I just want to run a loop for 6 times and generate 6 different random numbers and depending on the values of random numbers I want a take an element from list_1 and copy that to list_2 using the logic :for the generated random number take the next higher number from the list.
But the catch is one number is not allowed more than n/2 times. In this case, no number can come from list_1 more than 3 times. If the 1st 3 generated random numbers are : 55, 61 and 58 then the first 3 elements of list_2 will be = [65,65,65], so 65 can't be copied anymore.
Below is the code:
import random
from random import choice
# choice module gives the choice to pick a random number from a range avoiding some sub range
list_1 = [5,12,22,37,65,100]
# list_1 indicates the list of all cumulative sums
print(list_1)
list_2 = []
# list_2 is initially an empty list
n_items = len(list_1)
# n_items holds how many elements are there in the cumulative sum list
# random.seed(0)
for _ in range(n_items):
rand_number = random.randint(1, 100)
# generates a random number between 1 to 100
print(f"Generated random number: {rand_number}")
for i, number in enumerate(list_1):
if number > rand_number:
n_same_numbers_in_list2 = sum(l2i == number for l2i in list_2)
# Keeps track of the numbers those are getting inserted
print(f"{n_same_numbers_in_list2+1} is the count for the value")
if (n_same_numbers_in_list2+1) <= n_items/2:
number_to_append = number
print(number)
else:
lower_bound = list_1[i-1] if i > 0 else 0
upper_bound = number
number_to_append = choice([i for i in range(1,100) if i not in range(lower_bound,upper_bound)])
list_2.append(number_to_append)
break
print(list_2)
Here it is creating problem in number_to_append = choice([i for i in range(1,100) if i not in range(lower_bound,upper_bound)]) this condition. That is whenever an element is trying to enter more than 3 times then it is simply generating another random number which can be outside the list_1.
Below I am attaching one such sample output.
[5, 12, 22, 37, 65, 100]
Generated random number: 84
1 is the count for the value
100
Generated random number: 75
2 is the count for the value
100
Generated random number: 90
3 is the count for the value
100
Generated random number: 82
4 is the count for the value
Generated random number: 52
1 is the count for the value
65
Generated random number: 13
1 is the count for the value
22
[100, 100, 100, 39, 65, 22]
Here though 39 is not in the list_1, still it is getting printed in the output file.
Here though 39 is not in the list_1, still it is getting printed in the output file.
Here is no check to presence of number inside list_1 when else running.
Fast and simple solution is to add while loop (very bad performance):
else:
lower_bound = list_1[i-1] if i > 0 else 0
upper_bound = number
number_to_append = None
while number_to_append not in list_1 or number_to_append == number:
# I changed i to m because i already used as enumerate index
number_to_append = choice([m for m in range(1,100) if m not in range(lower_bound,upper_bound)])
However there is other logic problems in your code and it is overcomplicated. Here is possible code:
import random
input_list = [5, 12, 22, 37, 65, 100]
# use real randoms
#randoms = []
#for _ in range(0, len(input_list)):
# randoms.append(random.randint(1, 100))
# or reproduceable randoms from your example
randoms = [84, 75, 90, 82, 52, 13]
result_list = []
input_list.sort()
banned_nums = {}
for random_num in randoms:
n = None
for i, v in enumerate(input_list):
if v > random_num:
if v in banned_nums and banned_nums[v] >= len(input_list)/2:
continue
else:
n = v
banned_nums[v] = banned_nums.get(v, 0) + 1
break
if n is None:
result_list.append(-1)
else:
result_list.append(v)
print(result_list)
You didn't say anything about what number we should append if "good" numbers in list_1 were ended (for example, if random numbers will be [99, 99, 99, 99, 99, 99]), so I used -1 as Good number not found:
[100, 100, 100, -1, 65, 22]
I need to generate a random number between a min and a max. This number should be a multiple of 10.
This is the solution I have come to.
import random
BLOCK = 10
def random_excluding_values(_min, _max, exclude_list):
while True:
random_number = round(random.randrange(_min, _max - BLOCK) / BLOCK) * BLOCK
if random_number not in exclude_list:
return random_number
There is a way to obtain the same result using list list comprehension or something else?
Lower bound includes multiples of 10 when calculating range.Skipping while loop makkes sure that you will not be stuck in infinite loop. Conversion to sets make sure that you know what you can get .
import random
def random_excluding_values(_min, _max, exclude_list, num_of_elems):
lower_bound = round(_min+4, -1)
all_nums = {num for num in range(lower_bound, _max+1, 10)}
exclude_set = set(exclude_list)
valid_choices = all_nums - exclude_set
return random.sample(valid_choices, num_of_elems)
_min = 9
_max = 103
exclude_list = [40,50,60,70]
#valid_list = [10,20,30,80,90,100]
num_elems = 3
for i in range(5):
print(f'run = {i}; result = {random_excluding_values(_min, _max, exclude_list, num_elems)}')
Outputs:
run = 0; result = [80, 30, 20]
run = 1; result = [10, 20, 80]
run = 2; result = [10, 80, 30]
run = 3; result = [100, 90, 10]
run = 4; result = [10, 20, 100]
random.sample returns a list of choices from valid set of numbers. So you can easily modify to get you single number.
return random.sample(valid_choices, 1)[0]
I'm trying to translate this pseudocode and can't do it accurately. In particular, I can't seem to figure out what real here means. This is the pseudocode:
Function Real average(Real values[], Integer size)
Declare Real total = 0.0
Declare Integer counter = 0
While counter < size
Set total = total + values[counter]
Set counter = counter + 1
End While
Return total / size
End Function
Declare Real scores[6] = 90, 80, 70, 100, 60, 80
Display average(scores, 6)
And this is what I've come up with:
def average(values[], int(size))
total = 0.0
counter = 0
while counter < size:
total = total + values[counter]
counter = counter + 1
return total / size
scores[6] = 90, 80, 70, 100, 60, 80
print(average(scores, 6))
Some languages use the term "real" in place of "float" etc. Therefore, in Python, with this bit of code you can leave it out.
..but there are a few things wrong with your code other than that. For example you just want
scores=[90,80, 70, 100, 60, 80]
then just give average "scores" and 6
Like this
def average(values ,size):
total = 0.0
counter = 0
while counter < size:
total = total + values[counter]
counter = counter + 1
return total / size
scores = [90, 80, 70, 100, 60, 80]
print(average(scores, 6))
Whilst clearly it is not necessary to do this in this way, I presume you are just learning Python...
If i've got 2 variables that contain the price needed, and the value of coins provided in cents. E.G, coins = [5, 5, 10, 20, 50, 100, 100, 200], and total = 250. I've tried a number of approaches, but I can't seem to find anything that works properly.
For example, if i've got coins = [10, 10, 20, 20, 20, 100, 100] and total = 250 it returns [10, 20, 20, 100, 100], ordering not important.
I've been trying find a solution to this for a while. Thanks for any help
For this to work, you would have to sort your coins in descending order, or use reversed(coinList) instead of coinList in the first for loop and vice versa in the second if you would like to sort them forwards:
total = 250
value = 0
coins = []
coinList = [100, 100, 20, 20, 20, 10, 10]
for coin in coinList:
if value + coin <= total:
value += coin
coins.append(coin)
else:
continue
if value >= total:
break
else:
for coin in reversed(coinList):
value += coin
coins.append(coin)
if value >= total:
break
print coins
This question already has answers here:
Finding Combinations to the provided Sum value
(2 answers)
Closed 9 years ago.
This is for a homework assignment, I solved the problem but I'm trying to find a faster solution.
The problem is as follows: I need to figure out how many possible aminoacid (aa) sequences exist that have a total mass m.
I have a table of aminoacids (single letter strings) and their corresponding mass (int) which I put in a dictionary.
My initial solution was to create all possible combinations of aa and compare each combination's total mass to the mass m. This works for small numbers of m but the number of combinations gets ridiculously high when m starts being in the hundreds.
I did some minor optimization and got it to work reasonably fast for m < 500 which is good enough for this problem but I want to know how to make it work for higher masses.
This is what I have so far:
totalmass = m
def pepList():
tempList = ['']
temp2List = []
length = 0
total = 0
aminoList = 'GASPVTCINDKEMHFRYW' #this are all the aminoacids
while length < maxLength:
for i in tempList:
for j in aminoList:
pepMass = peptideMass(i+j, massTable) #find the mass of
#this peptide
if pepMass == totalmass:
total += 1
elif pepMass <= totalmass:
temp2List.append(i+j)
tempList = []
for i in temp2List:
tempList.append(i)
temp2List = []
length = length + 1
print (total)
pepList()
I can get a solution for m = 300 in about a second but m = 500 takes about 40 seconds
I tried an alternative using itertools but it wasn't any faster:
total = 0
pepList = []
for i in range(maxLength+1):
for p in itertools.combinations_with_replacement(aminoList, i):
#order matters for the total number of peptides but not for calculating
#the total mass
amino = ''.join(p)
if peptideMass(amino, massTable) == mass:
pepList.append(amino)
print (len(pepList))
newpepList = []
for i in pepList:
for p in itertools.permutations(i, r = len(i)):
#I use permutations here to get the total number because order matters
if p not in newpepList:
newpepList.append(p)
total +=1
print (total)
Sample input:
m = 270
output:
22
The order in which the amino acids occur does not change the mass - AAC weighs the same as ACA and CAA.
Therefore this could be reduced to a linear programming problem - find the values of the coefficients such that M = a*A + b*C + c*D + d*E + e*G + ... + r*W
Once you have a solution, you can generate all possible permutations of the given set of amino acids - or if you just need the number of permutations, you can calculate it directly.
Edit:
As #Hooked points out, this is not Linear Programming for two reasons: first, we require integer coefficients, and second, we are looking for all combinations, not finding a single optimal solution.
I've worked out a recursive generator as follows:
from math import floor, ceil
import profile
amino_weight = {
'A': 71.038,
'C': 103.009,
'D': 115.027,
'E': 129.043,
'F': 147.068,
'G': 57.021,
'H': 137.059,
'I': 113.084,
'K': 128.095,
'L': 113.084, # you omitted leutine?
'M': 131.040,
'N': 114.043,
'P': 97.053,
'Q': 128.059, # you omitted glutamine?
'R': 156.101,
'S': 87.032,
'T': 101.048,
'V': 99.068,
'W': 186.079,
'Y': 163.063
}
def get_float(prompt):
while True:
try:
return float(raw_input(prompt))
except ValueError:
pass
# This is where the fun happens!
def get_mass_combos(aminos, pos, lo, hi, cutoff):
this = aminos[pos] # use a pointer into the string, to avoid copying 8 million partial strings around
wt = amino_weight[this]
kmax = int(floor(hi / wt))
npos = pos - 1
if npos: # more aminos to consider recursively
for k in xrange(0, kmax + 1):
mass = k * wt
nlo = lo - mass
nhi = hi - mass
ncutoff = cutoff - mass
if nlo <= 0. and nhi >= 0.:
# we found a winner!
yield {this: k}
elif ncutoff < 0.:
# no further solution is possible
break
else:
# recurse
for cc in get_mass_combos(aminos, npos, nlo, nhi, ncutoff):
if k > 0: cc[this] = k
yield cc
else: # last amino - it's this or nothing
kmin = int(ceil(lo / wt))
for k in xrange(kmin, kmax+1):
yield {this: k}
def to_string(combo):
keys = sorted(combo)
return ''.join(k*combo[k] for k in keys)
def total_mass(combo):
return sum(amino_weight[a]*n for a,n in combo.items())
def fact(n):
num = 1
for i in xrange(2, n+1):
num *= i
return num
def permutations(combo):
num = 0
div = 1
for v in combo.values():
num += v
div *= fact(v)
return fact(num) / div
def find_combos(lo, hi):
total = 0
bases = []
aminos = ''.join(sorted(amino_weight, key = lambda x: amino_weight[x]))
for combo in get_mass_combos(aminos, len(aminos)-1, lo, hi, hi - amino_weight[aminos[0]]):
base = to_string(combo)
bases.append(base)
mass = total_mass(combo)
cc = permutations(combo)
total += cc
print("{} (mass {}, {} permutations)".format(base, mass, cc))
print('Total: {} bases, {} permutations'.format(len(bases), total))
def main():
lo = get_float('Bottom of target mass range? ')
hi = get_float('Top of target mass range? ')
prof = profile.Profile()
prof.run('find_combos({}, {})'.format(lo, hi))
prof.print_stats()
if __name__=="__main__":
main()
It also looks for a mass range, using floating-points amino masses. Searching for a mass between 748.0 and 752.0 returns 7,505 bases totaling 9,400,528 permutations in 3.82 seconds on my machine (i5-870).
Inspired by Hugh's answer below: here is a solution using numpy. It always calculates all combinations, therefore it uses a lot of memory, but it has linear complexity.
The idea is to store all possible coeficient arrays in a numpy array (C), and use numpy.dot to generate the sum of masses for each coeficient array, which is then stored in the array PROD.
The problem is then finding which elements of PROD contain the desired mass M, and work back (based on the element index in PROD) the actual PEP_NAMES
It would be interesting to know if it actually produces the correct result:
import sys
import numpy as np
def main():
try:
M = int(sys.argv[1])
except Exception:
print "Usage: %s M" % sys.argv[0]
print " M = total mass"
sys.exit()
PEP_NAMES = ['G', 'A', 'S', 'P', 'V', 'T', 'C', 'I', 'N', 'D', 'K', 'E', 'M', 'H', 'F', 'R', 'Y', 'W']
# random data
PEP_MASSES = np.array([ 71, 99, 14, 37, 61, 63, 83, 3, 52, 43, 2, 80, 18, 37, 56, 36, 96, 13])
LEN_PEP_MASSES = len(PEP_MASSES)
NUM_COMB = 2**LEN_PEP_MASSES-1
# numpy array containing all possible coeficients
C = np.array([[int(x) for x in np.binary_repr(K, width=LEN_PEP_MASSES)] for K in xrange(NUM_COMB)])
# each element is an array of coefficients representing a number between 0 and NUM_COMB in binary form
print "type(C) = %s" % type(C)
print "type(C[0]) = %s" % type(C[0])
print "C.shape = %s" % str(C.shape)
print "C[0].shape = %s" % str(C[0].shape)
print "C[0] = %s" % C[0]
print "C[15] = %s" % C[15]
print "C[255] = %s" % C[255]
# Calculate sum of all combinations
PROD = C.dot(PEP_MASSES)
# find the ones that match M
valid_combinations = [(i,x) for i,x in enumerate(PROD) if x == M]
print 'Found %d possibilities with total mass = %d:' % (len(valid_combinations), M)
print valid_combinations
for comb_index, comb_mass in valid_combinations:
# work back the combinations in string format
comb_str = [PEP_NAMES[i] for i,x in enumerate(C[comb_index]) if x==1]
print '%10d --> %s' % (comb_index, ''.join(comb_str))
if __name__ == '__main__':
main()
Sample output:
python test.py 750
type(C) = <type 'numpy.ndarray'>
type(C[0]) = <type 'numpy.ndarray'>
C.shape = (262143, 18)
C[0].shape = (18,)
C[0] = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
C[15] = [0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1]
C[255] = [0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1]
Found 24 possibilities with total mass = 750:
[(130815, 750), (196478, 750), (204671, 750), (208895, 750), (212575, 750), (220155, 750), (221039, 750), (225263, 750), (227455, 750), (228059, 750), (228943, 750), (229151, 750), (236542, 750), (244446, 750), (244695, 750), (252910, 750), (257914, 750), (260062, 750), (260814, 750), (260988, 750), (261022, 750), (261063, 750), (261750, 750), (262109, 750)]
130815 --> ASPVTCINKEMHFRYW
196478 --> GSPVTCINDEMHFRY
204671 --> GATCINDEMHFRYW
208895 --> GAVCINDKEMHFRYW
212575 --> GAVTCINEHFRYW
220155 --> GAPTCNDKEMHFYW
221039 --> GAPTCINDEMFRYW
225263 --> GAPVCINDKEMFRYW
227455 --> GAPVTCEMHFRYW
228059 --> GAPVTCNKEHFYW
228943 --> GAPVTCINEFRYW
229151 --> GAPVTCINDHFRYW
236542 --> GASTCNDKEMHFRY
244446 --> GASVTCNKEHFRY
244695 --> GASVTCNDKEHRYW
252910 --> GASPTCNDKEMFRY
257914 --> GASPVCINDEMHFY
260062 --> GASPVTINDKEHFRY
260814 --> GASPVTCNKEFRY
260988 --> GASPVTCNDEMHFR
261022 --> GASPVTCNDKHFRY
261063 --> GASPVTCNDKERYW
261750 --> GASPVTCINEMHRY
262109 --> GASPVTCINDKEHFRW
It takes about 15 seconds to run on my laptop.
Note that it gives you all combinations (!) (i.e. the order of elements is not important). If you need all permutations, you just need to iterate over each result and generate them.