Thought process to solve tree based Dynamic Programming problems

I am having a very hard time understanding tree based DP problems. I am fairly comfortable with array based DP problems but I cannot come up with the correct thought process for tree based problems and I was hoping somebody could please explain their thought process.

I will talk about my thought process behind array based problems and then explain my troubles with tree based DP problems.

My thought process for array problems

The way I think about DP in array based problems is as follows. Let us consider a problem like Minimum Path Sum. Here the objective is to get from the top left to bottom right positions in a matrix such that we minimize the cost of the path. We can only move left and right.

The way I would approach problems like this is as follows:

  • First I would construct a recurrence. In this case the recurrence is as follows

The recurrence is:

f(i, j) = a[i][j] // if i == m and j == n f(i, j) = a[i][j] + f(i, j+1) // if i == m f(i, j) = a[i][j] + f(i+1, j) // if j == n f(i, j) = a[i][j] + Math.min( f(i, j+1), f(i+1, j) ) // Otherwise 
  • Next I look at the last equation f(i, j) = a[i][j] + Math.min( f(i, j+1), f(i+1, j) ) which tells me the problem can be solved using DP as there are overlapping subproblems in f(i+1, j) and f(i, j+1). There is also an optimal substructure.

  • I can also tell the time/space complexity just by looking at the recurrence.

    • Because we must compute all states which is all (i,j) pairs and because time per state is O(1) (adding a[i][j] to result) the time complexity is O(n^2).
    • Looking at the recurrence, i depends only on i+1 and not on i+2, i+3 … similarly j depends only on j+1 and not on j+2, j+3… so we can get away with using only 1 extra row (either i+1 or j+1) instead of the entire matrix so space complexity is O(n).

Hence I would come up with a n^2 time and n space solution. I can do this without any problems.

My thought process for tree problems

However I am having a hard time applying the same thought process to tree based DP problems. As an example let us consider the problem Diameter of Binary Tree where the objective is to find the longest path between any 2 nodes in the tree.

I can come up with a recurrence for this problem which is as follows:

f(n) = 0 // if n == null f(n) = max( 1+height(n.left) + height(n.right),         // longest path passing through root             f(n.left),                                  // longest path in left subtree             f(n.right)                                  // longest path in right subtree 

Because f(n.left) for example is computed by doing 1+height(n.left.left) + height(n.left.right) I can tell that DP must be used.

So my approach would be to create a cache of size ‘n’ that stores all the heights of the nodes. So the space complexity would be O(n).

However the optimal solution of this problem has a space complexity of O(1) and I am having a hard time figuring that out just by looking at the recurrence. How does the recurrence tell you that space complexity can be reduced and that O(1) space is enough and O(n) is not needed? How do you know what value(s) to store in this case? In array based problems I can get the answers to both these questions just by looking at the recurrence but for tree based dp it is not so obvious to me.

My questions:

  1. What can you tell about this problem just by looking at the recurrence for the tree problem? Putting aside my own thought process, if I gave you this recurrence and nothing else what conclusions would you reach and how would you write the program? I am curious about your thought process.

  2. For array based problems I can tell just by looking at the recurrence both how much space I needed to solve the problem AND what exactly I needed to store (I need to store values of row i+1 in min path sum and nothing else). How can I do the same for the tree problem?


How to adjust PrecisionGoal and AccuracyGoal to solve my equation using NDSolve[]?

This is a very interesting question. I’m solving a differential equation to get a function z[$ \rho$ ], the expected solution needs to start from $ z[\rho_c]=z[\frac{13}{5}]=\frac{24195890215702774518563237183870329}{8112963841460668169578900514406400}\approx 2.98$ , until when the function $ z[\rho]$ hits on the $ \rho$ -axis. The code is as follows:

precTmp = 4 MachinePrecision; sol = (\[Rho]c = 13/5;    vt = Sqrt[zt^2 - \[Rho]c^2] - zt;     ParametricNDSolveValue[{(1 - z[\[Rho]]^3)^2/z[\[Rho]] +         3/2 z[\[Rho]]^2 Derivative[1][          z][\[Rho]]^2 + (1 - z[\[Rho]]^3) (Derivative[1][z][\[Rho]]^2/           z[\[Rho]] + (z^\[Prime]\[Prime])[\[Rho]]) + (        169 (-(13/5) + zt)^2 (13/5 +            zt)^2 z[\[Rho]]^2 (-3 z[\[Rho]]^2 +            2 (z^\[Prime]\[Prime])[\[Rho]]))/(200 zt^2) == 0,       Derivative[1][z][13/5] == -((13 (2 - (-(169/25) + zt^2)^(3/2)))/(        10 Sqrt[-(169/25) + zt^2])), z[13/5] == Sqrt[-(169/25) + zt^2],       WhenEvent[z[\[Rho]] == 10^-3, "StopIntegration"],       WhenEvent[z[\[Rho]] == 1.1` Sqrt[zt^2 - \[Rho]c^2],        "StopIntegration"]}, z, {\[Rho], \[Rho]c, R + 1}, {zt},      WorkingPrecision -> precTmp, AccuracyGoal -> precTmp/2, MaxSteps -> 10^6,      Method -> "StiffnessSwitching"]); 

Note that we can adjust the parameter precTmp to ramp up the precision. To check the correctness of the solution, it’s natural to plot the solution out, as follows:

ztValue = 24195890215702774518563237183870329/   8112963841460668169578900514406400; Plot[sol[ztValue][\[Rho]], {\[Rho], sol[ztValue]["Domain"][[1, 1]],    sol[ztValue]["Domain"][[1, 2]]}, PlotRange -> All,   WorkingPrecision -> precTmp] 

The first thing I can’t understand is that the shape of the solution $ z[\rho]$ is different when I change the precision, for example, taking precTmp to be 3 MachinePrecision, 5 MachinePrecision, or 6 MachinePrecision, the solution is decreasing and hit on $ \rho$ -axis, but when taking precTmp to be 4 MachinePrecision, 8 MachinePrecision or 12 MachinePrecision, the solution turns out to be increasing. My first question is which one of them is the correct answer?

The second question that I can’t understand is when providing a PrecisionGoal in ParametricNDSolveValue[] function, for example, we take precTmp = 4 MachinePrecision again, and add PrecisionGoal->precTmp, the solution becomes a decreasing function, which is originally an increasing one. I’ve already learned the difference between precision and accuracy, which can somehow be regarded to be equivalent as long as the function doesn’t go to zero. Thus my second question is how to understand the difference brought by the PrecisionGoal set here.

Would a difficult to access “Key” be an option to securely solve the Apple vs. FBI problem?

In recent times, there has been an escalating demand by legislators in the US and the world around to be able to decrypt phones that come pre-configured with strong encryption. Key escrow is commonly suggested as a solution, with the risk seeming to arise out of the escrow agent misusing or not appropriately securing the keys — allowing for remote, illegal, or surreptitious access to the secured data.

Could a system secure from remote attack be devised by adding an offline tamper-evident key to the device? This could be an unconnected WLCSP flash chip or a barcode within the device with the plaintext of a decryption key.

I recognize the evil maid attack, but presume a tamper seal could be made sufficiently challenging to thwart all but the most motivated attackers from surreptitious access to the data.

What would be lost in this scheme relative to the current security afforded by a consumer-grade pre-encrypted device (cf. iPhone)? Bitcoin, Subpoena efficacy, and other scenarios that seem fine with “smash and grab” tactics come to mind.

Solve Performmance Tuning

I’m facing a Problem in Mathematica, where I have to solve a large number of equations generated by an AppendTo.

n = 200; dz = 1000/n; RollLM[l_, EM_, Ixx_, p_] := {{1, l, Power[l, 2] / (2 EM Ixx), Power[l, 3] / (6 EM Ixx), p Power[l, 4] / (24 EM Ixx)}, {0, 1, l / (EM Ixx), Power[l, 2] / (2 EM Ixx), p Power[l, 3] / (6 EM Ixx)}, {0, 0, 1, l , p Power[l, 2] / 2}, {0, 0, 0, 1, p l}, {0, 0, 0, 0, 1}}; BearLM[d_, c_] := {{1, 0, 0, 0, 0}, {0, 1, 0, 0, 0}, {0, d, 1, 0, 0}, {-c, 0, 0, 1, 0}, {0, 0, 0, 0, 1}}; pBF = Function[z, Piecewise[{{989000/500, 250 <= z <= 750}}]];  Vec1A = {vA, PhiA, 0, 0, 1}; Vec2A = BearLM[810000, 850000].Vec1A; VecAList = {Vec2A}; Do[AppendTo[VecAList, RollLM[dz, 210000, 262440000 \[Pi], pBF[(i-0.5)dz]] .VecAList[[i]]], {i, n}]; Vec3A = BearLM[810000, 850000].Last[VecAList]; SolA = Solve[{Vec3A[[3]] == 0, Vec3A[[4]] == 0},{vA,PhiA}][[1]] 

I’m now trying to tune this code performancewise and first thought about switching from AppendTotoo Reap & Sowbut the most time consuming factor is the solving. Is there any option how to speed this up?

Also I already asked a similar question here: Switching from AppendTo to Reap & Sow

Trouble implementing outflow boundary condition when trying to solve a pde using NDSolve

Trying to solve the following pde: $ \partial_{t}y + c\partial_{c}y = 0$ (for simplicity $ c=1$ ).

For the initial data I am using a Gaussian. The problem surges when I am trying to implement the outflow boundary condition as it was suggested to me, namely $ \frac{\partial{y}}{\partial{t}} =0$ at $ x =0$ .

So far my code is pretty simple:

 v = 1 ; L = 2;   With[{y = y[t, x]},   eq = D[y, t] + vD[y, x] == 0;  ic = y == Exp[-x^2] /. t -> 0;  bc = {D[y, x] == 0 /. x -> 0 }];    mol[n_Integer, o_: "Pseudospectral"] := {"MethodOfLines",   "SpatialDiscretization" -> {"TensorProductGrid", "MaxPoints" -> n,   "MinPoints" -> n, "DifferenceOrder" -> o}};   sol = NDSolveValue[{eq, ic, bc}, y, {t, 0, 1}, {x, 0, L},   Method -> mol[100, 4]];  {t0, tend} = sol["Domain"][[1]];   Manipulate[  Plot[sol[t, x], {x, 0, L}, PlotRange -> {-10, 10}], {t, 0, tend}]; 

It stems from answers to questions previously asked here, and I intend later to test finite difference methods (BTCS/FTCS) as done in Schemes for nonlinear advection equation.

However I am not being able to evolve the equation do to confusion when trying to implement the BC, I get the following:

  NDSolveValue: Boundary condition $  y^{(0,1)}[t,0]$   should have derivatives of order lower than the differential order of the partial    differential equation.  

This is expected as I am not sure what would be the best way to impose BC on the problem.

If anyone has any suggestions they would be welcolmed.


Solve a recurrence relation

I have this recurrence:

$ $ f\left(i, j \right) = af\left(i + 1, j \right) + bf\left(i – 1, j \right)+ cf\left(i, j + 1 \right) + cf\left(i, j – 1 \right) $ $ where $ a+b+c=1$ and $ a,b,c>0$ , $ j=0,1$ , $ i=1,2,3,..,N+1$

Subject to conditions: $ $ f(i,2)=f(i,-1)=0$ $ $ $ f(0,j)=f(N+2,j)=0$ $ $ $ f(N+1,1)=1$ $ $ $ f(1,1)=0$ $

I need to solve or at least find the values of $ f(1,0)$ and $ f(2,1)$ in terms of $ N,a,b,c$ . I tried to find it through generating function but without success.

I appreciate any help.

What problem does the Yo-yo algorithm solve that depth-first search does not?

The Yo-yo algorithm has some requirements, two of which draw my attention:

  • Each node has an initial distinct value (source)
  • The algorithm is started by an initiator (source)

These conditions also make it possible to do a depth-first search for the minimum ID, and the algorithm is simpler. So, why use the Yo-yo algorithm?

What does a model X need to be so that one program of $X/O(1)$ solve in $X$?

Let $ X=P$ , then we can have function

L = len(input) k = 0 while (L>0):     k = k+1     L = log2(L) if (k mod 4 == c):     for 2^len(input):         pass return 1 

which, for every $ n$ , we have a $ c\in \{0,1,2,3\}$ , making all inputs shorter than $ n$ runs in $ Poly(n)$ time; but no matter what $ c$ is, it doesn’t run in polynomial time. Yet, let $ X=R$ , then proof that finding is possible is shown here.

Note that function

return 1 

always return the same value and is polynomial time, but we require to select one rather than create one program. Proof that creating is possible is shown here.

So what should $ X$ be so that, if solver needs to consider smaller inputs, at least one of the limit(s) of $ X/O(1)$ solve problem in $ X$ ? What should $ X$ be so that, All of the limit(s) of $ X/O(1)$ solve problem in $ X$ ?

What algorithm may be used to solve the re-assembly of shredded papers?

I was once given this question in an interview:

Suppose a piece of paper has 80 columns of alphabets with a fixed size font, and now the paper is shredded vertically, into 80 vertical pieces (so each piece can show a series of alphabets going down vertically), and there are 300 pages of such paper shredded total. Assume they are just English words with no proper names (for people / places). Find a method to reassemble all the papers and give the O() complexity time and space.

I proposed a solution where we take one piece of stripe, and go down the row to match for consecutive occurring alphabets, for a proper stripe. So we would build up a dictionary of all English words, for example, for the word Apple, that will mean ap, pp, pl, and le are all valid. And if we go down both stripes, we should expect most (or all) of them match with each other.

But doing this way, it looks like it would be (180 * 300)! = 24000! (factorial) steps, before we can finish the task? Is there a better way to solve this?