I'm studying for an exam, and the professor gave us a bunch of practice problems that we don't have the answer to. This is one of them, but I've been working on it forever and don't even know if I'm headed in the right direction. I'm not even asking for an answer - just someone to point me in the right direction?
I'm supposed to develop a dynamic programming algorithm (of O(n^2)) for the following function that finds the expected number of acyclic orientations in a graph using this recurrence.
I think I'm supposed to solve the recurrence using the Master Theorem or unfold-and-sum to simplify/solve the recurrence, and then develop the algorithm from there? Any hints or clues would be much appreciated. Thanks!
As you provide both formula you needed, then you can find what you need by following steps:
EDITED:
For point #2, which is to calculate (1+x)^i(n-i)
for i
in [0,n]
, though the largest power can be up to n^2, but indeed there is only n
instances, and we do not need to calculate all power up to n^2.
Let's write down the sequence of the power for different i
in [0,n]
: 0, n-1, 2(n-2), 3(n-3)...(n-1)(1)
And we can precompute them in a way like
function repeat_squaring(base, power){
//O(lg (power))
}
for(int i=0; i<=n; i++){
repeat_squaring(1+x, i*(n-i));
}
So now, what is the complexity to compute them in total? Just sum them up!
T = O(lg n + lg(2n) + lg(3n) + ... lg(n*n)) = O(summation(lg(i)) + nlg(n)) = O(lg(n!) + nlgn) = O(n lg n)
For the complexity of O(lg(n!))
, there is two way to reason it, one is the famous Stirling's approximation, another way is this post: log(n!)
EDITED2: For shrinking n problem
I observe the pattern of the (1+x)^(i(N-i))
for N = n, n-1, n-2
..etc
You can see that we can derive the term (1+x)^j
for smaller A_n()
from some already calculated (1+x)^(i(N-i))
We use O(n lg n)
as described above to pre-calculate for the powers when N = n
first, also we can use O(n lg n)
to pre-calculate all (1+x)^i
for i in [0..n]
Now as the pattern shown, the term (1+x)^(i(N-i))
for consecutive N
values (n vs n-1, n-1 vs n-2 ...
), you can indeed use O(1)
to multiply / divided by some (1+x)^i
where i in [0..n]
(depends on your implementation, either bottom-up / top-down)
So I still think you only need O(n lgn)
to pre-compute those powers, and use O(1)
to transform them into other powers dynamically when you needed. (You can think as you are doing dynamic programming on both (1+x)^(i(N-i))
and A_i()
at the same time)
TL;DR
Of course, if you do not want things being too complicated, you can just use O(n^2)
to do a separate DP on (1+x)^(i(N-i))
for all N in [1..n]
// High Level Psuedo Code
var nCr[][];
var p[]; // (1+x)^0, (1+x)^1 ... (1+x)^n
var b[]; // (1+x)^0, (1+x)^(n-1), (1+x)^2(n-2)...
var power[n][i] // (1+x)^i(n-i), power[max_n][x] = b[] actually
var A[] // main problem!
Precompute nCr[][]; // O(n^2)
Precompute p[]; // O(n lg n)
Precompute b[]; // O(n lg n)
Precompute power[n][i] {
// O(n^2)
for all x, for all i
power[x][i] = power[x+1][i]/p[i]
}
Compute A using all those precompute arrays, O(n^2)