I am trying to implement Knuth's optimal binary search tree which can run in O(n^2) time. I have the code which is running in O(n^3).
float P[N + 1] = {0, .13, .12, .15, .05, .12, .10, .08, .09, .03, .13};
float sum[N + 1] = {0, .13, .25, .40, .45, .57, .67, .75, .84, .87, 1.00};
float M[N + 1][N + 1];
int root[N + 1][N + 1];
int s, i, j;
float temp;
for (s = 0; s <= N; s++){
for (i = 0; i <= N; i++){
M[s][i] = 0;
root[s][i] = 0;
}
}
for (s = 2; s <= N; s++){
for (i = 1; i <= N - s + 1; i++){
M[s][i] = N;
for (j = i; j <= i + s - 1; j++){
temp = M[j - i][i] + M[i + s - j - 1][j + 1]+ sum[i + s - 1] - sum[i - 1] - P[j];
if (M[s][i] > temp){
M[s][i] = temp;
root[s][i] = j;
}
}
}
}
M is the array of costs. P is the probability of each node. I get some ideas from: Dynamic Programming: Why Knuth's improvement to Optimal Binary Search Tree O(n^2)?. In my case, I tried to modify the third loop from for (j = i; j <= i + s - 1; j++) to for (j = root[s+1][i]; j <= root[s][i-1]; j++). But it doesn't work. Could someone give me some clue on this problem?
You're supposed to be computing the costs of the optimal subtrees in non-decreasing order by size, so when you're filling in M[s][i] --- the minimum cost of a subtree of size s whose leftmost key has index i --- you haven't filled in M[s+1][i] or root[s+1][i] yet.
Cheers,
Travis
PS "j <= root[s][i-1]" isn't quite right either.
Related
I have an engineering problem that requires me to partition a set of positive numbers to k parts, without changing the ordering of the numbers in the set, so that the sums of the parts are as equal as possible. I have understood that there are solutions to this 'linear partitioning problem'. In fact, I have successfully tried the dynamic programming solution already and it works.
Now the question: What if there is a maximum size limitation for the parts? ("No part can have more than m items") Can it be added e.g. to the DP solution or can some other technique be used for solving this.
Appreciate all comments, links and suggestions.
Edit: I added a crude sketch of the original algorithm below
// Partition number array seq to k parts with max number of m items in each part
public void LinearPartition(double[] seq, int k, int m)
{
double[,] table;
int[,] solution;
int n = seq.Length - 1;
linearPartitionTable(seq, k, m, out table, out solution);
k = k - 2;
while (k >= 0)
{
Console.WriteLine(solution[n - 1, k] + 1);
n = solution[n - 1, k];
k--;
}
}
private void linearPartitionTable(double[] seq, int k, int m,
out double[,] table, out int[,] solution)
{
int n = seq.Length;
table = new double[n, k];
solution = new int[n - 1, k - 1];
table[0, 0] = 0;
for (int i = 1; i < n; i++)
table[i, 0] = seq[i] + table[i - 1, 0];
for (int j = 0; j < k; j++)
table[0, j] = seq[0];
for (int i = 1; i < n; i++)
{
for (int j = 1; j < k; j++)
{
double currentMin = double.MaxValue;
int minX = 0;
for (int x = 0; x < i; x++)
{
double cost = Math.Max(table[x, j - 1], table[i, 0] - table[x, 0]);
if (cost < currentMin)
{
currentMin = cost;
minX = x;
}
}
table[i, j] = currentMin;
solution[i - 1, j - 1] = minX;
}
}
}
I read the article about
How to determine the longest increasing sub-sequence using dynamic programming
with this algorithm:
int maxLength = 1, bestEnd = 0;
DP[0] = 1;
prev[0] = -1;
for (int i = 1; i < N; i++)
{
DP[i] = 1;
prev[i] = -1;
for (int j = i - 1; j >= 0; j--)
if (DP[j] + 1 > DP[i] && array[j] < array[i])
{
DP[i] = DP[j] + 1;
prev[i] = j;
}
if (DP[i] > maxLength)
{
bestEnd = i;
maxLength = DP[i];
}
}
but i want to know how to solve this problem with this condition that we can take the arrays with joined integers.
For example: 1,5,3,1,5,6,7,8,1,2,9
we can have this set:1,3,5,6,7,8,12 for solution
that 12 is joint form 1 and 2
so conditions are:
The input array includes 1-9 numbers! and the integers can joined from few other integers.
Original problem
dp[i] = max(DP[j] + 1, a[j] < a[i])
Your problem
Let:
a[x,y] = a[x] + a[x + 1] + ... + a[y] (+ means concatenate)
So:
f[x,y] = max(DP[j] + 1, a[j] < a[x,y], j < x)
dp[i] = max(f[i,j], 0 <= j <= i) = max(
max(DP[j] + 1, a[j] < a[i], j < i) # f(i, i)
max(DP[j] + 1, a[j] < a[i-1, i], j < i - 1) # f(i-1, i)
...
)
If you still have some problems, please don't hesitate to leave a comment here.
I am trying to write an algorithm which will find a(0),..., a(n-1), given the values of n, x_1, ..., x_n, a(n), such that:
a(n)*p^n + a(n-1)*p^(n-1) + ... + a(1)*p + a(0) = a(n)(p-x_1)(p-x_2)...(p-x_n)
for all real p.
After multiplying a(n)(p-x_1)(p-x_2) I've thought of using Viete's formulas to find the coefficients.
But it turns out writing the code down isn't as obvious as I expected.
I want to use only the basics in my code - that is loops, if-s addition and multiplication - no ready/ complex functions.
Here are the formulas:
First, I would like to emphasise that I only need a pseudocode, and I do not care about defining arrays for the root and coefficients. That's why I will just write a(n), xn. Oh, and I hope it won't bother you very much if I start indexing from i=1 not i=0 in order to be in synch with the mathematical notation. In order to start with i=0 I would have to renumerate the roots and introduce more brackets.
And this is what I've come up with so far:
a(n-1)=0;
for(i=1; i <= n; i++){
a(n-1) = a(n-1) + x_i;
}
a(n-1) = -a(n)*a(n-1);
a(n-2)=0;
for(i=1; i <= n; i++){
for(j=i; j <= n; j++){
a(n-2) = a(n-2)+ x_i * x_j;
}
}
a(n-2) = -a(n)*a(n-2);
a(n-3)=0;
for(i=1; i <= n; i++){
for(j=i; j <= n; j++){
for(k=j; k <= n; k++){
a(n-3) = a(n-3)+ x_i * x_j * x_k;
}
}
}
a(n-3) = a(n)*a(n-3);
...
a(0)=1;
for(i=1; i<=n; i++){
a(0) = a(0) * x_i;
}
if(n%2 == 0) a(0) = a(n) * a(0);
else a(0) = -a(n) * a(0);
As you can see, it doesn't look good.
I would like to link all those loops into one loop, because without I cannot write the full code, I cannot fill the gap between a(0) and a(n-j) for a fixed j.
Could you help me out?
This is what I have, based on Nico Schertler's answer:
for(i=1; i<=n; i++)
{a(i)=1;
for(j=1; j <= n; j++)
{b(i)= clone( a(i) );
a(i) = a(i-1);
b(i) = x_j * b(i);
c(i) = a(i) - b(i);
}
}
Would it be the same if instead we wrote
for(i=1; i<=n; i++)
{a(i)=1; b(i)=1;
for(j=1; j <= n; j++)
{t = a(i) ;
a(i) = a(i-1);
b(i) = x_j * t;
c(i) = a(i) - b(i);
}
}
(this is how we for example swap two elements of an array, by keeping the value of a[i] in some variable t).
You can create the polynomial incrementally.
Start with p = 1. I.e. a(0) = 1.
In order to add a root, you have to multiply the current polynomial by x - x_i. This is:
p * (x - x_i) = p * x - p * x_i
So you need to support three operations:
1. Multiplication by x
This is quite simple. Just shift all coefficients by one to the left. I.e.
a(i ) := a(i - 1)
a(i - 1) := a(i - 2)
...
a(1 ) := a(0)
a(0 ) := 0
2. Multiplication by a scalar
This is equally simple. Multiply each coefficient:
a(i ) *= s
a(i - 1) *= s
...
3. Subtraction
Just subtract the respective coefficients:
c(i ) = a(i ) - b(i )
c(i - 1) = a(i - 1) - b(i - 1)
...
Altogether
Add root by root. First, clone your current polynomial. Then, do the operations as described above:
p := 1
for each root r
p' = clone(p)
multiply p with x
multiply p' with r
p := p - p'
next
A static function in c# for this purpose.
The roots of x^4-11x^3+44x^2-76x+48 are {2,2,3,4} and given the argument
roots = new Complex[4] {2, 2, 3, 4}
this function returns [48,-76,44,-11,1]
public static double[] FromRoots(Complex[] roots)
{
int N = roots.Length;
Complex[] coefs = new Complex[N + 1];
coefs[0] = -roots[0];
coefs[1] = 1.0;
for (int k = 2; k <= N; k++)
{
coefs[k] = 1.0;
for (int i = k - 2; i >= 0; i--)
{
coefs[i + 1] = coefs[i] - roots[k - 1] * coefs[i + 1];
}
coefs[0] *= -roots[k - 1];
if (Math.IEEERemainder(k, 2) == 1)
coefs[k] = -coefs[k];
}
double[] realCoefs = new double[N + 1];
for (int i = 0; i < N + 1; i++)
realCoefs[i] = coefs[i].Real; // Not sure about this part!
return realCoefs;
}
We all heard of bentley's beautiful proramming pearls problem
which solves maximum subsequence sum:
maxsofar = 0;
maxcur = 0;
for (i = 0; i < n; i++) {
maxcur = max(A[i] + maxcur, 0);
maxsofar = max(maxsofar, maxcur);
}
What if we add an additional condition maximum subsequence that is lesser M?
This should do this. Am I wright?
int maxsofar = 0;
for (int i = 0; i < n - 1; i++) {
int maxcur = 0;
for (int j = i; j < n; j++) {
maxcur = max(A[j] + maxcur, 0);
maxsofar = maxcur < M ? max(maxsofar, maxcur) : maxsofar;
}
}
Unfortunately this is O(n^2). You may speed it up a little bit by breaking the inner loop when maxcur >=M, but still n^2 remains.
This can be solved using dynamic programming albeit only in pseudo-polynomial time.
Define
m(i,s) := maximum sum less than s obtainable using only the first i elements
Then you can calculate max(n,M) using the following recurrence relation
m(i,s) = max(m(i-1,s), m(i-1,s-A[i]]+A[i]))
This solution is similar to the solution to the knapsack problem.
If all A[i] > 0, you can do this in O(n lg n): precompute partial sums S[i], then binary search S for S[i] + M. For instance:
def binary_search(L, x):
def _binary_search(lo, hi):
if lo >= hi: return lo
mid = lo + (hi-lo)/2
if x < L[mid]:
return _binary_search(lo, mid)
return _binary_search(mid+1, hi)
return _binary_search(0, len(L))
A = [1, 2, 3, 2, 1]
M = 4
S = [A[0]]
for a in A[1:]:
S.append(S[-1] + a)
maxsum = 0
for i, s in enumerate(S):
j = binary_search(S, s + M)
if j == len(S):
break
sum = S[j-1] - S[i]
maxsum = max(sum, maxsum)
print maxsum
EDIT: as atuls correctly points out, the binary search is overkill; since S is increasing, we can just keep track of j each iteration and advance from there.
Solveable in O(n log(n)). Using a binary search tree (balanced) to search for smallest value larger than sum-M, and then update min, and insert sum, by going from left to right. Where sum is the partial sum so far.
best = -infinity;
sum = 0;
tree.insert(0);
for(i = 0; i < n; i++) {
sum = sum + A[i];
int diff = sum - tree.find_smallest_value_larger_than(sum - M);
if (diff > best) {
best = diff;
}
tree.insert(sum);
}
print best
I found the following problem on the internet, and would like to know how I would go about solving it:
Problem: Integer Partition without Rearrangement
Input: An arrangement S of non negative numbers {s1, . . .
, sn} and an integer k.
Output: Partition S into k or fewer ranges, to minimize the maximum
of the sums of all k or fewer ranges,
without reordering any of the
numbers.*
Please help, seems like interesting question... I actually spend quite a lot time in it, but failed to see any solution..
Let's try to solve the problem using dynamic programming.
Note: If k > n we can use only n intervals.
Let consider d[ i ][ j ] is solution of the problem when S = {s1, ..., si } and k = j. So it is easy to see that:
d[ 0 ][ j ] = 0 for each j from 1 to k
d[ i ][ 1 ] = sum(s1...si) for each i from 1 to n
d[ i ][ j ] = minfor t = 1 to i (max ( d[i - t][j - 1], sum(si - t + 1...si)) for i = 1 to n and j = 2 to k
Now let's see why this works:
When there is no elements in the sequence it is clear that only one interval there can be (an empty one) and sum of its elements is 0. That's why d[ 0 ][ j ] = 0 for all j from 1 to k.
When only one interval there can be, it is clear that solution is sum of all elements of the sequence. So d[ i ][ 1 ] = sum(s1...si).
Now let's consider there are i elements in the sequence and number of intervals is j, we can assume that last interval is (si - t + 1...si) where t is positive integer not greater than i, so in that case solution is max ( d[i - t][j - 1], sum(si - t + 1...si), but as we want the solution be minimal we should chose t such to minimize it, so we will get minfor t = 1 to i (max ( d[i - t][j - 1], sum(si - t + 1...si)).
Example:
S = (5,4,1,12), k = 2
d[0][1] = 0, d[0][2] = 0
d[1][1] = 5, d[1][2] = 5
d[2][1] = 9, d[2][2] = 5
d[3][1] = 10, d[3][2] = 5
d[4][1] = 22, d[4][2] = 12
Code:
#include <algorithm>
#include <vector>
#include <iostream>
using namespace std;
int main ()
{
int n;
const int INF = 2 * 1000 * 1000 * 1000;
cin >> n;
vector<int> s(n + 1);
for(int i = 1; i <= n; ++i)
cin >> s[i];
vector<int> first_sum(n + 1, 0);
for(int i = 1; i <= n; ++i)
first_sum[i] = first_sum[i - 1] + s[i];
int k;
cin >> k;
vector<vector<int> > d(n + 1);
for(int i = 0; i <= n; ++i)
d[i].resize(k + 1);
//point 1
for(int j = 0; j <= k; ++j)
d[0][j] = 0;
//point 2
for(int i = 1; i <= n; ++i)
d[i][1] = d[i - 1][1] + s[i]; //sum of integers from s[1] to s[i]
//point 3
for(int i = 1; i <= n; ++i)
for(int j = 2; j <= k; ++j)
{
d[i][j] = INF;
for(int t = 1; t <= i; ++t)
d[i][j] = min(d[i][j], max(d[i - t][j - 1], first_sum[i] - first_sum[i - t]));
}
cout << d[n][k] << endl;
return 0;
}
This problem is taken verbatim from Steven Skiena's book "The Algorithm Design Manual". You can read the detailed discussion and his solution on Google Books. Better yet, buy the book.