How to count efficiently the number of right isosceles triangles in a nxm rectangular grid?
The 3 corners of the triangles must have integer coordinates.
The right sides of the triangles are not necessarily parallel to grid lines.
Let A be the triangle's corner where the right angle is located. Let ab be the difference vector to the first corner and ac the difference vector to the other corner. ac should always be left of ab. So for a given ab, we can calculate ac as:
ac = (-ab.y, ab.x)
So there are only two integer degrees of freedom for a certain triangle, given A. We can calculate the possible intervals for ab as:
A.x + ab.x >= 0 --> ab.x >= -A.x
A.x + ab.x <= m --> ab.x <= m - A.x
A.y + ab.x >= 0 --> ab.x >= -A.y
A.y + ab.x <= n --> ab.x <= n - A.y
A.y + ab.y >= 0 --> ab.y >= -A.y
A.y + ab.y <= n --> ab.y <= n - A.y
A.x - ab.y >= 0 --> ab.y <= A.x
A.x - ab.y <= m --> ab.y >= A.x - m
So the possible intervals are
xmin = max(-A.x, -A.y)
xmax = min(m - A.x, n - A.y)
ymin = max(-A.y, A.x - m)
ymax = min(n - A.y, A.x)
The number of possible vectors that fulfil these conditions (except the 0-vector) are:
c = (xmax - xmin + 1) * (ymax - ymin + 1) - 1
The desired result is the sum of c for all possible A within the grid. With the naive approach you get an O(grid cells) runtime. Depending on your grid, this could already be fast enough.
Update
In order to split the sum of c (over all grid cells) we must determine where the results of the max and min operations change. E.g. ymax changes as follows:
Note that I have offset the line a bit to the right, so there is a unique mapping from points to the regions the points belong to.
When we do this for all interval boundaries, we get the following picture (for m > n):
Now we can calculate the sum of c for each region independently. I'll show that exemplarily for the left most triangle region.
For that region the following conditions hold:
xmin = -A.y
xmax = n - A.y
ymin = -A.y
ymax = A.x
c(x, y) = (n - y + x) * (x + y) - 1
= (nx + ny - y^2 + x^2 - 1)
Now we sum that for all xs and ys in that region. x runs from 0 through n/2. y runs from x to n-x.
So we get:
n/2 n-x
sumLeftRegion = Sum Sum (nx + ny - y² + x² - 1)
x=0 y=x
We can solve this (e.g. using your favorite algebra program and get:
sumLeftRegion = 1/48 * (2 + n)*(-24 - 14n + 6n^2 + 5n^3)
This can be done for all other regions. You would need some few cases (i.e. m > n, m = n, m < n, probably even and odd n and m). The case n > m can be converted to m > n by rotating the grid (which does not change the number of possible triangles). It'll be some work, but in the end you get the result in O(1) because there will be no iterations.
Consider RAIT ABC. If we maintain a corner (A) of the triangle at (0,0), then we can say that for a every positive point (B) along y=1 (so (1,1),(2,1),etc a RAIT exists whose other corner (C) is at (n,n), where Cn = Bx-1. It should be obvious that must also be true for y=2 (starting from B(2,2), y=3 (starting from B(3,3), etc. - you're just doubling, tripling, etc. the cooordinates. Does that help? (I'm no mathematician - in case that's not obvious)
Consider any triangle in the box. It has a unique bounding box (in the sense that it only has one, not that it's the only triangle with that bounding box). So, if you consider every possible bounding box side lengths, (i,j), and count the number of RAIT triangles with this bounding box, (denote this CP(i,j)) then your answer is sum(i=1 to n, sum(j=1 to m,CP(i,j)*(m-j+1))*(n-i+1)). The (m-j+1)*(n-i+1) is counting how many of that size box can fit in the n*m box. So the hard part now is to find CP(i,j). Again, CP(i,j) is the number of RAIT triangles with points on the perimeter of the box with lengths i and j. We can use symmetry to our advantage here. Assume with no loss of generality that the right angle is ABC. Imagine the triangle being aligned with the positive axes.
A
|\
| \
B__C
Now consider rotating it clockwise until it lines back up with itself. There are two possibilities through that process: BC is in quadrants 1 and 2 or in quadrants 3 and 4.
2|1
---
3|4
What we notice here is that in the first case B lies along the bottom or right of the bounding box, and in the second case B lies along the top or left of the bounding box. So wlog we assume that B lies along the bottom or right, excluding the very bottom left corner.
+-----*
| *
| *
+******
Once we calculate how many RAIT triangles have this property we can just double it!
If B is at the top right corner, then there is just one triangle to test for RAIT-ness, same if it's in the bottom right corner. If it's along the right wall, then there are i triangles to check (placing C at each point along the top, and calculating the third point to see if it forms the given bounding box). Similarly, if it's along the bottom wall, then there are j triangles to check.
This is definitely worse than O(m*n), it's probably somewhere around O(m^2*n^2), but hopefully this helps somehow.
EDIT
We can actually do a little better in our testing triangles. With a little geometric intuition, we can see that if B is along the bottom, and closer to the bottom right corner, then C is in the top right corner. Similarly if it's closer to the bottom left corner, then A is in the top right corner. So we can just consider half of B's positions, and for each position we only need to consider one possible triangle. (The argument when B is on the right is similar).
Related
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]
I'd like to take a grid of 0's and 1's and arrive at all rectangles present in the grid, which share a common corner.
Similar to the Maximal Rectangle problem -
Link 1
Link 2
But instead of finding a single rectangle with the maximum area, I'd like to find all rectangles that share a common corner. IE if I specify coordinates (10,10) I'd like all possible rectangles that have a bottom left corner of 10,10.
Could someone help explain how I can adapt the implementation of the algorithm I linked to do what I described?
I am not sure about how to adapt the given algorithm for this problem, but I have a good way to solve this problem.
Say, we are given the bottom-left corner for the rectangles at (x1, y1). We keep a count of the number of rectangles(with value 1 and sharing the bottom-left corner) num and a horizontal value till which rectangles can be found at this value of y hval. Now, we traverse horizontally until hval or if we find a 0 in the way, since no rectangles can be made beyond this point. Now, we make this x the new hval. We move to the next row and do the same each time counting the number of rectangles we have passed.
The solution is O(n2). Since we used atmost 2*num steps the complexity of solution is also bounded by θ(num).
This python code sample can provide a better understanding:
num = 0
hval = len(board)
i = 1
x1=1
y1=1
x2 = x1
while (y1 < len(board) and i > 0):
i = 0
while (board[y1][x2] == 1 and x2 < hval):
num += 1
x2 += 1
i += 1
else:
hval = x2
y1 += 1
x2 = x1
print(num)
You don't want or need my linked answer for this. An easy way to do this is to first calculate the "integral image" I[][] of the input matrix M[][] -- that is, a matrix I[][] whose (x, y) entry is the sum of all entries below and to the left of (x, y) in the input matrix. This lets you easily compute the number of 1s in any given rectangle (x1, y1, x2, y2) in constant time using I[x2][y2] - I[x1-1][y2] - I[x2][y1-1] + I[x1-1][y1-1], if (x1, y1) is the bottom-left corner and (x2, y2) is the top-right corner. I[][] can be computed in a one-time O(n^2) preprocessing step.
For a given bottom-left corner (x1, y1), you can then simply loop through all widths w and heights h in increasing order using a pair of nested loops, testing whether the number of 1s in the rectangle (x1, y1, x1+w, y2+h) is the number of 1s that should be there in an all-1 rectangle -- i.e., (b+1)*(h+1). As soon as this fails to hold, exit that loop early (since clearly if some rectangle is not an all-1 rectangle, then no rectangle that contains it can be either).
This algorithm is of course O(n^2)-time for an n*n rectangle, but there is no avoiding this worst case (consider an input matrix in which every entry is 1).
I'm well aware of the marching / DDA algorithms, but instead I would like to be able to do a per voxel-ray pair check, in constant time, without having to "march" through the voxel space. How would I go about this?
To be clear, I am not trying to find the first voxel a ray intersects, but rather, given a ray and a voxel, determine if that voxel's cell even lies within the ray's path.
A ray is P = O + t D, where P, O, D are vectors and t a positive real. There is an intersection with the voxel [x,y,z]x[x+1,y+1,z+1] if the system below has a solution:
x < Ox + t Dx < x + 1
y < Oy + t Dy < y + 1
z < Oz + t Dz < z + 1
0 < t
which we rewrite for brevity (with x' = (x - Ox) / Dx ...)
x' < t < x"
y' < t < y"
z' < t < z"
0 < t
If Dx < 0, the inequalities must be reversed. If Dx == 0, the inequation degenerates in x < Ox < x + 1, which can be directly decided.
Repeat the same discussion for all three axis and check if all bracketings are compatible.
For instance, for Dx, Dy, Dz > 0, you must have
max(0, x', y', z') < min(x", y", z").
There are 27 sign combinations to be considered, which are separated in three cascaded three-way comparisons.
As a micro-optimization, you can rescale the last inequality by Dx Dy Dz and simplify, to trade divisions for (faster) multiplications.
If many rays miss the voxel, you can speed-up the process a little by using the bounding sphere. Assuming that the voxel center is C, voxel radius R and the vector D is normalized, the pre-test is
(OC x D)² < R²
You can use any Ray Box (AABB) Intersection algorithm.
Arbitrary one
If you need intersection coordinates, then choose 3D line clipping algorithm
Input: S = {p1, . . . , pn}, n points on 2D plane each point is given by its x and y-coordinate.
For simplicity, we assume:
The origin (0, 0) is NOT in S.
Any line L passing through (0, 0) contains at most one point in S.
No three points in S lie on the same line.
If we pick any three points from S, we can form a triangle. So the total number of triangles that can be formed this way is Θ(n^3).
Some of these triangles contain (0, 0), some do not.
Problem: Calculate the number of triangles that contain (0, 0).
You may assume we have an O(1) time function Test(pi, pj , pk) that, given three points pi, pj , pk in S, returns 1, if the triangle formed by {pi, pj , pk} contains (0, 0), and returns 0 otherwise. It’s trivial to solve the problem in Θ(n^3) time (just enumerate and test all triangles).
Describe an algorithm for solving this problem with O(n log n) run time.
My analysis of the above problem leads to the following conclusion
There are 4 coordinates ( + ,+ ) , ( + ,- ) , ( -, - ), ( -, + ) { x and y coordinate > 0 or not }.
Let
s1 = coordinate x < 0 and y > 0
s2 = x > 0 , y > 0
s3 = x < 0 , y < 0
s4 = x > 0 , y < 0
Now we need to do the testing of points in between sets of the following combinations only
S1 S2 S3
S1 S1 S4
S2 S2 S3
S3 S3 S2
S1 S4 S4
S1 S3 S4
S1 S2 S4
S2 S3 S4
I now need to test the points in the above combination of sets only ( e.g. one point from s1 , one point from s2 and one point from s3 < first combinaton > ) and see the points contain (0,0) by calling Test function ( which is assumed as constant time function here) .
Can someone guide me on this ?
Image added below for clarification on why only some subsets (s1,s2 , s4 ) can contain (0,0) and some ( s1,s1,s3) cannot.
I'm guessing we're in the same class (based on the strange wording of the question), so now that the due date is past, I feel alright giving out my solution. I managed to find the n log n algorithm, which, as the question stated, is more a matter of cleverly transforming the problem, and less of a Dynamic Programming / DaC solution.
Note: This is not an exhaustive proof, I leave that to you.
First, some visual observations. Take some triangle that obviously contains the origin.
Then, convert the points to vectors.
Convince yourself that any selection of three points, one from each vector, describes a triangle that also contains the origin.
It also follows that, if you perform the above steps on a triangle that doesn't enclose the origin, any combination of points along those vectors will also not contain the origin.
The main point to get from this is, the magnitude of the vector does not matter, only the direction. Additionally, a hint to the question says that "any line crossing (0,0) only contains one point in S", from which we can extrapolate that the direction of each vector is unique.
So, if only the angle matters, it would follow that there is some logic that determines what range of points, given two points, could possibly form a triangle that encloses the origin. For simplicity, we'll assume we've taken all the points in S and converted them to vectors, then normalized them, effectively making all points lie on the unit circle.
So, take two points along this circle.
Then, draw a line from each point through the origin and to the opposite side of the circle.
It follows that, given the two points, any point that lies along the red arc can form a triangle.
So our algorithm should do the following:
Take each point in S. Make a secondary array A, and for each point, add the angle along the unit circle (atan2(x,y)) to A (0 ≤ Ai ≤ 2π). Let's assume this is O(n)
Sort A by increasing. O(n log n), assuming we use Merge Sort.
Count the number of triangles possible for each pair (Ai,Aj). This means that we count the number of Ai + π ≤ Ak ≤ Aj + π. Since the array is sorted, we can use a Binary Search to find the indices of Ai + π and Aj + π, which is O(2 log n) = O(log n)
However, we run into a problem, there are n^2 points, and if we have to do an O(log n) search for each, we have O(n^2 log n). So, we need to make one more observation.
Given some Ai < Aj, we'll say Tij describes the number of triangles possible, as calculated by the above method. Then, given a third Ak > Aj, we know that Tij ≤ Tik, as the number of points between Ai + π and Ak + π must be at least as many as there are betwen Ai + π and Aj + π. In fact, it is exactly the count between Ai + π and Aj + π, plus the count between Aj + π and Ak + π. Since we already know the count between Ai + π and Aj + π, we don't need to recalculate it - we only need to calculate the number between Aj + π and Ak + π, then add the previous count. It follows that:
A(n) = count(A(n),A(n-1)) + count(A(n-1),A(n-2)) + ... + count(A(1),A(0))
And this means we don't need to check all n^2 pairs, we only need to check consecutive pairs - so, only n-1.
So, all the above can give us the following psuedocode solution.
int triangleCount(point P[],int n)
int A[n], C[n], totalCount = 0;
for(i=0...n)
A[i] = atan2(P[i].x,P[i].y);
mergeSort(A);
int midPoint = binarySearch(A,π);
for(i=0...midPoint-1)
int left = A[i] + π, right = A[i+1] + π;
C[i] = binarySearch(a,right) - binarySearch(a,left);
for(j=0...i)
totalCount += C[j]
return totalCount;
It seems that in the worst case there are Θ(n3) triangles containing the origin, and since you need them all, the answer is no, there is no better algorithm.
For a worst case consider a regular polygon of an odd degree n, centered at the origin.
Here is an outline of the calculations. A chord connecting two vertices which are k < n/2 vertices apart is a base for Θ(k) triangles. Fix a vertex; its contribution is a sum over all chords coming from it, yielding Θ(n2), and a total (a contribution of all n vertices) is Θ(n3) (each triangle is counted 3 times, which doesn't affect the asymptotic).
I have encountered the following interesting problem while preparing for a
contest.
You have a triangle with sides of length a, b, c and a rope of length L. You need to find
the surfaced enclosed by the rope that has the maximum surface area and it has to be entirely inside the triangle.
So, if L = a + b + c, then it's the area of the triangle.
Else, we know that the circle has the biggest surface to perimeter area, so if L is smaller or equal to the perimeter of the inscribed circle of the triangle, then the area will be the area of the circle of perimeter L.
So, the remaining case is alfa < L < a + b + c, where alfa is the perimeter of the inscribed circle .
Any ideas would be great!
EDIT: I would like to know if I should focus on some kind of algorithm for solving this
or trying to figure it out a mathematical formula. The contest contains somehow a combination of both. The edges can be as long as 100 and the precision of a,b,c,L is of 4 digits after the decimal point .
After reading the answers to this question: https://math.stackexchange.com/questions/4808/why-circle-encloses-largest-area, I agree with n.m., and think the optimal curve verifies:
Curvature is either constant, or flat when it touches the triangle, meaning it is composed of segments lying on the triangle sides, and circle arcs, all sharing the same radius.
There are no angles, meaning the arcs are tangent to the triangle sides.
With these conditions, the solution is obtained by three circles of same radius R, each tangent to two sides of the triangle (see below). When R varies between 0 and the radius of the inscribed circle, we start from the triangle itself, and end to the inscribed circle, where all three circles coincide. The length of the curve is the perimeter of the circle of radius R + the perimeter (p) of the smaller triangle: L = 2*PiR + p. The area is the area (a) of the smaller triangle + one disc of radius R + the remaining rectangles: A = PiR^2 + p*R + a.
Since a circle has the largest Area/Perimeter, start with the inscribed circle. If L is less than that circumference, then shrink appropriately. If L is longer, grow whichever of the 3 arcs maximizes dA/dL. I don't know if there's a closed form, but the largest arc will be in the 3rd of the triangle with the sides most approaching parallel.
It should be trivial to solve this algorithmically. With 4 decimals of precision, increment by 0.0001 checking each arc to see which has the greatest dA/dL for that single increment.
I worked up a drawing of the geometry overnight:
The inscribed circle is constructed by bisecting each of the angles and finding the intersections of the bisectors. I've labeled the half-angle "a1" (and all related variables have '1'). The area of the non-circular portion is two trapezoids (one denoted with the red outline). We can calculate the area for a single trapezoid as L1 * (m1 + R)/2 (note that when L1, L2, L3 are all zero, these trapezoids are all zero, and we just get the inscribed circle area). The circular cap has a radius of m1 to remain tangent with the side of the triangle. For a given choice of L1, m1 = R(x1-L1)/x1.
From here you can easily calculate the perimeter and area of each of the three sectors and solve numerically.
I cannot prove that this is the largest area, just that this is how to calculate the area and perimeter of this construction.
..answering my own comment/question, it can be proved that the radii must be equal,
Here is a useful formula:
the gray area A is
A = r^2 ( alpha - Pi + 2/tan(alpha/2) ) /2
but even more useful..the arc length is simply:
s = 2 ( b - A/r )
from here it is straightforward to show the three radii must be equal to each other:
writing the rope length and enclosed area:
ropelength = trianglelength - 2 Sum[r[i] a[i] ]
ropearea = trianglearea - Sum[r[i]^2 a[i] /2 ]
where
a[i]=( alpha[i] - Pi + 2/tan(alpha[i]/2) )
after a bit of manipulation maximizing the area leads to all r[i] equal. Note the three a[i], ropelength,trainglearea,trianglelength are all constants that you do not need to work out. Pedantically solve for r[l] = f( constants, r[2],r[3] ) sub into the second expression and solve for d ropearea /d r[2] = 0 and d /d r[3] = 0 with the result:
r =(1/2) (triangle_length - rope_length) /(Sum(1/tan(alpha[i]/2)) - Pi)
(the messy expression for a[i] is substituted only at the last step ) finally..
ropearea = trianglearea - (trianglelength-ropelength)^2/(8 Sum[a[i])
= trianglearea - (1/2)(trianglelength-ropelength) r
edit -- a useful identity ..with a,b,c, the lengths of the sides.
Sum(1/tan(alpha[i]/2)) = Sqrt( S^3 / ((S-a)(S-b)(S-c)) )
S = 1/2 (a+b+c) ! S is semiperimeter not to be confused with arc length s
the above expressions then can be used to reproduce the formula for an inscribed circle,
rinscribed = Sqrt( ((S-a)(S-b)(S-c)) / S )
If the perimeter of the rope is too small or too large, the answers are trivial. The interesting case is a shape with 6 vertices that goes line-arc-line-arc-line-arc. The arc are all tangent to their neighbouring lines and their radii are equal. I don't have a rigorous proof, but imagine a 2D balloon filled with air and squeezed between the sides of the triangle.
It is easy to express the overall shape and thus the perimeter given the radius; the opposite direction (perimeter to radius) is then easily found numerically.