Tied points to-in the grid
Given a set of random points in the grid, how do you check efficiently that they all lie within a fixed range of other points. ie: pick any random point that you can then navigate to any other point on the grid.
To clarify further: if you have a 1000 x 1000 grid and 100 points are randomly placed, how can you prove that any point is within 100 units of a neighbor and all points are accessible by going from one point to another?
I wrote the code and came up with an interesting problem: very rarely (only once) it creates an island of points that exceeds the maximum range from the rest of the points. I need to fix this problem, but brute force is not the answer.
It's written in Java, but I'm good with either pseudocode or C ++.
source to share
I like @ joel.neely's constructional approach , but if you want a more uniform density, this will most likely work (although it will probably result in a larger cluster, not an overall uniform density):
- Randomly place the starting point P_0, picking x, y from a uniform distribution inside the valid grid
- For i = 1: N-1
- Pick a random j = evenly spaced from 0 to i-1, define a point
P_j
that was previously placed - Choose a random point
P_i
where distance (P_i
,P_j
) <100, repeating the following until a valid value is selected in sub-item 4P_i
:- Select (dx, dy) each evenly spaced from -100 to +100
- If dx ^ 2 + dy ^ 2> 100 ^ 2 the distance is too great (fails 21.5% of the time), go back to the previous step.
- Calculate the candidates' corans (
P_i
) = coords (P_j
) + (dx, dy). -
P_i
is valid if it is within the overall valid grid.
- Pick a random j = evenly spaced from 0 to i-1, define a point
source to share
Just think: if you divide the grid into 50x50 patches and when you place the starting points, you also write down which patch they belong to. Now, when you want to check if a new point is within 100 pixels of the rest, you can simply check the patch plus the 8 surrounding it and see if the points match.
For example, you know that you have 100 random points and each patch contains the number of points they contain, you can just sum it up and see if it is really 100 - which means all points are reachable.
I'm sure there are other tough ways.
EDIT: The distance from the top left point to the right side of the 50x50 patch is sqrt (50 ^ 2 + 50 ^ 2) = 70 points, so you probably have to choose a smaller patch size. Maybe 35 or 36 will do (50 ^ 2 = sqrt (x ^ 2 + x ^ 2) => x = 35.355 ...).
source to share
Find the convex body for the set of points and then use the rotating calipers . The two most distant points on the convex hull are the two most distant points in the set. Since all other points are contained in the convex hull, they are guaranteed to be closer than two extreme points.
source to share
As far as evaluating existing sets of points is concerned, it looks like a type of Euclidean minimum spanning tree problem . The wikipedia page states that this is a subgraph of the Delaunay triangulation ; so I think it would be sufficient to compute the Delaunay triangulation (see the prior link or "computational geometry") and then the minimum spanning tree and make sure all edges are less than 100 in length.
From reading the links, you can see that this is O (N log N), maybe there is a faster way, but that's enough.
A simpler (but probably less efficient) algorithm would be something like this:
- Given: points are in the array from index 0 to N-1.
- Sort points in x-coordinate order, which is O (N log N) for efficient sorting.
- Initialize i = 0.
- Increment i. If i == N, stop with success. (All points can be reached from another with a radius R)
- Initialize j = i.
- Decrease j.
- If
j<0
orP[i].x - P[j].x > R
, Stop with error. (there is a gap and all points cannot be reached from each other with radius R) - Otherwise we get here if P [i] .x and P [j] .x are inside R of each other. Check if point P [j] is close enough to P [i]: if
(P[i].x-P[j].x)^2 + (P[i].y-P[j].y)^2 <
R ^ 2`, then point P [i] is reachable by one of the previous points inside radius R and returns to step 4. - Keep trying: go back to step 6.
Edit: this can be changed to what should be O (N log N), but I'm not sure:
- Given: points are in the array from index 0 to N-1.
- Sort points in x-coordinate order, which is O (N log N) for efficient sorting.
- Store a sorted set of YLIST points in y-coordinate order, initializing YLIST to {P [0]}. We will sweep the x-coordinate from left to right, adding points one by one to the YLIST and removing points that have an x-coordinate that is too far from the newly added point.
- Initialize i = 0, j = 0.
- The Loop invariant is always true at this point: all points P [k] where k <= i form a network where they can be reached from each other with radius R. All points in the YLIST have x-coordinates that are between P [i ] .xR and P [i] .x
- Increment i. If i == N, stop with success .
- If P [i] .xP [j] .x <= R, go to step 10. (this is automatically true if i == j)
- Point P [j] is not accessible from point P [i] with radius R. Remove P [j] from YLIST (this is O (log N)).
- Increment j, go to step 6.
- At this moment, all points P [j] with
j<i
and x-coordinates between P [i] .xR and P [i] .x are in the set YLIST. - Add P [i] to YLIST (that's O (log N)) and store the index k in YLIST, where YLIST [k] == P [i].
- Points YLIST [k-1] and YLIST [k + 1] (if they exist, P [i] may be the only element within YLIST, or it may be at the extreme end) are the closest points in YLIST to P [I].
- If point YLIST [k-1] exists and is in radius R from P [i], then P [i] is reachable with radius R from at least one of the previous points. Go to step 5.
- If point YLIST [k + 1] exists and is in radius R from P [i], then P [i] is reachable with radius R from at least one of the previous points. Go to step 5.
- P [i] is not available from any of the previous points. Stop crashing.
source to share
New and improved ; -)
Thanks to Guillaume and Jason S for the comments that made me think a little more. This produced a second proposal, the statistics of which show a significant improvement.
Guillaume noted that the previously published strategy will lose its uniform density. Of course, he is right, because it is, in fact, a "drunken walk" that tends to orbit the starting point. However, evenly random placement of points gives a significant probability of abandoning the "path" requirement (all points are connected along a path without a step greater than 100). Testing for this condition is costly; generating purely random solutions until they pass, even more so.
Jason S suggested a variation, but statistical testing over a large number of simulations leads me to conclude that his variation creates patterns that are as grouped as those in my first sentence (based on examining the mean and standard number of coordinates of a value).
The revised algorithm below generates sets of points whose statistics are very similar to the characteristics of pure (uniform) random placement, but which are guaranteed by the design to satisfy the path requirement. Unfortunately, this is a little easier to visualize than to explain orally. Essentially, this requires the dots to randomly wobble in an indefinitely sequential direction (NE, SE, SW, NW), only changing directions when they "bounce off the wall."
Here's a high-level overview:
-
Select the starting point at random, set the horizontal movement to the RIGHT position and the vertical movement to the DOWN position.
-
Repeat for the remaining number of points (e.g. 99 in the original specification):
2.1. Randomly select dx and dy with 50-100 spacing. (I assumed Euclidean distance - the square root of the sums of squares - in my trial implementation, but the "taxicab" distance - the sum of absolute values ββ- would be even easier for the code.)
2.2. Apply dx and dy to the previous point based on horizontal and vertical movement (RIGHT / DOWN β add, LEFT / UP β subtract).
2.3. If any coordinate is out of bounds (less than 0 or at least 1000), flip that coordinate around the border and break its movement in the opposite direction. This means four cases (2 coordinates x 2 borders):
2.3.1. if x < 0, then x = -x and reverse LEFT/RIGHT horizontal travel. 2.3.2. if 1000 <= x, then x = 1999 - x and reverse LEFT/RIGHT horizontal travel. 2.3.3. if y < 0, then y = -y and reverse UP/DOWN vertical travel. 2.3.4. if 1000 <= y, then y = 1999 - y and reverse UP/DOWN vertical travel.
Note that the reflections in step 2.3 are guaranteed to leave the new point within 100 units of the previous point, so the path requirement is preserved. However, the horizontal and vertical travel constraints cause the points to be generated to "swing" randomly throughout the space, creating more complete variance than the original pure drunk walk algorithm.
source to share
If I understand your problem correctly, given a set of sites, you want to check if the nearest neighbor (for distance L1, i.e. grid distance) from each site is less than the K value.
This is easily obtained for Euclidean distance by computing the Delaunay triangulation of a set of points: the site's nearest neighbor is one of its neighbors in the Delaunay triangulation. Interestingly, the L1 distance is greater than the Euclidean distance (within the sqrt (2) factor).
It follows that the way to check your status is as follows:
- compute the Delaunay triangulation of sites
- for each site s, start breadth-first search with s in the triangulation, so that you find all vertices at a Euclidean distance less than K from s (Delaunay triangulation has the property that a set of vertices at a distance less than K from a given site is connected in triangulation)
- for each site s, among these vertices at a distance less than K from s, check if any of them is at a distance L1 less than K from s. If not, the property is not executed.
This algorithm can be improved in several ways:
- The breadth-first search in step 2 must of course be stopped as soon as a site is found at a distance L1 less than K.
- during the search for a valid neighbor s, if it is found that site s 'is at a distance L1 less than K from s, there is no need to look for a valid neighbor for s': s is obviously one of them.
- full breadth-first search is unnecessary: ββafter visiting all triangles incident to s, if none of the neighbors of s in the triangulation is a valid neighbor (i.e., a site at a distance L1 less than K), we denote by (v 1, ..., v n) neighbors. There are at most four edges (v i, v i + 1) that intersect the horizontal and vertical axes. The search should only continue through these four (or less) edges. [This follows from the shape of the sphere L1]
source to share
Configure the desired construction condition. Instead of placing all the points solely by drawing random numbers, constrain the coordinates as follows:
-
Place the origin at random.
-
Repeat for the remaining number of points (e.g. 99):
2.1. Randomly select an x-coordinate within some range (eg 90) of the previous point.
2.2. Calculate the legal range for the y-coordinate, which will make it within 100 units of the previous point.
2.3. Randomly select a y-coordinate in this range.
-
If you want to completely shade the origin, collect points along their coordinate pair.
This does not require much overhead and pure randomness, but it ensures that every point is within 100 units of at least one other point (in fact, except for the first and last point, each point will be within 100 units of the other two points).
Alternatively, in step 2, arbitrarily select any already generated point and use it as a reference instead of the previous point.
source to share