Related
I have two knapsacks, let's call them knapsack A, and knapsack B, each knapsack has a max weight limit. We also have some item types that we can take with us. Each item type has a price, and two values - value X, and value Y. Each of these values is added to it's respective knapsack when used, so that the following is true:
We are operating with the following limits (note that this is not a solution, just an example of the core principle):
Knapsack A
capacity: 10
Knapsack B
capacity: 20
items:
Item 1
price: 10
X: 3
Y: 9
After we 'take' Item 1 we get the following knapsack state:
Knapsack A
remaining capacity: 7 (capacity of A - X of item 1)
Knapsack B
remaining capacity: 11 (capacity of B - Y of item 1)
To reach this state we have paid 10 units (price of Item 1).
We're trying to find the best combination of items (we may use each item an infinite amount of times) so that we fill both knapsacks perfectly, and at the lowest possible price. We may use fractions of each item, this means that X, Y, and price all get divided by the fraction.
How can I achieve this?
I have tried modifying a fractional knapsack algorithm, but I don't know what parts/what to modify in order to achieve my goals.
As user3386109 notes, there is a straightforward linear program.
For each item i, let ui ≥ 0 be the (fractional) number of
units purchased of i. We want to
minimize ∑i pricei ui
(minimize the sum over items i of the product of the price of i and the
purchase quantity of i), subject to linear constraints ensuring that we
exactly fill both knapsacks:
A: ∑i Xi ui = capacityA
B: ∑i Yi ui = capacityB.
Now, you can write a program to expand the sums and hand the resulting
equations off to a solver library. I use
GLOP at
work; sample code below.
If you don’t want to use an off-the-shelf solver, this linear program is
quite special in that it only has two constraints, so there’s likely a
specialized algorithm (e.g., maybe we could use Megiddo’s
low-dimensional LP algorithm to solve the dual). But it won’t be as
simple as fractional knapsack.
import collections
from ortools.linear_solver import pywraplp
Item = collections.namedtuple("Item", ("price", "X", "Y"))
def optimize(A, B, items):
solver = pywraplp.Solver.CreateSolver("GLOP")
quantities = [solver.NumVar(0, solver.infinity(), "") for i in items]
solver.Minimize(sum(i.price * u for (i, u) in zip(items, quantities)))
solver.Add(sum(i.X * u for (i, u) in zip(items, quantities)) == A)
solver.Add(sum(i.Y * u for (i, u) in zip(items, quantities)) == B)
solver.Solve()
return solver.Objective().Value(), [
(i, u.solution_value()) for (i, u) in zip(items, quantities)
]
def test():
obj_value, quantities = optimize(
10,
20,
[Item(price=10, X=3, Y=9), Item(price=5, X=5, Y=7), Item(price=100, X=1, Y=1)],
)
print("# cost: {}".format(obj_value))
for i, u in quantities:
print("# {}: {}".format(i, u))
if __name__ == "__main__":
test()
# cost: 18.750000000000007
# Item(price=10, X=3, Y=9): 1.250000000000001
# Item(price=5, X=5, Y=7): 1.2499999999999991
# Item(price=100, X=1, Y=1): 0.0
Consider we have a sacks of gold and thief wants to get the maximum gold. Thief can take the gold to get maximum by,
1) Taking the Gold from contiguous sacks.
2) Thief should take the same amount of gold from all sacks.
N Sacks 1 <= N <= 1000
M quantity of Gold 0 <= M <= 100
Sample Input1:
3 0 5 4 4 4
Output:
16
Explanation:
4 is the minimum amount he can take from the sacks 3 to 6 to get the maximum value of 16.
Sample Input2:
2 4 3 2 1
Output:
8
Explanation:
2 is the minimum amount he can take from the sacks 1 to 4 to get the maximum value of 8.
I approached the problem using subtracting the values from array and taking the transition point from negative to positive, but this doesn't solves the problem.
EDIT: code provided by OP to find the index:
int temp[6];
for(i=1;i<6;i++){
for(j=i-1; j>=0;j--) {
temp[j] = a[j] - a[i];
}
}
for(i=0;i<6;i++){
if(temp[i]>=0) {
index =i;
break;
}
}
The best amount of gold (TBAG) taken from every sack is equal to weight of some sack. Let's put indexes of candidates in a stack in order.
When we meet heavier weight (than stack contains), it definitely continues "good sequence", so we just add its index to the stack.
When we meet lighter weight (than stack top), it breaks some "good sequences" and we can remove heavier candidates from the stack - they will not have chance to be TBAG later. Remove stack top until lighter weight is met, calculate potentially stolen sum during this process.
Note that stack always contains indexes of strictly increasing sequence of weights, so we don't need to consider items before index at the stack top (intermediate AG) in calculation of stolen sum (they will be considered later with another AG value).
for idx in Range(Sacks):
while (not Stack.Empty) and (Sacks[Stack.Peek] >= Sacks[idx]): //smaller sack is met
AG = Sacks[Stack.Pop]
if Stack.Empty then
firstidx = 0
else
firstidx = Stack.Peek + 1
//range_length * smallest_weight_in_range
BestSUM = MaxValue(BestSUM, AG * (idx - firstidx))
Stack.Push(idx)
now check the rest:
repeat while loop without >= condition
Every item is pushed and popped once, so linear time and space complexity.
P.S. I feel that I've ever seen this problem in another formulation...
I see two differents approaches for the moment :
Naive approach: For each pair of indices (i,j) in the array, compute the minimum value m(i,j) of the array in the interval (i,j) and then compute score(i,j) = |j-i+1|*m(i,j). Take then the maximum score over all the pairs (i,j).
-> Complexity of O(n^3).
Less naive approach:
Compute the set of values of the array
For each value, compute the maximum score it can get. For that, you just have to iterate once over all the values of the array. For example, when your sample input is [3 0 5 4 4 4] and the current value you are looking is 3, then it will give you a score of 12. (You'll first find a value of 3 thanks to the first index, and then a score of 12 due to indices from 2 to 5).
Take the maximum over all values found at step 2.
-> Complexity is here O(n*m), since you have to do at most m times the step 2, and the step 2 can be done in O(n).
Maybe there is a better complexity, but I don't have a clue yet.
I have N lists containing various numbers of objects and for each list a number X of required distinct values.
An simple example:
List1 = [ 1,2,3,4 ] : 2
List2 = [ 2,3,4 ] : 1
List3 = [ 1,2,4 ] : 1
Here one solution is to select 1 and 2 from List1, 3 from List2 and 4 from List3
But if the problem looks like this, there is no solutions
List1 = [ 1,2,3,4 ] : 2
List2 = [ 2,3,4 ] : 2
List3 = [ 1,2,4 ] : 1
So, the brute force solution to this problem is to select the required numbers of objects from the first list, then select the required numbers from the second list where the selected cannot be in the previous selected. If this fails, select other objects from the first list and so forth.
This, however, is not efficient and I might end up trying all combinations before I find a solution, if any.
So, is there any other way to solve this problem?
This problem can be restated in terms of a flow network, and solved using a number of maximum flow algorithms:
Add a source vertex S
Add a vertex Gi for each of the sets
Add an edge from S to Gi with the capacity equal to the number of items to be selected
Add a vertex Ni for each distinct number in the union of all sets
Add an edge with capacity 1 between each Gi and Ni where the set contains the number
Add a sink vertex T
Add an edge with capacity 1 between each Ni and T
Here is how the flow network for your problem would look:
If the max flow algorithm does not produce a flow equal to the total required distinct numbers, the problem cannot be solved. Otherwise, use capacity assignments produced by the algorithm for edges between Gi and Ni to decide which numbers to take from each set.
I've got a following problem:
There is a set of items, every item has 2 different positive values A and B.
The knapsack has two values: totalA and totalB. which are the maximum sums of values A and B of chosen items.
I have to find out, what the maximum items count the knapsack can hold is.
Example:
Input:
totalA: 10, totalB: 15
item1 A:3, B: 4
item2 A:7, B: 2
item3 A:1, B: 9
item4 A:2, B: 1
item5 A:4, B: 6
Output:
3(items: 2, 3, 4)
How should I use dynamic programming in order to solve this task?
This is known as the "multiply-constrained knapsack problem" (MKP, occasionally rendered as d-KP). It can be solved in pseudopolynomial time just like the regular knapsack problem, but you need a two-dimensional table instead of one.
Define m[i,wa,wb] to be the maximum value (count of items here), that can be attained with sum of as being less than or equal to wa and sum of bs being less than or equal to wb, using items up to i.
m[i,wa,wb] = m[i-1,wa,wb]
if item[i].a > wa or item[i].b > wb
or
m[i,wa,wb] = max (m[i-1, wb, wb], m[i-1, wa - item[i].a, wb - item[i].b] + 1)
if item[i].a <= wa and item[i].b <= wb
Here is an recurrence equation that might help you :-
if(Items[N].b<=Wa && Items[N].b<=Wa)
Value(N,Wa,Wb) = max(1+Value(N-1,Wa-Items[N].a,Wb-Items[N].b),Value(N-1,Wa,Wb))
else Value(N,Wa,Wb) = Value(N-1,Wa,Wb)
Where Wa = Current capacity of A sack & Wb of B sack
N = no of items considered
Note: You can use a hash table implementation on recursive solution which would prevent of three dimensional array.
Given a set of items (sized anywhere from 1 to 100) and a number of bins (1 to 15.) Each item having a subset of bins the item can be assigned to and a preference ordering of which bin is best, second best, etc., just for it. Items also have a natural order, represented below by naming, e.g., item1 before item2. Each bin has a capacity between 1 and 5 (every item has identical weight, i.e., 1.)
An example input could be three bins and six items (- indicates a bin is not in the item's usable set, i.e., can't be packed with it):
| bin1 bin2 bin3 | bin1 bin2 bin3
------------------------ ----------------------------
item1 | 1 2 - capacity | 4 4 5
item2 | - 1 2
item3 | 2 1 3
item4 | 1 2 3
item5 | 1 - 2
item6 | 1 2 3
The goals are (in order with each goal completely overriding any lower goal when there's a conflict, e.g., packing five items is always better than four no matter what number of bins are used or preferences ignored):
maximize number of items packed
pack items in their natural order, e.g., if total bin capacity is one and there are two items, item1 will be packed and item2 not
minimize number of bins used
pack each item according to its bin preferences and natural order, i.e, item1 in its first preference and item2 in its second is better than item1 in its second and item2 in its first
in cases where two solutions are indistinguishable by these goals, either solution is acceptable to rank higher, e.g, as a side-effect of implementation or just arbitrary tie-breaking.
So the input above would be packed as:
| bin1 bin2 bin3
------------------------
item1 | x
item2 | x
item3 | x
item4 | x
item5 | x
item6 | x
The question then is for what to read/review to help me come up with algorithm ideas for solving this problem with the input sizes from the first paragraph and a time constraint of a few seconds, i.e., not brute force (or at least any brute force I've conceived of so far.) I'm using Ruby and C but language isn't overly relevant at this stage of woods stumbling.
I'll be grateful of any reading suggestions, ideas on combinations of algorithms, or just thoughts on clarifying the problem statement...
Update 1
To be less unclear, while there are many algorithms that cover various parts of this my difficulty is in finding (or perhaps recognizing) information handling all the criteria together, especially minimizing the number of bins used when there is excess capacity and conflicting item-to-bin sets and item preferences, which is hopefully more clearly shown in the following example:
| bin1 bin2 bin3 | bin1 bin2 bin3
------------------------ ----------------------------
item1 | 1 2 3 capacity | 3 2 3
item2 | 1 2 3
item3 | - 1 2
While bin1 is the most preferred, item3 can't be placed in it at all, and while bin2 is the next most preferred for all items, it can hold only two of the three items. So the correct set of assignments (x) is actually the least preferred bin:
| bin1 bin2 bin3
------------------------
item1 | x
item2 | x
item3 | x
Update 2
I reworked the description with information on how the goals relate and removed the variable of bin priority as it only makes finding an answer less likely and can be worked around elsewhere in the system I'm working on.
Suppose there are n items and b bins, and each bin has size s. The ordering of constraints you have added actually simplifies the problem a great deal.
They mean specifically that we should always pick items 1, 2, ..., m for the largest m <= n that will fit in the allotted number of bins (since picking a smaller number would necessarily produce a worse solution by rule 1). Items will be packed in bins in this order, possibly with some bins left incompletely filled (since rearranging items within a bin or across bins would produce a worse solution by rule 2). There are 2 cases:
m < n, meaning that we can't fit all the items. In that case, all b bins will be tightly packed with the 1st m items in that order, and we are done.
m = n, in which case we can fit all the items. We now consider subcases of this case.
In this case, it may be possible that packing bins tightly will leave a final block of 0 < e <= b of the bins completely empty. In that case, discard those final e empty bins and proceed (since using more bins would produce a worse solution by rule 3). In any case, call the final number of bins remaining r. (r = b - e.)
We now know exactly which items and which bins we will be using. We also know the order in which the items must be packed. Because of the ordering constraint, we can regard the decisions about which bins are to be left incompletely filled as the problem of how to inject "start-next-bin" instructions into the ordered list 1, 2, ... n of items. We can inject up to r-1 of these instructions.
This problem can be solved in O(nrs) time using dynamic programming. Essentially we compute the function:
f(i, j, k) = the score of the best solution in which the first i items occupy the first j boxes, with exactly k items in the jth box.
The recurrence is:
f(i, j, 0) = max(f(i, j-1, k)) over all 0 <= k <= s
f(i, j, k > 0) = f(i-1, j, k-1) + q(i, j)
Where q(i, j) is the quality score of assigning item i to box j. (As I mentioned in the comments on your post, you need to decide on some way to assign scores for a placement of any item i into any box j, presumably based on how well i's preferences are met. If it's easier to work with "badness" values than quality values, just change the max() to a min() and the -infinity boundary values below to infinity.)
The first equation says that the best score of a solution for the first i items whose rightmost bin is empty is equal to the best score that can be found by considering every solution for the first i items without that bin. These candidate solutions consist of all the ways that the previous bin can be packed, including leaving it empty too.
The second equation says that the best score for the first i items whose rightmost bin is not empty is found simply by adding the quality score for placing the last item to the best score for placing the first i-1 items in the same number of bins.
The boundary conditions are:
f(0, 0, 0) = 0
f(i, 0, k) = -infinity for all other i and k
After calculating the values of f(i, j, k) for each 0 <= i <= n, 0 <= j <= r and 0 <= k <= s and storing them in a table, f(n, r, s) will give the optimal score of the final solution. Although this only gives the score of the maximum, the actual optimal solution(s) can be found by tracing back through the f(i, j, k) matrix from the end, at each k = 0 step looking for the predecessor state (i.e. the alternative under the max()) that must have led to the current state. (It may happen that several alternatives under the max() give equal scores, in which case multiple optimal solutions exist, and any of these paths can be followed to find just one of them.)
This reminds me of the "Match" algorithm used to place medical school graduates in residency programs. What if you treat the items like students, their bin preferences like the rank lists, and the bins like hospitals?
Basically, you go through the list of items, and for each item, find the bin it prefers most. Check with the bin: do you have room for this item, and if not, do you prefer it more than any items you currently have?
If no, cross this bin off the item's list, and move to the item's next choice.
If yes, place this item in the bin, and put the displaced item (if any) back in the unmatched pool.
The difference between your problem and the residency match is that you wouldn't fix the bin's preferences up front. Instead you would use a rule that prefers items that bring the bin closest to 100% full.
My only concern is that this modification might make the algorithm unstable. But it's such a simple algorithm, it's probably worth trying.
This is a bipartite matching problem and can be solved in polynomial time.
http://en.wikipedia.org/wiki/Matching_(graph_theory)#Maximum_matchings_in_bipartite_graphs