0-1 knapSnack problem with negetive numbers - python

Given an array of positive and negative numbers, find if we can choose some of them and sum them in a way that the final sum is zero. (any non zero number will do).
We have weights of them and values.
I believe this is a version of the 0-1 knapsack problem.
the sample input and output:
4 - weight - percent
+ 50 30
+ 80 1
- 20 30
- 30 30
yes
This is the code I wrote for it, but somehow I can't figure out why this won't work:
I'm getting the output 0 for it (and I should be getting 1000, choosing the first, third and fourth one).
Is there any way to solve the knapsack problem with negative values?
def knapSack(W , wt , val , n):
if n == 0 :
return 0
if (wt[n-1] > W):
return knapSack(W , wt , val , n-1)
else:
return max(val[n-1] + knapSack(W-wt[n-1] , wt , val , n-1), knapSack(W , wt , val , n-1))
val = [80, 50, -20, -30]
wt = [ 1,30, 30, 30]
W = 0
n = len(val)
print(knapSack(W, wt, val, n))
Anyone has any idea how should I change this in order to be working??

This is by no ways efficient but it seemed to be the simplest method of seeing if any combination of acids and bases would cancel out. (I did not use the knapsack approach)
For each (the acids and the bases) create an array that contains their amount. In the case of your example these arrays would be - acid = [1500, 80] and base = [600, 900]. Getting these arrays depends on your input format.
once you have these arrays you can do the following -
def SumOfSubsets(arr):
Allsums = []
for number in arr:
Placeholder = Allsums[:]
Allsums.append(number)
for sum in Placeholder:
Allsums.append(sum + number)
return(Allsums)
AcidList = SumOfSubsets(acid)
BaseList = SumOfSubsets(base)
for acids in AcidList:
if acids in BaseList:
print("can be neutralized")
*Note that by using this approach you do not know what combinations of acids and bases are needed to achieve the neutralization. You can get this combination by using another function if needed.

Related

Generate n lists of numbers between two numbers with python

I have a tuple J = (inf, sup, n) and I want to generate n lists of numbers between inf and sup.
J = (-7, 9.5, 4)
The expected output should be like this:
[-7,-2.875], [-2.875,1.25], [1.25,5.375], [5.375,9.5]
Can someone help ?
Thanks in advance !
Sorry but this platform is not for getting code solutions but for debugging or fixing issues in your code. It would help if you could mention what is it have you tried so far ?
However, here's a solution.
Your inputs are inf, n, sup.
If you notice, you list of n tuples in between inf and sup.
So the difference will be (sup-inf)/n
In the example you gave, it will be (9.5-(-7))/4 = 4.125.
So we will move from -7 to 9.5 by storing in each tuple, an initial value and a final value.
For 1st pair,
initial value = -7
final value = -7+4.125 = -2.875
For 2nd pair,
initial = -2.875
Final = -2.875 + 4.125 = 1.25
3rd pair,
initial = 1.25
final = 1.25 + 4.125 = 5.375
4th pair
initial = 5.375
final = 5.375 + 4.125 = 9.5
You may create a function that returns a list of these Pairs.
def getLists(inf, n, sup):
output = []
initial = inf
final = sup
continuous_difference = (sup-inf)/n
while(initial != final):
output.append([initial, initial + continuous_difference])
initial += continuous_difference
return output
if __name__ == '__main__':
print(getLists(-7, 4, 9.5))
[[J[0] + i/J[2]*(J[1]-J[0]), J[0] + (i+1)/J[2]*(J[1]-J[0])] for i in range(J[2])]

Finding boundary with given number and a list

I was given this problem. Given a list of percentage = [0.1,0.1,0.8] and number = 9, find all possible list (boundary of each element is 0.25 to 10, increment = 0.25) that multiply with the percentage list ,sum those number together and round to 1 decimal place must be equal to number = 9. I use brute force algorithm to solve this problem with the assistance of itertools product. but brute force this way is pretty slow. I'm trying to find a boundary (upper and lower boundary in range(lower boundary,upper boundary,25) for my 'for loop'. Can you guys suggest me a way to find it?
import itertools
ranges = []
n = int(input()) #number of element in percentage list
percent = []
for i in range(n):
percent.append(float(input())) #input the percentage list
total = float(input()) #the number mentioned above
for i in range(n):
ranges.append(range(25,1025,25)) #find boundary for this line
for xs in itertools.product(*ranges):
avg = 0
for i in range(n):
avg += xs[i]*percent[i]
if avg < (total*100+5) and avg >= (total*100-5):
for each in xs:
print(each/100, end = ' ')
print()
It's a little bit hard for me to explain algorithm in concise words T.T
So sufficient explanation is stated in the following code comments.
Basic idea is that this is be done in a recursive way (DFS, depth first search). The function should be something like recursion(percent_list, result_list, target).
Initially, it should be recursion([0.1, 0.1, 0.8], [], 9)
If we try the first value to be 3.25, then we update target value by 9 - 3.25*0.1 = 8.675. So we next call recursion([0.1, 0.8], [3.25], 8.675);
Then, we try the second value to be 4.00, then update target value by 8.675 - 4.0*0.1 = 8.275. So call recursion([0.8], [3.25, 4.0], 8.275);
Finally, we try the third value, and only 9.75, 10 is valid, since the summed up value are 8.525 and 8.725, respectly, and could round up to 9. So we append results [3.25, 4.0, 9.75] and [3.25, 4.0, 10.0] to result list.
After that, we try the second value to be 0.25, ..., 3.75, 4.25, 4.5, ..., 10.
Try first value to be 0.25, ..., 3.0, 3.5, 3.75, ..., 10.
To avoid too much recursion calls, we need to calculate the valid value could be appended to results every time, to cut the branches that's impossible.
The actual function signature is somehow differnt, to achieve round up.
import numpy as np
def recursion(percent_list, value_list, previous_results, target, tolerate_lower, tolerate_upper, result_list):
# change , 0.25 ~ 10 , change, , change, 0.5 = 9.5-9 , 0.4999 < 9-8.5, your answer
# init: [0.1,0.1,0.8] [] 9
# if reach the target within tolerate
if len(percent_list) == 0:
# print(previous_results)
result_dict.append(previous_results)
return
# otherwise, cut impossible branches, check minimum and maximum value acceptable for current percent_list
percent_sum = percent_list.sum() # sum up current percent list, **O(n)**, should be optimized by pre-generating a sum list
value_min = value_list[0] # minimum value from data list (this problem 0.25)
value_max = value_list[-1] # maximum value from data list (this problem 10.0)
search_min = (target - tolerate_lower - (percent_sum - percent_list[0]) * value_max) / percent_list[0] # minimum value acceptable as result
search_max = (target + tolerate_upper - (percent_sum - percent_list[0]) * value_min) / percent_list[0] # maximum value acceptable as result
idx_min = np.searchsorted(value_list, search_min, "left") # index of minimum value (for data list)
idx_max = np.searchsorted(value_list, search_max, "right") # index of maximum value (for data list)
# recursion step
for i in range(idx_min, idx_max):
# update result list
current_results = previous_results + [value_list[i]]
# remove the current state for variables `percent_list`, and update `target` for next step
recursion(percent_list[1:], value_list, current_results, target - percent_list[0] * value_list[i], tolerate_lower, tolerate_upper, result_list)
To solve this current problem,
result = []
recursion(np.array([0.1, 0.1, 0.8]), np.arange(0.25, 10.25, 0.25), [], 9, 0.5, 0.49999, result)
There's totally 4806 possible results. To validate results sum up to about 9 (but could not validate results is plenty enough),
for l in result:
if not (8.5 <= (np.array([0.1, 0.1, 0.8]) * np.array(l)).sum() < 9.5):
print("Wrong code!")
I think the wrost case complixty is still O(m^n * n), if m refers to data list length (0.25, 0.5, ..., 10), and n refers to percent list length (0.1, 0.1, 0.8). It should be further optimized to O(m^n * log(m)), to avoid summing up percent list every recursion; and to O(m^n), if we could fully utilize the nature of arithmetic sequence of the data list.

how to find 3 Numbers with Sum closest to a given number

I'm trying to write simple code for that problem. If I get an array and number I need to find the 3 numbers that their sum are close to the number that's given.
I've thought about first to pop out the last digit (the first number)
then I'll have a new array without this digit. So now I look for the second number who needs to be less the sum target. so I take only the small numbers that it's smaller them the second=sum-first number (but I don't know how to choose it.
The last number will be third=sum-first-second
I tried to write code but it's not working and it's very basic
def f(s,target):
s=sorted(s)
print(s)
print(s[0])
closest=s[0]+s[1]+s[2]
m=s[:-1]
print(m)
for i in range(len(s)):
for j in range(len(m)):
if (closest<=target-m[0]) and s[-1] + m[j] == target:
print (m[j])
n = m[:j] + nums[j+1:]
for z in range (len(z)):
if (closest<target-n[z]) and s[-1]+ m[j]+n[z] == target:
print (n[z])
s=[4,2,12,3,4,8,14]
target=20
f(s,target)
if you have idea what to change here. Please let me know
Thank you
Here is my solution I tried to maximize the performance of the code to not repeat any combinations. Let me know if you have any questions.
Good luck.
def find_3(s,target):
to_not_rep=[] #This list will store all combinations without repetation
close_to_0=abs(target - s[0]+s[1]+s[2]) #initile
There_is_one=False #False: don't have a combination equal to the target yet
for s1,first_n in enumerate(s):
for s2,second_n in enumerate(s):
if (s1==s2) : continue #to not take the same index
for s3,third_n in enumerate(s):
if (s1==s3) or (s2==s3) : continue #to not take the same index
val=sorted([first_n,second_n,third_n]) #sorting
if val in to_not_rep :continue #to not repeat the same combination with diffrent positions
to_not_rep.append(val)#adding all the combinations without repetation
sum_=sum(val) #the sum of the three numbers
# Good one
if sum_==target:
print(f"Found a possibility: {val[0]} + {val[1]} + {val[2]} = {target}")
There_is_one = True
if There_is_one is False: #No need if we found combination equal to the target
# close to the target
# We know that (target - sum) should equal to 0 otherwise :
# We are looking for the sum of closet combinations(in abs value) to 0
pos_n=abs(target-sum_)
if pos_n < close_to_0:
closet_one=f"The closet combination to the target is: {val[0]} + {val[1]} + {val[2]} = {sum_} almost {target} "
close_to_0=pos_n
# Print the closet combination to the target in case we did not find a combination equal to the target
if There_is_one is False: print(closet_one)
so we can test it :
s =[4,2,3,8,6,4,12,16,30,20,5]
target=20
find_3(s,target)
#Found a possibility: 4 + 4 + 12 = 20
#Found a possibility: 2 + 6 + 12 = 20
#Found a possibility: 3 + 5 + 12 = 20
another test :
s =[4,2,3,8,6,4,323,23,44]
find_3(s,target)
#The closet combination to the target is: 4 + 6 + 8 = 18 almost 20
This is a simple solution that returns all possibilites.
For your case it completed in 0.002019 secs
from itertools import combinations
import numpy as np
def f(s, target):
dic = {}
for tup in combinations(s, 3):
try:
dic[np.absolute(np.sum(tup) - target)].append(str(tup))
except KeyError:
dic[np.absolute(np.sum(tup) - target)] = [tup]
print(dic[min(dic.keys())])
Use itertools.combinations to get all combinations of your numbers without replacement of a certain length (three in your case). Then take the three-tuple for which the absolute value of the difference of the sum and target is minimal. min can take a key argument to specify the ordering of the iterable passed to the function.
from typing import Sequence, Tuple
def closest_to(seq: Sequence[float], target: float, length: int = 3) -> Tuple[float]:
from itertools import combinations
combs = combinations(seq, length)
diff = lambda x: abs(sum(x) - target)
return min(combs, key=diff)
closest_to([4,2,12,3,4,8,14], 20) # (4, 2, 14)
This is not the fastest or most efficient way to do it, but it's conceptionally simple and short.
Something like this?
import math
num_find = 1448
lst_Results = []
i_Number = num_find
while i_Number > 0:
num_Exp = math.floor(math.log(i_Number) / math.log(2))
lst_Results.append(dict({num_Exp: int(math.pow(2, num_Exp))}))
i_Number = i_Number - math.pow(2, num_Exp)
print(lst_Results)
In a sequence of numbers: for example 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, etc ...
The sum of the previous numbers is never greater than the next. This gives us the possibility of combinations, for example:
The number: 1448, there is no other combination than the sum of the previous numbers: 8 + 32 + 128 + 256 + 1024
Then you find the numbers whose sum is close to the number provided

Standard deviation of combinations of dices

I am trying to find stdev for a sequence of numbers that were extracted from combinations of dice (30) that sum up to 120. I am very new to Python, so this code makes the console freeze because the numbers are endless and I am not sure how to fit them all into a smaller, more efficient function. What I did is:
found all possible combinations of 30 dice;
filtered combinations that sum up to 120;
multiplied all items in the list within result list;
tried extracting standard deviation.
Here is the code:
import itertools
import numpy
dice = [1,2,3,4,5,6]
subset = itertools.product(dice, repeat = 30)
result = []
for x in subset:
if sum(x) == 120:
result.append(x)
my_result = numpy.product(result, axis = 1).tolist()
std = numpy.std(my_result)
print(std)
Note that D(X^2) = E(X^2) - E(X)^2, you can solve this problem analytically by following equations.
f[i][N] = sum(k*f[i-1][N-k]) (1<=k<=6)
g[i][N] = sum(k^2*g[i-1][N-k])
h[i][N] = sum(h[i-1][N-k])
f[1][k] = k ( 1<=k<=6)
g[1][k] = k^2 ( 1<=k<=6)
h[1][k] = 1 ( 1<=k<=6)
Sample implementation:
import numpy as np
Nmax = 120
nmax = 30
min_value = 1
max_value = 6
f = np.zeros((nmax+1, Nmax+1), dtype ='object')
g = np.zeros((nmax+1, Nmax+1), dtype ='object') # the intermediate results will be really huge, to keep them accurate we have to utilize python big-int
h = np.zeros((nmax+1, Nmax+1), dtype ='object')
for i in range(min_value, max_value+1):
f[1][i] = i
g[1][i] = i**2
h[1][i] = 1
for i in range(2, nmax+1):
for N in range(1, Nmax+1):
f[i][N] = 0
g[i][N] = 0
h[i][N] = 0
for k in range(min_value, max_value+1):
f[i][N] += k*f[i-1][N-k]
g[i][N] += (k**2)*g[i-1][N-k]
h[i][N] += h[i-1][N-k]
result = np.sqrt(float(g[nmax][Nmax]) / h[nmax][Nmax] - (float(f[nmax][Nmax]) / h[nmax][Nmax]) ** 2)
# result = 32128174994365296.0
You ask for a result of an unfiltered lengths of 630 = 2*1023, impossible to handle as such.
There are two possibilities that can be combined:
Include more thinking to pre-treat the problem, e.g. on how to sample only
those with sum 120.
Do a Monte Carlo simulation instead, i.e. don't sample all
combinations, but only a random couple of 1000 to obtain a representative
sample to determine std sufficiently accurate.
Now, I only apply (2), giving the brute force code:
N = 30 # number of dices
M = 100000 # number of samples
S = 120 # required sum
result = [[random.randint(1,6) for _ in xrange(N)] for _ in xrange(M)]
result = [s for s in result if sum(s) == S]
Now, that result should be comparable to your result before using numpy.product ... that part I couldn't follow, though...
Ok, if you are out after the standard deviation of the product of the 30 dices, that is what your code does. Then I need 1 000 000 samples to get roughly reproducible values for std (1 digit) - takes my PC about 20 seconds, still considerably less than 1 million years :-D.
Is a number like 3.22*1016 what you are looking for?
Edit after comments:
Well, sampling the frequency of numbers instead gives only 6 independent variables - even 4 actually, by substituting in the constraints (sum = 120, total number = 30). My current code looks like this:
def p2(b, s):
return 2**b * 3**s[0] * 4**s[1] * 5**s[2] * 6**s[3]
hits = range(31)
subset = itertools.product(hits, repeat=4) # only 3,4,5,6 frequencies
product = []
permutations = []
for s in subset:
b = 90 - (2*s[0] + 3*s[1] + 4*s[2] + 5*s[3]) # 2 frequency
a = 30 - (b + sum(s)) # 1 frequency
if 0 <= b <= 30 and 0 <= a <= 30:
product.append(p2(b, s))
permutations.append(1) # TODO: Replace 1 with possible permutations
print numpy.std(product) # TODO: calculate std manually, considering permutations
This computes in about 1 second, but the confusing part is that I get as a result 1.28737023733e+17. Either my previous approaches or this one has a bug - or both.
Sorry - not that easy: The sampling is not of the same probability - that is the problem here. Each sample has a different number of possible combinations, giving its weight, which has to be considered before taking the std-deviation. I have drafted that in the code above.

Python Random Selection

I have a code which generates either 0 or 9 randomly. This code is run 289 times...
import random
track = 0
if track < 35:
val = random.choice([0, 9])
if val == 9:
track += 1
else:
val = 0
According to this code, if 9 is generated 35 times, then 0 is generated. So there is a heavy bias at the start and in the end 0 is mostly output.
Is there a way to reduce this bias so that the 9's are spread out quite evenly in 289 times.
Thanks for any help in advance
Apparently you want 9 to occur 35 times, and 0 to occur for the remainder - but you want the 9's to be evenly distributed. This is easy to do with a shuffle.
values = [9] * 35 + [0] * (289 - 35)
random.shuffle(values)
It sounds like you want to add some bias to the numbers that are generated by your script. Accordingly, you'll want to think about how you can use probability to assign a correct bias to the numbers being assigned.
For example, let's say you want to generate a list of 289 integers where there is a maximum of 35 nines. 35 is approximately 12% of 289, and as such, you would assign a probability of .12 to the number 9. From there, you could assign some other (relatively small) probability to the numbers 1 - 8, and some relatively large probability to the number 0.
Walker's Alias Method appears to be able to do what you need for this problem.
General Example (strings A B C or D with probabilities .1 .2 .3 .4):
abcd = dict( A=1, D=4, C=3, B=2 )
# keys can be any immutables: 2d points, colors, atoms ...
wrand = Walkerrandom( abcd.values(), abcd.keys() )
wrand.random() # each call -> "A" "B" "C" or "D"
# fast: 1 randint(), 1 uniform(), table lookup
Specific Example:
numbers = dict( 1=725, 2=725, 3=725, 4=725, 5=725, 6=725, 7=725, 8=725, 9=12, 0=3 )
wrand = Walkerrandom( numbers.values(), numbers.keys() )
#Add looping logic + counting logic to keep track of 9's here
track = 0
i = 0
while i < 290
if track < 35:
val = wrand.random()
if val == 9:
track += 1
else:
val = 0
i += 1

Categories