According to wikipedia there's a possible modification to the Wagner-Fischer-algorithm that can calculate if the Levenshtein distance of two words is lower than a certain threshold, that is much quicker than the original one if that's all you want to know.
"By examining diagonals instead of rows, and by using lazy evaluation, we can find the Levenshtein distance in O(m (1 + d)) time (where d is the Levenshtein distance), which is much faster than the regular dynamic programming algorithm if the distance is small."
How does this solution work? I'm having a real hard time visualizing it since it feels like the value of any matrix cell depends on the values of the cells above, to the left and diagonally up to the left of it, so I'm not sure how to traverse the matrix using only a diagonal strip.
Second attempt at explanation:
Suppose that we're finding the distance between a length-m word and a length-n word. Let the matrix entries be indexed by [0, m] ร [0, n], where the (i, j) entry represents the edit distance between the length-i prefix of the length-m word and the length-j prefix of the length-n word.
We can view the dynamic program as finding the shortest path from (0, 0) to (m, n) in a directed graph whose vertices correspond to matrix entries, with length-1 rightward arcs and length-1 downward arcs and length-0 or length-1 diagonal arcs depending on whether the characters at i and j match. The idea, in a nutshell, is to use A* with the length difference heuristic H(i, j) = |(m - i) - (n - j)|. Then, we don't expand nodes whose A* value is greater than d. The result is that only some of the diagonals need to be opened:
o t h e r w o r d
t * * *
h * * *
e * * *
w * * *
o * * *
r * * *
d * * *
First attempt at explanation:
Each matrix entry (i, j) is lowerbounded by |i - j|, because that's a lowerbound on the number of non-diagonal moves needed to reach that state. The strip is every element whose coordinates (i, j) satisfies |i - j| โค d, which looks like
o t h e r w o r d
t * * *
h * * * *
e * * * * *
w * * * * *
o * * * * *
r * * * * *
d * * * * *
for d = 2. When the values for the blank elements off of the strip are needed, just use d. At the end, any strip entry that's โค d is valid, because the blank elements can only contribute d + 1, seeing as the upper left neighbor of a strip element is also on the strip.
For words of different lengths, we can actually apply the argument to the transpose and restrict to the strip like
o t h e r w o r d
t * * *
h * * *
e * * *
w * * *
o * * *
r * * *
d * * *
though the asymptotic running time is the same.
Related
Let h be a function which applies entrywise (e.g squaring, etc.). Let u and v be a 1d arrays of length k and A be a 2d array of shape n x m. Here l, n, and m can be very large!
Question. In numpy, what is an efficient way to compute the n x m array defined by B := v[0] * h(u[0] * A) + ... + v[k-1] * h(u[k-1] * A) ?
Observations
Of course, a naive solution is to just loop over the u[j]'s and v[j]'s and accumulate v[j] * h(u[j] * A).
Another solution which seems horrible (at least memory-wise is): B = np.sum(v[:, None, None] * h(u[:, None, None] * A), axis=0)
I am trying to solve this math concept programmatically. Given two points A and B, on a rounded grid, find the points along the line from A to B that intersect with the rounded grid. I have looked into fast voxel traversal algorithms but they do not seem to only get points that are on the grid, instead just any points that intersect.
Here is a picture that might better describe my problem:
I am given the two orange points on any line, but I want to be able to find the blue points that that line intersects with. As you can see, the blue points are rounded to the grid.
Let me know if I can clarify anything.
There's two points that we know will always be aligned with the grid: the points at the end of the segment. I'll call them a and b. The vector describing the segment is then b - a (v).
v can be reduced while maintaining the ratio of the vector as follows:
v' = v / gcd(v_x, v_y)
where v_x and v_y are the x and y-components of v respectively. v' is the smallest possible vector with the same orientation as v consisting solely of integer components. This means that a + v' * n will be aligned to the grid for any n we may pick.
So the points on the line that are grid-aligned would be [a + 0 * v', a + 1 * v', ..., a + m * v'], where a + m * v' = b must hold. So we need to solve
a_x + m * v'_x = b_x
a_y + m * v'_y = b_y
which lead to
m = (b_x - a_x) / v'_x
m = (b_y - a_y) / v'_y
However there are three possible edge-cases to consider here:
a == b: none of the equations are solveable (and there's exactly one grid-aligned point anyways)
v'_x == 0 or v'_y == 0: just pick the other equation to find m
Putting it all together as pythonesque pseudocode:
grid_aligned(a, b):
if a == b:
return [a]
v = b - a
v' = v / gcd(v.x, v.y)
if v'.x == 0:
m = v.y / v'.y
else:
m = v.x / v'.x
return [a + v' * n for n in 0..m]
In the Second Version of Data Structures and Algorithm Analysis in C,about the binary tree,there is the description that the mean depth of binary tree is O(sqrt(n)). I try to prove that,but I have no ideas about how to do that,Is there anyone who can help me?
Let us consider prefect binary tree (always splitting elements into 2):
*
* *
* * * *
* * * * * * * *
if I remove gaps and align to left:
*
**
****
********
You see that each depth level doubles the number of elements in it (in comparison to previous level) so:
n = 1 + 2 + 4 + 8 + ...
or:
n = sum( 2^i ) where i={ 0,1,2, ... ,level-1 }
So if you think of it (like binary number with all bits set) its the same as:
n = 2^level -1
so extracting level from it:
n = 2^level -1 // +1
n+1 = 2^level // log2
log2(n+1) = level
where n is number of elements and level is tree depth.
For different number of splits just the base of the logarithm changes... However for usual real trees the splits are not regular and you need to take into account average number of splits per element instead ...
If your tree's level should be proportional to O(sqrt(n)) then the elements should form a square (level^2 = n) or rectangle so speculating graphically:
******
******
******
******
******
******
######
*#####
**####
***###
****##
*****#
#
*##
**###
***####
****#####
*****######
*
***
*****
*******
*********
***********
*
* * *
* * * * *
* * * * * * *
* * * * * * * * *
* * * * * * * * * * *
so most of elements does not split just first and last in each level splits into 2 except the first level which splits to 3. If I chose a rectangle instead of square then the wider rectangle the more splits on average would be there ...
*
* *
* * *
* * * * *
* * * * * * *
* * * * * * * * *
* * * * * * * * *
That's the average depth of a binary tree, not a balanced one, by the way, where the maximum depth is proportion to logN. Paraphrasing the section that's relevant:
A property of a binary tree that is sometimes important is that the depth of an average binary tree is considerably smaller than N.
An analysis shows that the average depth is O(โN), and that for a special type of binary tree, namely the binary search tree, the
average value of the depth is O(logN). Unfortunately, the depth can be as large as n-1.
To work that out, you can generate all the possible trees for a certain value of N, then just average those depths.
An ๐ ร ๐ chessboard is to be cut into its ๐ยท๐ unit squares. At each step, you can make either one horizontal cut or one vertical cut. The first cut will split the board into two sub-boards; after that each cut splits one remaining sub-board into two. The cost of a cut equals the number of unit squares remaining in the smaller of the two resulting sub-boards. For example, a horizontal cut on a 2 ร 3 board results in two 1 ร 3 sub-boards and costs 3, whereas a vertical cut results in sub-boards of dimensions 2 ร 1 and 2 ร 2 and costs 2. Costs are additive: the cost of a sequence of cuts equals the sum of their individual costs. Describe an algorithm to compute the minimum total cost of reducing the ๐ ร ๐ board into its unit squares. Prove its correctness and show your analysis of its time complexity.
My solution goes as follows:
1. I follow the greedy approach of checking for the highest between m (row) and n (column) and making a cut.
2. If m is higher I make a vertical cut and other a horizontal cut.
3. This gives me the lowest cut cost in every step.
4. I follow divide and conquer and recursively follow the approach until I have m x n = 1 x 1
This seems to be working but what I am struggling with is to derive the time complexity and proving the correctness of my algorithm.
My expression of time complexity is T(mn) = 2 T(mn/2) + theta(n).
Can someone advice me how I can do this?
The minimum cost is (n-1)*m+(m-1)*n. You can obtain by making m horizontal cuts paying for every one n-1 and n vertical cuts paying for every one m-1. That is O(1) algorithm.
To cut off a one unit square you need to pay at least 2 for it in (n-1)*(m-1) cases, at least 1 in (n-1)+(m-1), and one unit square you can get for free. This bounds overall price from below:
2*(n-1)*(m-1)+1*(n-1)+(m-1)+0*1 = 2*n*m-n-m = (n-1)*m+(m-1)*n
Here is a dynamic programming approach which works in O (m * n * (m + n)).
For each possible size w * h of a sub-rectangle, compute the cost f (w, h) of cutting it into individual squares.
Choose the first cut: w * h is cut either into a * h and b * h, or into w * c and w * d.
The cost of cutting into a * h and b * h is min (a, b) * h + f (a, h) + f (b, h).
The cost of cutting into w * c and w * d is w * min (c, d) + f (w, c) + f (w, d).
Putting all that together, we have
f (w, h) = min of
min {for every a such that 0 < a < w}
(let b = w - a)
min (a, b) * h + f (a, h) + f (b, h)
and
min {for every c such that 0 < c < h}
(let d = h - c)
w * min (c, d) + f (w, c) + f (w, d)
The base is f (1, 1) = 0.
We can store all f (w, h) as a two-dimensional array and calculate them bottom-up in two loops, like this:
for w = 1, 2, ..., m:
for h = 1, 2, ..., n:
calculate f[w][h] by the formula above in O (w + h)
Or write a recursive function with memoization.
Any of the two approaches will work in O (m * n * (m + n)): we have to calculate m * n values, and each one is calculated as a minimum of O (m + n) values.
If the greedy approach actually works (I don't know), it would do so in O (log m + log n).
By intuitive argument, for example, if m = 17 and n = 23, then the following rectangles must be considered:
17 * 23
17 * 11 and 17 * 12
8 * 11 and 8 * 12 and 9 * 11 and 9 * 12
8 * 5 and 8 * 6 and 9 * 5 and 9 * 6
4 * 5 and 4 * 6 and 5 * 5 and 5 * 6
4 * 2 and 4 * 3 and 5 * 2 and 5 * 3
2 * 2 and 2 * 3 and 3 * 2 and 3 * 3
1 * 1 and 1 * 2 and 2 * 1 and 2 * 2
1 * 1 again
As we can see, the rectangles will be of the form (m / 2^x) * (n / 2^y), where x and y are between 0 and log_2 (m + n), and the rounding can go either way.
I am having trouble analyzing the following.
for b = 1 to n
for u = b to n
for i = b to u
Print
I am having trouble determining the worst-case runtime.
The first loop runs n times, the second loop runs (n(n+1))/2 times.
I then beleive that the third loop runs (n+1)/2 times.
However I have been told that it has a runtime of O(n^3).
Therefore it's best-case runtime can't be greater then the worst-case runtime!
I'm hoping for a push in the right direction if possible!
Thanks!
The third loop executes Print u-b+1 times {3} when it is activated.
When the second loop is activated, the third loop is activated for all u from b to n, i.e. executes Print 1+2+3+...n-b+1 times (substitute u=1, 2, 3...n in {3}). Using the formula for triangular numbers, this count equals (n-b+1)(n-b+2)/2 {2}.
When the first loop is activated, the second loop is activated for all b from 1 to n, i.e. executes Print n(n+1)/2 + (n-1)n/2 + (n-2)(n-1)/2 + ... 1 times (substitute b= 1, 2, 3... n in {2}; the counts are decreasing).
Using the formula for tetrahedral numbers, this equals n(n+1)(n+2)/6 {1}.
If you have trouble with this approach, you can simulate the Prints by hand instead.
For n=1
*
For n=2
*
* *
*
For n=3
*
* *
* * *
*
* *
*
For n=4
*
* *
* * *
* * * *
*
* *
* * *
*
* *
*
...
You clearly see the pattern of triangles of decreasing size. The area of the triangles grows as the square of the side. The total volume grows as the cube of the side.