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]
Related
I am attempting to solve a coding challenge however my solution is not very performant, I'm looking for advice or suggestions on how I can improve my algorithm.
The puzzle is as follows:
You are given a grid of cells that represents an orchard, each cell can be either an empty spot (0) or a fruit tree (1). A farmer wishes to know how many empty spots there are within the orchard that are within k distance from all fruit trees.
Distance is counted using taxicab geometry, for example:
k = 1
[1, 0]
[0, 0]
the answer is 2 as only the bottom right spot is >k distance from all trees.
My solution goes something like this:
loop over grid and store all tree positions
BFS from the first tree position and store all empty spots until we reach a neighbour that is beyond k distance
BFS from the next tree position and store the intersection of empty spots
Repeat step 3 until we have iterated over all tree positions
Return the number of empty spots remaining after all intersections
I have found that for large grids with large values of k, my algorithm becomes very slow as I end up checking every spot in the grid multiple times. After doing some research, I found some solutions for similar problems that suggest taking the two most extreme target nodes and then only comparing distance to them:
https://www.codingninjas.com/codestudio/problem-details/count-nodes-within-k-distance_992849
https://www.geeksforgeeks.org/count-nodes-within-k-distance-from-all-nodes-in-a-set/
However this does not work for my challenge given certain inputs like below:
k = 4
[0, 0, 0, 1]
[0, 1, 0, 0]
[0, 0, 0, 0]
[1, 0, 0, 0]
[0, 0, 0, 0]
Using the extreme nodes approach, the bottom right empty spot is counted even though it is 5 distance away from the middle tree.
Could anyone point me towards a more efficient approach? I am still very new to these types of problems so I am finding it hard to see the next step I should take.
There is a simple, linear time solution to this problem because of the grid and distance structure. Given a fruit tree with coordinates (a, b), consider the 4 diagonal lines bounding the box of distance k around it. The diagonals going down and to the right have a constant value of x + y, while the diagonals going down and to the left have a constant value of x - y.
A point (x, y) is inside the box (and therefore, within distance k of (a, b)) if and only if:
a + b - k <= x + y <= a + b + k, and
a - b - k <= x - y <= a - b + k
So we can iterate over our fruit trees (a, b) to find four numbers:
first_max = max(a + b - k); first_min = min(a + b + k);
second_max = max(a - b - k); second_min = min(a - b + k);
where min and max are taken over all fruit trees. Then, iterate over empty cells (or do some math and subtract fruit tree counts, if your grid is enormous), counting how many empty spots (x,y) satisfy
first_max <= x + y <= first_min, and
second_max <= x - y <= second_min.
This Python code (written in a procedural style) illustrates this idea. Each diagonal of each bounding box cuts off exactly half of the plane, so this is equivalent to intersection of parallel half planes:
fruit_trees = [(a, b) for a in range(len(grid))
for b in range(len(grid[0]))
if grid[a][b] == 1]
northwest_half_plane = -infinity
southeast_half_plane = infinity
southwest_half_plane = -infinity
northeast_half_plane = infinity
for a, b in fruit_trees:
northwest_half_plane = max(northwest_half_plane, a - b - k)
southeast_half_plane = min(southeast_half_plane, a - b + k)
southwest_half_plane = max(southwest_half_plane, a + b - k)
northeast_half_plane = min(northeast_half_plane, a + b + k)
count = 0
for x in range(len(grid)):
for y in range(len(grid[0])):
if grid[x][y] == 0:
if (northwest_half_plane <= x - y <= southeast_half_plane
and southwest_half_plane <= x + y <= northeast_half_plane):
count += 1
print(count)
Some notes on the code: Technically the array coordinates are a quarter-turn rotated from the Cartesian coordinates of the picture, but that is immaterial here. The code is left deliberately bereft of certain 'optimizations' which may seem obvious, for two reasons: 1. The best optimization depends on the input format of fruit trees and the grid, and 2. The solution, while being simple in concept and simple to read, is not simple to get right while writing, and it's important that the code be 'obviously correct'. Things like 'exit early and return 0 if a lower bound exceeds an upper bound' can be added later if the performance is necessary.
As Answered by #kcsquared ,Providing an implementation in JAVA
public int solutionGrid(int K, int [][]A){
int m=A.length;
int n=A[0].length;
int k=K;
//to store the house coordinates
Set<String> houses=new HashSet<>();
//Find the house and store the coordinates
for(int i=0;i<m;i++) {
for (int j = 0; j < n; j++) {
if (A[i][j] == 1) {
houses.add(i + "&" + j);
}
}
}
int northwest_half_plane = Integer.MIN_VALUE;
int southeast_half_plane = Integer.MAX_VALUE;
int southwest_half_plane = Integer.MIN_VALUE;
int northeast_half_plane = Integer.MAX_VALUE;
for(String ele:houses){
String arr[]=ele.split("&");
int a=Integer.valueOf(arr[0]);
int b=Integer.valueOf(arr[1]);
northwest_half_plane = Math.max(northwest_half_plane, a - b - k);
southeast_half_plane = Math.min(southeast_half_plane, a - b + k);
southwest_half_plane = Math.max(southwest_half_plane, a + b - k);
northeast_half_plane = Math.min(northeast_half_plane, a + b + k);
}
int count = 0;
for(int x=0;x<m;x++) {
for (int y = 0; y < n; y++) {
if (A[x][y] == 0){
if ((northwest_half_plane <= x - y && x - y <= southeast_half_plane)
&& southwest_half_plane <= x + y && x + y <= northeast_half_plane){
count += 1;
}
}
}
}
return count;
}
This wouldn't be easy to implement but could be sublinear for many cases, and at most linear. Consider representing the perimeter of each tree as four corners (they mark a square rotated 45 degrees). For each tree compute it's perimeter intersection with the current intersection. The difficulty comes with managing the corners of the intersection, which could include more than one point because of the diagonal alignments. Run inside the final intersection to count how many empty spots are within it.
Since you are using taxicab distance, BFS is unneccesary. You can compute the distance between an empty spot and a tree directly.
This algorithm is based on a suggestion by https://stackoverflow.com/users/3080723/stef
// select tree near top left corner
SET flag false
LOOP r over rows
LOOP c over columns
IF tree at c, r
SET t to tree at c,r
SET flag true
BREAK
IF flag
BREAK
LOOP s over empty spots
Calculate distance between s and t
IF distance <= k
ADD s to spotlist
LOOP s over spotlist
LOOP t over trees, starting at bottom right corner
Calculate distance between s and t
IF distance > k
REMOVE s from spotlist
BREAK
RETURN spotlist
I have a single large rectangle of dimensions L*W, and n smaller rectangles that each have the same dimension l * w. Every small rectangle has the same dimensions.
My goal is to fit all n of smaller rectangles into the large rectangle while making the most efficient use of space in the large rectangle possible. l and w can be scaled up or down as needed, as long as the proportion is kept the same.
How can it be determined how the smaller rectangles should be scaled to fit them all in the large rectangle?
Here is an algorithm that finds the max value of a scaling factor F such that all small a x b rectangles, when scaling by F will fit in the containing rectangle A x B:
For every pair (p, q) of positive integers such that
p <= n
q <= n
n = p * q - r for some integer r >= 0 satisfying r < p or p < q
compute f = min(A/(a*p), B/(b*q)).
Let F be the maximum of all factors f computed in 1.
The computation of all pairs (p, q) may proceed as follows:
[Initialize] p := 0
[Increment] p := p + 1
[End?] If p > n, stop
[Next] Let q := n + p - 1 / p (integer division). Next pair (p, q).
[Repeat] Go to 2.
Idea of the algorithm
Every pair (p, q) represents a particular layout of the scaled rectangles with p rectangles in an horizontal row and q rows, the last one possibly incomplete. Here is an example for n = 13 written as 3 * 5 - 2:
Since p scaled rectangles of width f*a must fit in a rectangle of width A, we have: p*f*a <= A or f <= A/(p*a). Similarly f <= B/(q*b). Therefore the maximum scale for this configuration is min(A/(p*a), B/(q*b)).
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).
Given a set of points with unequal x-coordinates, I want to calculate the value v > 0 such that the shear transformation (x, y) -> (x + v*y, y) doesn't change the order in the x-direction.
This isn't difficult. Order the points by their x-axis. Because of the continuity of the shear transformation, it's enough for you to find a maximum v that two consecutive points (in x-order) do not change order. Let (x,y) and (x',y') be two consecutive points in your ordering. with v>0, the x coordinates change as x -> x + vy and x' -> x' + vy'. Now as x'>x, you want to find maximum v such that x' + vy' >= x + vy. By linearity, it's enough to solve
x' + vy' = x + vy
i.e.
x' - x = vy - vy' = v(y - y')
thus
v = (x' - x)/(y - y')
If the result is negative, then any value of v goes (the points are moving farther away); if the result is positive, that's the maximum value that the pair (x,y), (x',y') can tolerate. Now calculate this maximum for all consecutive pairs and take their minimum.
Note that if y = y', v becomes undefined. In this case the points lie at the same point on y-axis and the shear transformation doesn't change their distance.
Convert each point (x, y) into a ray {(x + yv, v) | v ≥ 0} in the xv-halfplane with v ≥ 0. Use a segment intersection algorithm to find the one with minimum v.
This problem is a subproblem of a problem posed in the ACM ICPC Kanpur Regionals Elimination Round:
Given 2 line segments bounded by the 2D points (Pa, Pb) and (Pc, Pd) respectively, find p and q (in the range [0,1]) that minimizes the function
f(p, q) = D(Px, Pa) + D(Py, Pd) + k D(Px, Py) where
2 <= k <= 5,
Px = p Pa + (1-p) Pb,
Py = q Pc + (1-q) Pd and
D(x, y) is the euclidean distance between points x and y
(effectively, Px and Py are points on the line segments and the function encodes the cost of going from Pa to Pd through a connecting link of a cost that is k times the euclidean distance)
Some observations regarding this function:
Parallel line segments will always cause atleast one of p and q to be either 0 or 1
Intersecting line segments will always cause p and q to locate the point of intersection of the line segments (the triangle inequality can be applied to prove this)
The question:
In the general case where the lines are inclined and potentially separated, how do we minimize this function?
I think you should be able to take the partial derivatives of f with respect to p and q, set them to 0, and solve for p and q. That will give you a (local) minimum. If the minimum has 0 <= p <= 1 and 0 <= q <= 1, you're done, otherwise check the four endpoints (p=0,q=1, and so on).
I'm not positive that this will handle all degenerate conditions, but it should be a good start.