Related
I have a large number of (x,y) grid points with integer coordinates which i want to test if they are in small number of circles given by radius and center. The points are some marked parts of an image, which means there are a small number of irregular shaped blocks, which contain the points. There i want to check for collisions and count the number of points inside a circle. My current approaches are rather slow (with python and numpy).
Now i have two tasks:
Test, if any point of set A are in any circle
Count the number of points of set B, which are in a circle
My current implementation looks like this (setA and setB are Nx2 numpy arrays and center is a 1x2 array.):
1) For each circle create an array of point - center, square it elementwise and take the sum, then check if it's smaller than radius**2
for circle in circles:
if (((setA - circle.center)**2).sum(axis=1) < circle.radius**2).any():
return "collision"
return "no collision"
This could be optimized by using a python loop and breaking on the first collision, but usually numpy loops are a lot faster than python loops and actually both versions were slower than expected.
2) For each circle create an array of distances and do an elementwise less than radius test. Add up all arrays and count the non-zero elements of the result.
pixels = sp.zeros(len(setB))
for circle in circles:
pixels += (((setB - circle.center)**2).sum(axis=1) < circle.radius**2)
return np.count_nonzero(pixels)
Is there an easy option to speedup this?
I do not want to over optimize (and make the program a lot more complicated), but just to use numpy in the most efficient way, using the numpy vectorization as much as possible.
So building the most perfect spatial tree or similiar isn't my goal, but i think a O(n^2) algorithm for a few thousand points and 10-20 circles should be possible in as fast way on an average desktop computer today.
Taking advantage of coordinates being integers:
create a lookup image
radius = max([circle.radius for circle in circles])
mask = np.zeros((image.shape[0] + 2*radius, image.shape[1] + 2*radius), dtype=int)
for circle in circles:
center = circle.center + radius
mask[center[0]-circle.radius:center[0]+circle.radius + 1,
center[1]-circle.radius:center[1]+circle.radius + 1] += circle.mask
circle.mask is a small square patch containing a mask of the disc of interior points
counting collisions is now as easy as
mask[radius:-radius, radius:-radius][setB[:,0], setB[:,1]].sum()
fast creation of discs (no multiplications, no square roots):
r = circle.radius
h2 = np.r_[0, np.add.accumulate(np.arange(1, 2*r+1, 2))]
w = np.searchsorted(h2[-1] - h2[::-1], h2)
q = np.zeros(((r+1), (r+1)), dtype=int)
q[np.arange(r+1), w[::-1]] = 1
q[1:, 0] -= 1
q = np.add.accumulate(q.ravel()).reshape(r+1, r+1)
h = np.c_[q, q[:, -2::-1]]
circle.mask = np.r_[h, h[-2::-1]]
I have a circle which i need to fill with rectangles.Piled one over the other.The rectangles are available in specific sizes only.And we are also given the number of rectangles we must put.I need to get the set of rectangle lengths which cover the most area of the circle.For eg if the circle has a diameter of 100,rectangles of lengths [100,95,90,85,...15,10,5] can be put.I have tried using a brute force method by parsing through all the possible combinations.It yields good results when the numbers are small.Another algorithm i tried is to restrict the range of lengths which each rectangle occupies.Like the first rectangle will have a length of 95 or 90 to give the best result.But even this method is cumbersome when the number of rectangles to be put is really high.Here is how the rectangles are arranged
If the first rectangle has a length l,and diameter of circle is d,its thickness is sqrt(d2-l2).The thickness of second one if its length is k is sqrt(d2-k2)-sqrt(d2-l2).
Is there any algorithm so that i can go formulate the results.
Why should a brute-force-attack on this problem be difficult? You just need to put some effort in your calculation code and I'm sure it will work fine. It just has 19 levels at maximum. This shouldn't be too complicated and will give you the result within ... well, some hours, as I just found out. 19 levels will result in 3.3e17 calculations.
About the algorithm:
With one rectangle, you get the largest covered area when the rectangle is a square. I think that's very easy to understand. The corner of the square is at 45° from the circle center (assuming that horizontal is 0°, but that actually doesn't matter as the whole structre is point symmetric), the size is (0.707*diameter)^2 = 5000.
The closest to width 70.7 is 70. In general I suggest checking the number below (70) and above (75) the accurate result (70.7). The area of your rectangle is 70 * 71.41 = 4999. (But it would be nice to know, if the height also has to be a value out of your 5's-grid!)
Now it's getting more difficult and I hope I am right:
As I write this answer, it turns out, I am not right. :-( The rounded values have a higher result than the theoretical maximum. But I will post it regardless, maybe it helps to find the real answer.
When you have 2 rectangles, the largest area to cover should be when
the corners of rect1 are at 30° (and 150°, 210°, 330°), and
the corners of rect2 are at 60° (and 120°, 240°, 300°).
The sizes would be:
rect 1: 0.866*dia * 0.5 *dia = 4330
rect 2: 0.5 *dia * 0.866*dia = 4330 - minus the overlap =>> 0.5*0.36*dia^2 = 1830
sum: 6160
rounded to the 5's grid:
rect 1, #1) 85*52.86 = 4478
rect 2: #1) 50*(86.60-52.86) = 1696.2 #2) 55*(83.52-52.86) = 1696.1 #3) 45*(89.30-52.86) = 1648
sums: 6173.87 // 6173.75 // 6126
rect 1, #2) 90*43.59 = 3923
rect 2: #1) 50*(86.60-43.59) = 2151 #2) 55*(83.52-43.59) = 2196 #3) 45*(89.30-43.59) = 2057
sums: 6074 // 6119 // 5980
The winner is combination 1.1: rect1 = 85, rect2 = 50.
Due to using rounded values, you have to check each combination of upper and lower (and exact if it is on the grid) value of each rectangle, resulting in a maximum of 3^n checks if n is the number of rectangles (except n=1). A brute force is not nicer, but maybe easier. (And as found out and written above, it maybe will return better results, as this method is inaccurate).
EDIT 1:
The formula for 1 rectangle (which results in a square) is:
A = x * sqrt(D²-x²)
calculate the maximum using the derivative of A:
A' = D²-2x² / sqrt(D²-x²) = 0
You can also find it here: http://oregonstate.edu/instruct/mth251/cq/Stage8/Lesson/rectangle.html
The formula for 2 rectangles is:
A = f(x,y) = x * sqrt(D²-x²) + y * [sqrt(D²-y²)-sqrt(D²-x²)]
( x = width of r1, y = width of r2 )
The formula for n rectangles depends on n unknown variables. So you need to calculate n partial derivatives. Have fun! (or consider brute force, as you already are given a grid and don't need to do iterations ;-) )
Brute force algorithm
amount of calculations:
levels calculations
1 19
2 19 + 19*18 = 361
...
5 19 + 19*18 + 19*18*17 + 19*18*17*16 + 19*18*17*16*15 = 1494559
...
10 3.7e11
15 6.3e15
19 3.3e19
C# (or C++):
double dDia = 100;
int nSizes = 20;
int nmax = 2; // number of rectangles
int main()
{
int n = 1;
double dArea = 0.0;
dArea = CalcMaxArea (n, 0);
}
double CalcMaxArea (int n, double dSizeYParent)
{
double dArea = 0.0;
double dAreaMax = 0.0;
for (int iRun = nSizes-n; iRun >= 1; iRun--)
{
double dSizeX = iRun * 5;
double dSizeY = Math.Sqrt(dDia * dDia - dSizeX * dSizeX) - dSizeYParent);
double dAreaThis = dSizeX * dSizeY;
double dAreaOthers = 0.0;
if (n < nmax)
dAreaOthers = CalcMaxArea (n+1, dSizeY);
if (dArea > dAreaMax)
dAreaMax = dArea;
}
}
VBA, to be used in MS Excel
Dim dDia As Double
Dim nmax As Integer
Dim nSizes As Integer
Sub main()
dDia = 100
nmax = 2
nSizes = 20
Dim n As Integer
Dim dArea As Double
n = 1
dArea = CalcMaxArea(n, 0)
End Sub
Function CalcMaxArea(n As Integer, dSizeYParent As Double) As Double
Dim dArea As Double
Dim dAreaMax As Double
dArea = 0
For iRun = nSizes - n To 1 Step -1
Dim dSizeX As Double
Dim dSizeY As Double
Dim dAreaThis As Double
Dim dAreaOthers As Double
dSizeX = iRun * 5
dSizeY = Sqr(dDia * dDia - dSizeX * dSizeX) - dSizeYParent
dAreaThis = dSizeX * dSizeY
dAreaOthers = 0
If n < nmax Then
dAreaOthers = CalcMaxArea(n + 1, dSizeY)
End If
dArea = dAreaThis + dAreaOthers
If dArea > dAreaMax Then
dAreaMax = dArea
End If
Next
CalcMaxArea = dAreaMax
End Function
Tested in VBA with the given values, got the same result: 6173.87.
Further code may be added to remember on which values the maximum as reached.
I read the question again, and realize I completely missed a couple of key points in your post. The picture confused me, but that isn't a good excuse. My previous suggestion in the comments was a completely bad idea. I'm sorry, and I hope you didn't send a lot of time looking into it. If I had to solve this problem, this is how I would do it, right or wrong.
So I have been thinking this problem over. The best solution I can think of is to use a search algorithm such as A*. A* its self it rather simple to implement. I'm assuming you already have a method to calculate the area, which to me seems the hardest part. I have an idea of how I would go on with calculating the area of overlapping rectangles, but its the reason why I didn't write a program that could prove that my suggestion is a good one.
What I would do is have a master list of all of the potential rectangles.
Add to your frontier a copy of all rectangles not in the current path as the nth rectangle placed. This will allow you to set the width, and therefore calculate the area of the circle left to be filled. Keeping doing this, selecting the lowest cost path from the frontier each time, and after m nodes are explored, you should have the best fit. Where m is the total number of rectangles you must place.
For the cost evaluation, using the amount of space left to fill seems a natural choice. One thing to make note of though, is that the area left decreases over time, and you will need one that increases. I would think dividing the area left by the number of rectangles left should give you a nice cost function for finding the lowest cost path to the least area left in the circle. That one sounded good to me, but i'm sure there are others that could be used.
In regards to the heuristic, without a heuristic function you still have a best first search, so I would expect it to perform better than a blind brute force technique. With a good heuristic function, I would expect the performance to increase significantly. In thinking about what would make a good heuristic function, I thought that estimating the amount of the circle the rectangle would fill might work well. For instance, 10% of the area of the rectangle divided by the number of rectangles left to be placed. Since there is no pre-determined goal state, any estimate would have to be base solely off the area of the next rectangle. We know the full area of the rectangle won't contribute to solution. The majority of every rectangle after the first is wasted space as far as the solution goes, which is how i came up with that heuristic. As with the cost function, it seems like a reasonable idea to me, but if anyone can think of a better one, all the better.
There are all sorts of sites on A* out there, but here is one that looks well written. http://web.mit.edu/eranki/www/tutorials/search/
I hope this helps you out.
I know devising a working algorithm is complex, but this is the approach I thought of :
There could be only one rectangle which can occupy the maximum area in the circle with given diameter.
Find out the the Max width and height of rectangle that can be made to fit into the circle. There are a lot of solutions for the same. For example look: Find Largest Inscribed Rectangle This rectangle will then conclude a major portion of the max area.
The next task is then to fill the remaining portion of the circle with the rectangle of different sizes. Find out the best fit rectangle, as in the below image. This can be done by checking if the circle points lie inside the rectangle for a specified height and width
I again agree that this is very difficult to implement.
Consider points Y given in increasing order from [0,T). We are to consider these points as lying on a circle of circumference T. Now consider points X also from [0,T) and also lying on a circle of circumference T.
We say the distance between X and Y is the sum of the absolute distance between the each point in X and its closest point in Y recalling that both are considered to be lying in a circle. Write this distance as Delta(X, Y).
I am trying to find a quick way of determining a rotation of X which makes this distance as small as possible.
My code for making some data to test with is
#!/usr/bin/python
import random
import numpy as np
from bisect import bisect_left
def simul(rate, T):
time = np.random.exponential(rate)
times = [0]
newtime = times[-1]+time
while (newtime < T):
times.append(newtime)
newtime = newtime+np.random.exponential(rate)
return times[1:]
For each point I use this function to find its closest neighbor.
def takeClosest(myList, myNumber, T):
"""
Assumes myList is sorted. Returns closest value to myNumber in a circle of circumference T.
If two numbers are equally close, return the smallest number.
"""
pos = bisect_left(myList, myNumber)
before = myList[pos - 1]
after = myList[pos%len(myList)]
if after - myNumber < myNumber - before:
return after
else:
return before
So the distance between two circles is:
def circle_dist(timesY, timesX):
dist = 0
for t in timesX:
closest_number = takeClosest(timesY, t, T)
dist += np.abs(closest_number - t)
return dist
So to make some data we just do
#First make some data
T = 5000
timesX = simul(1, T)
timesY = simul(10, T)
Finally to rotate circle timesX by offset we can
timesX = [(t + offset)%T for t in timesX]
In practice my timesX and timesY will have about 20,000 points each.
Given timesX and timesY, how can I quickly find (approximately) which rotation of timesX gives
the smallest distance to timesY?
Distance along the circle between a single point and a set of points is a piecewise linear function of rotation. The critical points of this function are the points of the set itself (zero distance) and points midway between neighbouring points of the set (local maximums of distance). Linear coefficients of such function are ±1.
Sum of such functions is again piecewise linear, but now with a quadratic number of critical points. Actually all these functions are the same, except shifted along the argument axis. Linear coefficients of the sum are integers.
To find its minimum one would have to calculate its value in all critical points.
I don'see a way to significantly reduce the amount of work needed, but 1,600,000,000 points is not such a big deal anyway, especially if you can spread the work between several processors.
To calculate sum of two such functions, represent the summands as sequences of critical points and associated coefficients to the left and to the right of each critical point. Then just merge the two point sequences while adding the coefficients.
You can solve your (original) problem with a sweep line algorithm. The trick is to use the right "discretization". Imagine cutting your circle up into two strips:
X: x....x....x..........x................x.........x...x
Y: .....x..........x.....x..x.x...........x.............
Now calculate the score = 5+0++1+1+5+9+6.
The key observation is that if we rotate X very slightly (right say), some of the points will improve and some will get worse. We can call this the "differential". In the above example the differential would be 1 - 1 - 1 + 1 + 1 - 1 + 1 because the first point is matched to something on its right, the second point is matched to something under it or to its left etc.
Of course, as we move X more, the differential will change. However only as many times as the matchings change, which is never more than |X||Y| but probably much less.
The proposed algorithm is thus to calculate the initial score and the time (X position) of the next change in differential. Go to that next position and calculate the score again. Continue until you reach your starting position.
This is probably a good example for the iterative closest point (ICP) algorithm:
It repeatedly matches each point with its closest neighbor and moves all points such that the mean squared distance is minimized. (Note that this corresponds to minimizing the sum of squared distances.)
import pylab as pl
T = 10.0
X = pl.array([3, 5.5, 6])
Y = pl.array([1, 1.5, 2, 4])
pl.clf()
pl.subplot(1, 2, 1, polar=True)
pl.plot(X / T * 2 * pl.pi, pl.ones(X.shape), 'r.', ms=10, mew=3)
pl.plot(Y / T * 2 * pl.pi, pl.ones(Y.shape), 'b+', ms=10, mew=3)
circDist = lambda X, Y: (Y - X + T / 2) % T - T / 2
while True:
D = circDist(pl.reshape(X, (-1, 1)), pl.reshape(Y, (1, -1)))
closestY = pl.argmin(D**2, axis = 1)
distance = circDist(X, Y[closestY])
shift = pl.mean(distance)
if pl.absolute(shift) < 1e-3:
break
X = (X + shift) % T
pl.subplot(1, 2, 2, polar=True)
pl.plot(X / T * 2 * pl.pi, pl.ones(X.shape), 'r.', ms=10, mew=3)
pl.plot(Y / T * 2 * pl.pi, pl.ones(Y.shape), 'b+', ms=10, mew=3)
Important properties of the proposed solution are:
The ICP is an iterative algorithm. Thus it depends on an initial approximate solution. Furthermore, it won't always converge to the global optimum. This mainly depends on your data and the initial solution. If in doubt, try evaluating the ICP with different starting configurations and choose the most frequent result.
The current implementation performs a directed match: It looks for the closest point in Y relative to each point in X. It might yield different matches when swapping X and Y.
Computing all pair-wise distances between points in X and points in Y might be intractable for large point clouds (like 20,000 points, as you indicated). Therefore, the line D = circDist(...) might get replaced by a more efficient approach, e.g. not evaluating all possible pairs.
All points contribute to the final rotation. If there are any outliers, they might distort the shift significantly. This can be overcome with a robust average like the median or simply by excluding points with large distance.
I have shapes constructed out of 8x8 squares. I need to tile them using the fewest number of squares of size 8x8, 16x16, 32x32 and 64x64. Four 8x8 squares arranged in a square can be replaced by a single 16x16 square, e.g.:
What algorithm can be used to achieve this?
This calls for a dynamic programming solution. I'll assume we have a square[r][c] array of booleans which is true if (r, c) has a 1x1 square (I've simplified the solution to work with 1x1, 2x2, 4x4 and 8x8 squares to make it easier to follow, but it's easy to adapt). Pad it with a wall of false sentinel values on the top row and left column.
Define a 2d count array, where count[r][c] refers to the number of 1x1 squares in the region above and to the left of (r, c). We can add them up using a dp algorithm:
count[0..n][0..m] = 0
for i in 1..n:
for j in 1..m:
count[i][j] = count[i-1][j] + count[i][j-1] -
count[i-1][j-1] + square[i][j]
The above works by adding up two regions we already know the sum of, subtracting the doubly-counted area and adding in the new cell. Using the count array, we can test if a square region is fully covered in 1x1 squares in constant time using a similar method:
// p1 is the top-left coordinate, p2 the bottom-right
function region_count(p1, p2):
return count[p1.r][p1.c] - count[p1.r][p2.c-1] -
count[p2.r-1][p1.c] + 2*count[p2.r-1][p2.c-1]
We then create a second 2d min_squares array, where min_squares[r][c] refers to the minimum number of squares required to cover the original 1x1 squares. These values can be calculates using another dp:
min_squares = count
for i in 1..n:
for j in 1..m:
for size in [2, 4, 8]:
if i >= size and j >= size and
region_count((i-size, j-size), (i, j)) == size*size:
min_squares[i][j] = min(min_squares[i][j],
min_squares[i-size-1][j] +
min_squares[i][j-size-1] -
min_squares[i-size-1][j-size-1] +
1)
In order to reconstruct the tiling needed to get the calculated minimum, we use an auxiliary size_used[r][c] array which we use to keep track of the size of square placed at (r, c). From this we can recursively reconstruct the tiling:
function reconstruct(i, j):
if i < 0 or j < 0:
return
place square of size size_used[i][j] at (i-size_used[i][j]+1, j-size_used[i][j]+1)
reconstruct(i-size_used[i][j], j)
reconstruct(i, j-size_used[i][j])
You might want to look at Optimal way for partitioning a cell based shape into a minimal amount of rectangles - if I understood correctly, this is the same problem but for rectangles instead of squares.
Actually this is a classic problem as SO user Victor put it (in another SO question regarding which tasks to ask during an interview).
I couldn't do it in an hour (sigh) so what is the algorithm that calculates the number of integer points within a triangle?
EDIT: Assume that the vertices are at integer coordinates. (otherwise it becomes a problem of finding all points within the triangle and then subtracting all the floating points to be left with only the integer points; a less elegant problem).
Assuming the vertices are at integer coordinates, you can get the answer by constructing a rectangle around the triangle as explained in Kyle Schultz's An Investigation of Pick's Theorem.
For a j x k rectangle, the number of interior points is
I = (j – 1)(k – 1).
For the 5 x 3 rectangle below, there are 8 interior points.
(source: uga.edu)
For triangles with a vertical leg (j) and a horizontal leg (k) the number of interior points is given by
I = ((j – 1)(k – 1) - h) / 2
where h is the number of points interior to the rectangle that are coincident to the hypotenuse of the triangles (not the length).
(source: uga.edu)
For triangles with a vertical side or a horizontal side, the number of interior points (I) is given by
(source: uga.edu)
where j, k, h1, h2, and b are marked in the following diagram
(source: uga.edu)
Finally, the case of triangles with no vertical or horizontal sides can be split into two sub-cases, one where the area surrounding the triangle forms three triangles, and one where the surrounding area forms three triangles and a rectangle (see the diagrams below).
The number of interior points (I) in the first sub-case is given by
(source: uga.edu)
where all the variables are marked in the following diagram
(source: uga.edu)
The number of interior points (I) in the second sub-case is given by
(source: uga.edu)
where all the variables are marked in the following diagram
(source: uga.edu)
Pick's theorem (http://en.wikipedia.org/wiki/Pick%27s_theorem) states that the surface of a simple polygon placed on integer points is given by:
A = i + b/2 - 1
Here A is the surface of the triangle, i is the number of interior points and b is the number of boundary points. The number of boundary points b can be calculated easily by summing the greatest common divisor of the slopes of each line:
b = gcd(abs(p0x - p1x), abs(p0y - p1y))
+ gcd(abs(p1x - p2x), abs(p1y - p2y))
+ gcd(abs(p2x - p0x), abs(p2y - p0y))
The surface can also be calculated. For a formula which calculates the surface see https://stackoverflow.com/a/14382692/2491535 . Combining these known values i can be calculated by:
i = A + 1 - b/2
My knee-jerk reaction would be to brute-force it:
Find the maximum and minimum extent of the triangle in the x and y directions.
Loop over all combinations of integer points within those extents.
For each set of points, use one of the standard tests (Same side or Barycentric techniques, for example) to see if the point lies within the triangle. Since this sort of computation is a component of algorithms for detecting intersections between rays/line segments and triangles, you can also check this link for more info.
This is called the "Point in the Triangle" test.
Here is an article with several solutions to this problem: Point in the Triangle Test.
A common way to check if a point is in a triangle is to find the vectors connecting the point to each of the triangle's three vertices and sum the angles between those vectors. If the sum of the angles is 2*pi (360-degrees) then the point is inside the triangle, otherwise it is not.
Ok I will propose one algorithm, it won't be brilliant, but it will work.
First, we will need a point in triangle test. I propose to use the "Barycentric Technique" as explained in this excellent post:
http://www.blackpawn.com/texts/pointinpoly/default.html
Now to the algorithm:
let (x1,y1) (x2,y2) (x3,y3) be the triangle vertices
let ymin = floor(min(y1,y2,y3)) ymax = ceiling(max(y1,y2,y3)) xmin = floor(min(x1,x2,x3)) ymax = ceiling(max(x1,x2,3))
iterating from xmin to xmax and ymin to ymax you can enumerate all the integer points in the rectangular region that contains the triangle
using the point in triangle test you can test for each point in the enumeration to see if it's on the triangle.
It's simple, I think it can be programmed in less than half hour.
I only have half an answer for a non-brute-force method. If the vertices were integer, you could reduce it to figuring out how to find how many integer points the edges intersect. With that number and the area of the triangle (Heron's formula), you can use Pick's theorem to find the number of interior integer points.
Edit: for the other half, finding the integer points that intersect the edge, I suspect that it's the greatest common denominator between the x and y difference between the points minus one, or if the distance minus one if one of the x or y differences is zero.
Here's another method, not necessarily the best, but sure to impress any interviewer.
First, call the point with the lowest X co-ord 'L', the point with the highest X co-ord 'R', and the remaining point 'M' (Left, Right, and Middle).
Then, set up two instances of Bresenham's line algorithm. Parameterize one instance to draw from L to R, and the second to draw from L to M. Run the algorithms simultaneously for X = X[L] to X[M]. But instead of drawing any lines or turning on any pixels, count the pixels between the lines.
After stepping from X[L] to X[M], change the parameters of the second Bresenham to draw from M to R, then continue to run the algorithms simultaneously for X = X[M] to X[R].
This is very similar to the solution proposed by Erwin Smout 7 hours ago, but using Bresenham instead of a line-slope formula.
I think that in order to count the columns of pixels, you will need to determine whether M lies above or below the line LR, and of course special cases will arise when two points have the same X or Y co-ordinate. But by the time this comes up, your interviewer will be suitably awed and you can move on to the next question.
Quick n'dirty pseudocode:
-- Declare triangle
p1 2DPoint = (x1, y1);
p2 2DPoint = (x2, y2);
p3 2DPoint = (x3, y3);
triangle [2DPoint] := [p1, p2, p3];
-- Bounding box
xmin float = min(triangle[][0]);
xmax float = max(triangle[][0]);
ymin float = min(triangle[][1]);
ymax float = max(triangle[][1]);
result [[float]];
-- Points in bounding box might be inside the triangle
for x in xmin .. xmax {
for y in ymin .. ymax {
if a line starting in (x, y) and going in any direction crosses one, and only one, of the lines between the points in the triangle, or hits exactly one of the corners of the triangle {
result[result.count] = (x, y);
}
}
}
I have this idea -
Let A(x1, y1), B(x2, y2) and C(x3, y3) be the vertices of the triangle. Let 'count' be the number of integer points forming the triangle.
If we need the points on the triangle edges then using Euclidean Distance formula http://en.wikipedia.org/wiki/Euclidean_distance, the length of all three sides can be ascertained.
The sum of length of all three sides - 3, would give that count.
To find the number of points inside the triangle we need to use a triangle fill algorithm and instead of doing the actual rendering i.e. executing drawpixel(x,y), just go through the loops and keep updating the count as we loop though.
A triangle fill algorithm from
Fundamentals of Computer Graphics by
Peter Shirley,Michael Ashikhmin
should help. Its referred here http://www.gidforums.com/t-20838.html
cheers
I'd go like this :
Take the uppermost point of the triangle (the one with the highest Y coordinate). There are two "slopes" starting at that point. It's not the general solution, but for easy visualisation, think of one of both "going to the left" (decreasing x coordinates) and the other one "going to the right".
From those two slopes and any given Y coordinate less than the highest point, you should be able to compute the number of integer points that appear within the bounds set by the slopes. Iterating over decreasing Y coordinates, add all those number of points together.
Stop when your decreasing Y coordinates reach the second-highest point of the triangle.
You have now counted all points "above the second-highest point", and you are now left with the problem of "counting all the points within some (much smaller !!!) triangle, of which you know that its upper side parallels the X-axis.
Repeat the same procedure, but now with taking the "leftmost point" instead of the "uppermost", and with proceedding "by increasing x", instead of by "decreasing y".
After that, you are left with the problem of counting all the integer points within a, once again much smaller, triangle, of which you know that its upper side parallels the X-axis, and its left side parallels the Y-axis.
Keep repeating (recurring), until you count no points in the triangle you're left with.
(Have I now made your homework for you ?)
(wierd) pseudo-code for a bit-better-than-brute-force (it should have O(n))
i hope you understand what i mean
n=0
p1,p2,p3 = order points by xcoordinate(p1,p2,p3)
for int i between p1.x and p2.x do
a = (intersection point of the line p1-p2 and the line with x==i).y
b = (intersection point of the line p1-p3 and the line with x==i).y
n += number of integers between floats (a, b)
end
for i between p2.x+1 and p3.x do
a = (intersection point of the line p2-p3 and the line with x==i).y
b = (intersection point of the line p1-p3 and the line with x==i).y
n += number of integers between floats (a, b)
end
this algorithm is rather easy to extend for vertices of type float (only needs some round at the "for i.." part, with a special case for p2.x being integer (there, rounded down=rounded up))
and there are some opportunities for optimization in a real implementation
Here is a Python implementation of #Prabhala's solution:
from collections import namedtuple
from fractions import gcd
def get_points(vertices):
Point = namedtuple('Point', 'x,y')
vertices = [Point(x, y) for x, y in vertices]
a, b, c = vertices
triangle_area = abs((a.x - b.x) * (a.y + b.y) + (b.x - c.x) * (b.y + c.y) + (c.x - a.x) * (c.y + a.y))
triangle_area /= 2
triangle_area += 1
interior = abs(gcd(a.x - b.x, a.y - b.y)) + abs(gcd(b.x - c.x, b.y - c.y)) + abs(gcd(c.x - a.x, c.y - a.y))
interior /= 2
return triangle_area - interior
Usage:
print(get_points([(-1, -1), (1, 0), (0, 1)])) # 1
print(get_points([[2, 3], [6, 9], [10, 160]])) # 289
I found a quite useful link which clearly explains the solution to this problem. I am weak in coordinate geometry so I used this solution and coded it in Java which works (at least for the test cases I tried..)
Link
public int points(int[][] vertices){
int interiorPoints = 0;
double triangleArea = 0;
int x1 = vertices[0][0], x2 = vertices[1][0], x3 = vertices[2][0];
int y1 = vertices[0][1], y2 = vertices[1][1], y3 = vertices[2][1];
triangleArea = Math.abs(((x1-x2)*(y1+y2))
+ ((x2-x3)*(y2+y3))
+ ((x3-x1)*(y3+y1)));
triangleArea /=2;
triangleArea++;
interiorPoints = Math.abs(gcd(x1-x2,y1-y2))
+ Math.abs(gcd(x2-x3, y2-y3))
+ Math.abs(gcd(x3-x1, y3-y1));
interiorPoints /=2;
return (int)(triangleArea - interiorPoints);
}