Related
Please, I would like to find the maximum sum with only one value per row. I already made the resolution by brute force and it is O (N^5). Now I would like to find a way with dynamic programming or another way to reduce the complexity.
For example:
Matrix:
100 5 4 3 1
90 80 70 60 50
70 69 65 20 10
60 20 10 5 1
50 45 15 6 1
Solution for 5 sets:
100 + 90 + 70 + 60 + 50 = 370
100 + 90 + 69 + 60 + 50 = 369
100 + 90 + 70 + 60 + 45 = 365
100 + 90 + 65 + 60 + 50 = 365
100 + 90 + 69 + 60 + 45 = 364
Sum: 1833
example for the sum with brute force:
for(int i=0; i<matrix[0].size(); i++) {
for(int j=0; j<matrix[1].size(); j++) {
for(int k=0; k<matrix[2].size(); k++) {
for(int l=0; l<matrix[3].size(); l++) {
for(int x=0; x<matrix[4].size(); x++) {
sum.push_back(matrix[0][i] + matrix[1][j] + matrix[2][k] + matrix[3][l] + matrix[4][x]);
}
}
}
}
}
sort(sum.begin(), sum.end(), mySort);
Thanks!
You can solve it in O(k*log k) time with Dijkstra's algorithm. A node in a graph is represented by a list with 5 indexes of the numbers in the corresponding rows of the matrix.
For example in the matrix
100 5 4 3 1
90 80 70 60 50
70 69 65 20 10
60 20 10 5 1
50 45 15 6 1
the node [0, 0, 2, 0, 1] represents the numbers [100, 90, 65, 60, 45]
The initial node is [0, 0, 0, 0, 0]. Every node has up to 5 outgoing edges increasing 1 of the 5 indexes by 1, and the distance between nodes is the absolute difference in the sums of the indexed numbers.
So for that matrix the edges from the node [0, 0, 2, 0, 1] lead:
to [1, 0, 2, 0, 1] with distance 100 - 5 = 95
to [0, 1, 2, 0, 1] with distance 90 - 80 = 10
to [0, 0, 3, 0, 1] with distance 65 - 20 = 45
to [0, 0, 2, 1, 1] with distance 60 - 20 = 40
to [0, 0, 2, 0, 2] with distance 45 - 15 = 30
With this setup you can use Dijkstra's algorithm to find k - 1 closest nodes to the initial node.
Update I previously used a greedy algorithm, which doesn't work for this problem. Here is a more general solution.
Suppose we've already found the combinations with the top m highest sums. The next highest combination (number m+1) must be 1 step away from one of these, where a step is defined as shifting focus one column to the right in one of the rows of the matrix. (Any combination that is more than one step away from all of the top m combinations cannot be the m+1 highest, because you can convert it to a higher one that is not in the top m by undoing one of those steps, i.e., moving back toward one of the existing combinations.)
For m = 1, we know that the "m highest combinations" just means the combination made by taking the first element of each row of the matrix (assuming each row is sorted from highest to lowest). So then we can work out from there:
Create a set of candidate combinations to consider for the next highest position. This will initially hold only the highest possible combination (first column of the matrix).
Identify the candidate with the highest sum and move that to the results.
Find all the combinations that are 1 step away from the one that was just added to the results. Add all of these to the set of candidate combinations. Only n of these will be added each round, where n is the number of rows in the matrix. Some may be duplicates of previously identified candidates, which should be ignored.
Go back to step 2. Repeat until there are 5 results.
Here is some Python code that does this:
m = [
[100, 5, 4, 3, 1],
[90, 80, 70, 60, 50],
[70, 69, 65, 20, 10],
[60, 20, 10, 5, 1],
[50, 45, 15, 6, 1]
]
n_cols = len(m[0]) # matrix width
# helper function to calculate the sum for any combination,
# where a "combination" is a list of column indexes for each row
score = lambda combo: sum(m[r][c] for r, c in enumerate(combo))
# define candidate set, initially with single highest combination
# (this set could also store the score for each combination
# to avoid calculating it repeatedly)
candidates = {tuple(0 for row in m)}
results = set()
# get 5 highest-scoring combinations
for i in range(5):
result = max(candidates, key=score)
results.add(result)
candidates.remove(result) # don't test it again
# find combinations one step away from latest result
# and add them to the candidates set
for j, c in enumerate(result):
if c+1 >= n_cols:
continue # don't step past edge of matrix
combo = result[:j] + (c+1,) + result[j+1:]
if combo not in results:
candidates.add(combo) # drops dups
# convert from column indexes to actual values
final = [
[m[r][c] for r, c in enumerate(combo)]
for combo in results
]
final.sort(key=sum, reverse=True)
print(final)
# [
# [100, 90, 70, 60, 50]
# [100, 90, 69, 60, 50],
# [100, 90, 70, 60, 45],
# [100, 90, 65, 60, 50],
# [100, 90, 69, 60, 45],
# ]
If you want just maximum sum, then sum maximum value at each row.
That is,
M = [[100, 5, 4, 3, 1],
[90, 80, 70, 60, 50],
[70, 69, 65, 20, 10],
[60, 20, 10, 5, 1],
[50, 45, 15, 6, 1]]
sum(max(row) for row in M)
Edit
It is not necessary to use dynamic programming, etc.
There is simple rule: select next number considering difference between the number and current number.
Here is a code using numpy.
import numpy as np
M = np.array(M)
M = -np.sort(-M, axis = 1)
k = 3
answer = []
ind = np.zeros(M.shape[0], dtype = int)
for _ in range(k):
answer.append(sum(M[list(range(M.shape[0])), ind]))
min_ind = np.argmin(M[list(range(len(ind))), ind] - M[list(range(len(ind))), ind+1])
ind[min_ind] += 1
Result is [370, 369, 365].
I am practicing Dynamic Programming. I am focusing on the following variant of the coin exchange problem:
Let S = [1, 2, 6, 12, 24, 48, 60] be a constant set of integer coin denominations. Let n be a positive integer amount of money attainable via coins in S. Consider two persons A and B. In how many different ways can I split n among persons A and B so that each person gets the same amount of coins (disregarding the actual amount of money each gets)?
Example
n = 6 can be split into 4 different ways per person:
Person A gets {2, 2} and person B gets {1, 1}.
Person A gets {2, 1} and person B gets {2, 1}.
Person A gets {1, 1} and person B gets {2, 2}.
Person A gets {1, 1, 1} and person B gets {1, 1, 1}.
Notice that each way is non-redundant per person, i.e. we do not count both {2, 1} and {1, 2} as two different ways.
Previous research
I have studied at very similar DP problems, such as the coin exchange problem and the partition problem. In fact, there are questions in this site referring to almost the same problem:
Dynamic Programming for a variant of the coin exchange - Here, OP studies the recursion relationship, but seems confused introducing the parity constraint.
Coin Change :Dynamic Programming - Here, OP seems to pursue the reconstruction of the solution.
Coin change(Dynamic programming) - Here, OP seems to also pursue the reconstruction of the solution.
https://cs.stackexchange.com/questions/87230/dynamic-programming-for-a-variant-of-the-coin-exchange-problem - Here, OP seems to ask about a similar problem, yet parity, i.e. splitting into two persons, becomes the main issue.
I am interested mostly in the recursion relation that could help me solve this problem. Defining it will allow me to easily apply either a memoization of a tabulation approach to design an algorithm for this problem.
For example, this recursion:
def f(n, coins):
if n < 0:
return 0
if n == 0:
return 1
return sum([f(n - coin, coins) for coin in coins])
Is tempting, yet it does not work, because when executed:
# => f(6, [1, 2, 6]) # 14
Here's an example of a run for S' = {1, 2, 6} and n = 6, in order to help me clarify the pattern (there might be errors):
This is what you can try:
Let C(n, k, S) be the number of distinct representations of an amount n using some k coins from S.
Then C(n, k, S) = sum(C(n - s_i, k - 1, S[i:])) The summation is for every s_i from S. S[i:] means all the elements from S starting from i-th element to the end - we need this to prevent repeated combinations.
The initial conditions are C(0, 0, _) = 1 and C(n, k, _) = 0 if n < 0 or k < 0 or n > 0 and k < 1 .
The number you want to calculate:
R = sum(C(i, k, S) * C(n - i, k, S)) for i = 1..n-1, k = 1..min(i, n-i)/Smin where Smin - the smallest coin denomination from S.
The value min(i, n-i)/Smin represents the maximum number of coins that is possible when partitioning the given sum. For example if the sum n = 20 and i = 8 (1st person gets $8, 2nd gets $12) and the minimum coin denomination is $2, the maximum possible number of coins is 8/2 = 4. You can't get $8 with >4 coins.
Here is a table implementation and a little elaboration on algrid's beautiful answer. This produces an answer for f(500, [1, 2, 6, 12, 24, 48, 60]) in about 2 seconds.
The simple declaration of C(n, k, S) = sum(C(n - s_i, k - 1, S[i:])) means adding all the ways to get to the current sum, n using k coins. Then if we split n into all ways it can be partitioned in two, we can just add all the ways each of those parts can be made from the same number, k, of coins.
The beauty of fixing the subset of coins we choose from to a diminishing list means that any arbitrary combination of coins will only be counted once - it will be counted in the calculation where the leftmost coin in the combination is the first coin in our diminishing subset (assuming we order them in the same way). For example, the arbitrary subset [6, 24, 48], taken from [1, 2, 6, 12, 24, 48, 60], would only be counted in the summation for the subset [6, 12, 24, 48, 60] since the next subset, [12, 24, 48, 60] would not include 6 and the previous subset [2, 6, 12, 24, 48, 60] has at least one 2 coin.
Python code (see it here; confirm here):
import time
def f(n, coins):
t0 = time.time()
min_coins = min(coins)
m = [[[0] * len(coins) for k in xrange(n / min_coins + 1)] for _n in xrange(n + 1)]
# Initialize base case
for i in xrange(len(coins)):
m[0][0][i] = 1
for i in xrange(len(coins)):
for _i in xrange(i + 1):
for _n in xrange(coins[_i], n + 1):
for k in xrange(1, _n / min_coins + 1):
m[_n][k][i] += m[_n - coins[_i]][k - 1][_i]
result = 0
for a in xrange(1, n + 1):
b = n - a
for k in xrange(1, n / min_coins + 1):
result = result + m[a][k][len(coins) - 1] * m[b][k][len(coins) - 1]
total_time = time.time() - t0
return (result, total_time)
print f(500, [1, 2, 6, 12, 24, 48, 60])
A polygonal number is defined as being a number represented as dots arranged in the shape of a regular polygon.
For example:
Triangular numbers are 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, ...
Square numbers are 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100, ...
Pentagonal number are 0, 1, 5, 12, 22, 35, 51, 70, 92, 117, ...
and so on...
There are well known formulas to calculate any of these numbers. To calculate the n-th s-gonal number, one can use the formula (n^2 * (s - 2) - (n * (s - 4))) / 2
What I would like to know is, is there an efficient way to check if a given number is s-gonal for a given s?
The obvious approach would be to take successive values from a function that generates s-gonal numbers until either n is found, or the values exceed n, however this has linear time complexity.
I know there are formulas that can be used to determine if a number is s-gonal for specific values of s, but I would like one that works for any s.
Based on Wikipedia's article on Polygonal numbers I can up with the following predicate that seems to solve the problems I ran into with the one the OP proposed:
def isPolygonal(s, x):
''' Check if x is a s-gonal number '''
assert s > 2 and s % 1 == 0 and x % 1 == 0
# Determine if x is some nth s-gonal number,
# fail if n doesn't come out a whole number
n = (sqrt(8 * (s - 2) * x + (s - 4) ** 2) + (s - 4)) / (2 * (s - 2))
return n % 1 == 0
This is a game for 2 people to remove numbers from a list. A player will lose if the player picks up the last number. Given the 2 rules of removing numbers in a list,
Prolog search for possible combination for subtracting 2 elements from a list
Prolog possible removal of elements in a list
There is a question,
write a predicate win(S), that succeed if S is a winning position for
the player whose turn it is to play and fails otherwise. Besides
giving the correct answers, your code for this should avoid evaluating
the same position more than once. For example, there are only 960
positions that can be reached from [30,30], but many billions of games
that could be played starting from there...
I am really confused how can [30,30] reach 960 positions. According to the 2 rules, if I subtract N from one element only, I can only reach 60 states.
Y = [29, 30] or Y = [30, 29]
Y = [28, 30] or Y = [30, 28]
Y = [27, 30] or ...
...
Y = [1, 30]
Y = [30]
Or If I subtract N from 2 elements, I can only reach 30 states..
Y = [29, 29]
Y = [28, 28]
...
Y = [1, 1]
Y = []
I am really confused how 960 position can be reached. Yet, win(S), will evaluate whether the S is a winning position... Does it mean that current S can directly leads to [1] by just one move? or the current S can lead to [1] by multiple moves?
I need to find numbers in a list, which make up a specific total:
Sum: 500
Subtotals: 10 490 20 5 5
In the end I need: {10 490, 490 5 5}
How do you call this type of problem? Are there algorithms to solve it efficiently?
This is Knapsack problem and it is an NP-complete problem, i.e. there is no efficient algorithm known for it.
This is not a knapsack problem.
In the worst case, with N subtotals, there can be O(2^N) solutions, so any algorithm in worst-case will be no better than this (thus, the problem doesn't belong to NP class at all).
Let's assume there are no non-positive elements in the Subtotals array and any element is no greater than Sum. We can sort array of subtotals, then build array of tail sums, adding 0 to the end. In your example, it will look like:
Subtotals: (490, 20, 10, 5, 5)
PartialSums: (530, 40, 20, 10, 5, 0)
Now for any "remaining sum" S, position i, and "current list" L we have problem E(S, i, L):
E(0, i, L) = (print L).
E(S, i, L) | (PartialSums[i] < S) = (nothing).
E(S, i, L) = E(S, i+1, L), E(S-Subtotals[i], j, L||Subtotals[i]), where j is index of first element of Subtotals lesser than or equal to (S-Subtotals[i]) or i+1, whichever is greater.
Our problem is E(Sum, 0, {}).
Of course, there's a problem with duplicates (if there were another 490 number in your list, this algorithm would output 4 solutions). If that's not what you need, using array of pairs (value, multiplicity) may help.
P.S. You may also consider dynamic programming if size of the problem is small enough:
Start with set {0}. Create array of sets equal to array of subtotals in size.
For every subtotal create a new set from previous set by adding subtotal value. Remove all elements greater than Sum. Merge it with previous set (it will essentially be the set of all possible sums).
If in the final set doesn't have Sum, then there is no solution. Otherwise, you backtrack solution from Sum to 0, checking whether previous set contains [value] and [value-subtotal].
Example:
(10, 490, 20, 5, 5)
Sets:
(0)
(0, 10)
(0, 10, 490, 500)
(0, 10, 20, 30, 490, 500) (510, 520 - discarded)
(0, 5, 10, 15, 20, 25, 30, 35, 490, 495, 500)
(0, 5, 10, 15, 20, 25, 30, 35, 40, 490, 495, 500)
From last set: [500-5] in previous set, [495-5] in previous set, [490-20] not in previous set ([490] is), [490-490] is 0, resulting answer {5, 5, 490}.