How to detect order of parallel line segments? - algorithm

Suppose more than two (nearly) line segments are given. How can I calculate the position order of them?
Example:
The result I expect from the input
(x_0a,y_0a), (x_0b,y_0b), (x_1a,y_1a), (x_1b,y_1b), ... , (x_4a,y_4a), (x_4b,y_4b)
is
L_0 -> L_4 -> L_2 -> L_1 -> L_3

Choose arbitrary segment end as base point. For example, (x0a, y0a).
Make vector for this segment (normalization is not needed)
v = (x0b,y0b) - (x0a,y0a)
Make perpendicular vector
n = (-v.y, v.x)
Calculate projection value of arbitrary end of every segment on the line, perpendicular to v (this value is the same for both ends of segment)
d(i) = (xia-x0a, yia-y0a)
p(i) = Dot(d(i), n)
Sort p(i)

Build linear equation for every segment:
Ai*x + Bi*y + Ci = 0, where
Ai = y_ib - y_ia
Bi = x_ia - x_ib
Ci = -A * x_ia - B * y_ia
For every equation substitute x with 0, and find y
Bi*y + Ci = 0
y = - Ci / Bi
Then sort segments by the resultant y.
This works when segments are not parallel to the y-axis. If they are parallel to the y-axis, then substitute y with 0, find x, and sort by x.

Related

Writing a vector sum in MATLAB

Suppose I have a function phi(x1,x2)=k1*x1+k2*x2 which I have evaluated over a grid where the grid is a square having boundaries at -100 and 100 in both x1 and x2 axis with some step size say h=0.1. Now I want to calculate this sum over the grid with which I'm struggling:
What I was trying :
clear all
close all
clc
D=1; h=0.1;
D1 = -100;
D2 = 100;
X = D1 : h : D2;
Y = D1 : h : D2;
[x1, x2] = meshgrid(X, Y);
k1=2;k2=2;
phi = k1.*x1 + k2.*x2;
figure(1)
surf(X,Y,phi)
m1=-500:500;
m2=-500:500;
[M1,M2,X1,X2]=ndgrid(m1,m2,X,Y)
sys=#(m1,m2,X,Y) (k1*h*m1+k2*h*m2).*exp((-([X Y]-h*[m1 m2]).^2)./(h^2*D))
sum1=sum(sys(M1,M2,X1,X2))
Matlab says error in ndgrid, any idea how I should code this?
MATLAB shows:
Error using repmat
Requested 10001x1001x2001x2001 (298649.5GB) array exceeds maximum array size preference. Creation of arrays greater
than this limit may take a long time and cause MATLAB to become unresponsive. See array size limit or preference
panel for more information.
Error in ndgrid (line 72)
varargout{i} = repmat(x,s);
Error in new_try1 (line 16)
[M1,M2,X1,X2]=ndgrid(m1,m2,X,Y)
Judging by your comments and your code, it appears as though you don't fully understand what the equation is asking you to compute.
To obtain the value M(x1,x2) at some given (x1,x2), you have to compute that sum over Z2. Of course, using a numerical toolbox such as MATLAB, you could only ever hope to compute over some finite range of Z2. In this case, since (x1,x2) covers the range [-100,100] x [-100,100], and h=0.1, it follows that mh covers the range [-1000, 1000] x [-1000, 1000]. Example: m = (-1000, -1000) gives you mh = (-100, -100), which is the bottom-left corner of your domain. So really, phi(mh) is just phi(x1,x2) evaluated on all of your discretised points.
As an aside, since you need to compute |x-hm|^2, you can treat x = x1 + i x2 as a complex number to make use of MATLAB's abs function. If you were strictly working with vectors, you would have to use norm, which is OK too, but a bit more verbose. Thus, for some given x=(x10, x20), you would compute x-hm over the entire discretised plane as (x10 - x1) + i (x20 - x2).
Finally, you can compute 1 term of M at a time:
D=1; h=0.1;
D1 = -100;
D2 = 100;
X = (D1 : h : D2); % X is in rows (dim 2)
Y = (D1 : h : D2)'; % Y is in columns (dim 1)
k1=2;k2=2;
phi = k1*X + k2*Y;
M = zeros(length(Y), length(X));
for j = 1:length(X)
for i = 1:length(Y)
% treat (x - hm) as a complex number
x_hm = (X(j)-X) + 1i*(Y(i)-Y); % this computes x-hm for all m
M(i,j) = 1/(pi*D) * sum(sum(phi .* exp(-abs(x_hm).^2/(h^2*D)), 1), 2);
end
end
By the way, this computation takes quite a long time. You can consider either increasing h, reducing D1 and D2, or changing all three of them.

Searching a 3D array for closest point satisfying a certain predicate

I'm looking for an enumeration algorithm to search through a 3D array "sphering" around a given starting point.
Given an array a of size NxNxN where each N is 2^k for some k, and a point p in that array. The algorithm I'm looking for should do the following: If a[p] satisfies a certain predicate, the algorithm stops and p is returned. Otherwise the next point q is checked, where q is another point in the array that is the closest to p and hasn't been visited yet. If that doesn't match either, the next q'is checked an so on until in the worst case the whole array has been searched.
By "closest" here the perfect solution would be the point q that has the smallest Euclidean distance to p. As only discrete points have to be considered, perhaps some clever enumeration algorithm woukd make that possible. However, if this gets too complicated, the smallest Manhattan distance would be fine too. If there are several nearest points, it doesn't matter which one should be considered next.
Is there already an algorithm that can be used for this task?
You can search for increasing squared distances, so you won't miss a point. This python code should make it clear:
import math
import itertools
# Calculates all points at a certain distance.
# Coordinate constraint: z <= y <= x
def get_points_at_squared_euclidean_distance(d):
result = []
x = int(math.floor(math.sqrt(d)))
while 0 <= x:
y = x
while 0 <= y:
target = d - x*x - y*y
lower = 0
upper = y + 1
while lower < upper:
middle = (lower + upper) / 2
current = middle * middle
if current == target:
result.append((x, y, middle))
break
if current < target:
lower = middle + 1
else:
upper = middle
y -= 1
x -= 1
return result
# Creates all possible reflections of a point
def get_point_reflections(point):
result = set()
for p in itertools.permutations(point):
for n in range(8):
result.add((
p[0] * (1 if n % 8 < 4 else -1),
p[1] * (1 if n % 4 < 2 else -1),
p[2] * (1 if n % 2 < 1 else -1),
))
return sorted(result)
# Enumerates all points around a center, in increasing distance
def get_next_point_near(center):
d = 0
points_at_d = []
while True:
while not points_at_d:
d += 1
points_at_d = get_points_at_squared_euclidean_distance(d)
point = points_at_d.pop()
for reflection in get_point_reflections(point):
yield (
center[0] + reflection[0],
center[1] + reflection[1],
center[2] + reflection[2],
)
# The function you asked for
def get_nearest_point(center, predicate):
for point in get_next_point_near(center):
if predicate(point):
return point
# Example usage
print get_nearest_point((1,2,3), lambda p: sum(p) == 10)
Basically you consume points from the generator until one of them fulfills your predicate.
This is pseudocode for a simple algorithm that will search in increasing-radius spherical husks until it either finds a point or it runs out of array. Let us assume that condition returns either true or false and has access to the x, y, z coordinates being tested and the array itself, returning false (instead of exploding) for out-of-bounds coordinates:
def find_from_center(center, max_radius, condition) returns a point
let radius = 0
while radius < max_radius,
let point = find_in_spherical_husk(center, radius, condition)
if (point != null) return point
radius ++
return null
the hard part is inside find_in_spherical_husk. We are interested in checking out points such that
dist(center, p) >= radius AND dist(center, p) < radius+1
which will be our operating definition of husk. We could iterate over the whole 3D array in O(n^3) looking for those, but that would be really expensive in terms of time. A better pseudocode is the following:
def find_in_spherical_husk(center, radius, condition)
let z = center.z - radius // current slice height
let r = 0 // current circle radius; maxes at equator, then decreases
while z <= center + radius,
let z_center = (z, center.x, point.y)
let point = find_in_z_circle(z_center, r)
if (point != null) return point
// prepare for next z-sliced cirle
z ++
r = sqrt(radius*radius - (z-center.z)*(z-center.z))
the idea here is to slice each husk into circles along the z-axis (any axis will do), and then look at each slice separately. If you were looking at the earth, and the poles were the z axis, you would be slicing from north to south. Finally, you would implement find_in_z_circle(z_center, r, condition) to look at the circumference of each of those circles. You can avoid some math there by using the Bresenham circle-drawing algorithm; but I assume that the savings are negligible compared with the cost of checking condition.

Calculating normal to line towards given side

Given is a line (segment), defined by two vectors start(x,y) and end(x,y). I also have a point p(x,y), which is on either of the two areas separated by the line (i.e. it is not exactly on the line).
How can I calculate the normal to the line that is facing towards the side in which p is?
Let:
A = (a,b) and B = (c,d) define the line segment
P = (p,q) be the other point.
Define:
dot( (p,q), (r,s) ) == p*r + q*s
Then the vector:
v = ( c-a, d-b)
defines the direction along the line segment. Its perpendicular is:
u = (d-b, (-(c-a)) = (d-b,a-c)
This can be seen by taking the dot product with v. To get the normal from the perpendicular, just divide by its length:
n = u /|u|, |u| = sqrt( dot(u,u))
We now just need to know where P lies relative to the normal. If we take:
dir = dot( (P-A), n) )
Then dir > 0 means n is in the same direction as P, whilst dir < 0 means it is in the opposite direction. Should dir == 0, then P is in fact on the extended line (not necessarily the line segment itself).
First, determine which side of the line the point lies on, by taking the cross product of end-start and p-end:
z = (xend-xstart)(yp-yend) - (yend-ystart)(xp-xend)
If z>0, then the point is to the left of the line (as seen by a person standing at start and facing end). If z<0, then the point is to the right of the line.
Second, normalize the line segment:
S = end - start
k = S/|S|
Finally, if the point is to the left of the line, rotate k to the left:
(xk, yk) => (-yk, xk)
or if the point is to the right of the line, rotate k to the right:
(xk, yk) => (yk, -xk)
My math skills are a bit rusty, so I can't give you the exact calculations, but what you do is this (assuming 2D from your description):
First you calculate a normal n.
Then you calculate P' which is the perpendicular projection of your point P onto your line.
Basically, what you do is, you "create" another line and use your vector n from step 1 as the direction (y = p + x * n where y,p and n are vectors, p is actually your p(x,y) and x is a real number), then you intersect this line with the first one and the point where they intersect is P'.
Seeing you're from Austria, everyone else please forgive me for using one German word, I really don't know the English translation and couldn't find any. P' = Lotfußpunkt
Calculate P - P'. If it has the same sign as n in both components, n is the normal you're searching for. If it has the opposite sign, -n is the one you're searching for.
I hope the idea is clear even though I don't know all the technical terms in English.
For
start = (a,b)
end = (c,d)
p = (x,y)
Slope(startend) = (d - b) / (c - a)
Slope(norm) = -(c - a) / (d - b)
Norm line must include p = (x,y), so
ynorm = -((c - a) / (d - b)) * xnorm + (y + ((c - a) / (d - b)) * x)
y = mx + c
is the normal line equation where m is the slope and c is any constant.
You have start and end. Lets call them (x1,y1) and (x2,y2) and the line joining them L1.
The slope of this line, m1, is (y2-y1)/(x2-x1). This line is perpendicular to the line you need which we can call L2 with slope m2. The product of slopes of 2 mutuallu perpendicular lines is -1. Hence,
m1*m2=-1.
Hence you can calculate m2. Now, you need to find the equation of the line L2. You have 1 point in the line P (x,y). You can substitute in this manner:
y=m2*x+c.
This will give you c. Once you have the line equation, you can convert it to parametric form as shown below:
http://thejuniverse.org/PUBLIC/LinearAlgebra/LOLA/lines/index.html
The equation of the line is given as
A = start.y-end.y
B = end.x-start.x
C = start.x*end.y-start.y*end.x
A*x + B*y + C = 0
The minimum distance d to the line of a point p=(px,py) is
d = (A*px+B*py+C)/sqrt(A^2+B^2)
If the value is positive then the point is at a counter clockwise rotation from the vector (start->end). If negative then it is in clockwise rotation. So if (start->end) is pointing up, then a positive distance is to the left of the line.
Example
start = (8.04, -0.18)
end = (6.58, 1.72)
P = (2.82, 0.66)
A = (-0.18)-(1.72) = -1.9
B = (6.58)-(8.04) = -1.46
C = (8.04)*(1.72)-(-0.18)*(6.58) = 15.01
d = (A*(2.82)+B*(0.66)+C)/√(A^2+B^2) = 3.63
The calculation for d shows the identical value as the length of vector (near->P) in the sketch.
N = (Ey - Sy, Sx - Ex) is perpendicular to the line (it is SE rotated by 90°, not normalized).
Then compute the sign of the dot product
N . SP = (Ey - Sy)(Px - Sx) + (Sx - Ex)(Py - Sy),
it will tell you on what side the normal is pointing.

Halton sequence extension

I am trying to fill an area defined by 2 intervals [a,b] x [c,d] with points uniformly distributed and I am implementing the Halton sequence. I am using the following code (which generates subunitary numbers).
The number I is input.
The number H is output.
for i = 1:N
H = 0
half = 1 / 2
I = rand() % MATLAB rand()
do while ( I is not zero )
digit = mod ( I, 2 )
H = H + digit * half
I = ( I - digit ) / 2
half = half / 2
end
x(i) = H
end
For the x-axis I use base 2 and for the y-axis I use base 3.
Because I divide by 2, 3 I seem to be unable to fill the whole [0,1] x [0,1] space completely. I have to fill [0,1] x [0,1] and I actually fill [0,0.5] x [0,0.35]. And when I try to extend the algorithm for [a,b] x [c,d] I get points in [a,b-0.5] x [c,d-1].
What can I do to fill the correct full intervals?

No of ways to walk M steps in a grid

You are situated in an grid at position x,y. The dimensions of the row is dx,dy. In one step, you can walk one step ahead or behind in the row or the column. In how many ways can you take M steps such that you do not leave the grid at any point ?You can visit the same position more than once.
You leave the grid if you for any x,y either x,y <= 0 or x,y > dx,dy.
1 <= M <= 300
1 <= x,y <= dx,dy <= 100
Input:
M
x y
dx dy
Output:
no of ways
Example:
Input:
1
6 6
12 12
Output:
4
Example:
Input:
2
6 6
12 12
Output:
16
If you are at position 6,6 then you can walk to (6,5),(6,7),(5,6),(7,6).
I am stuck at how to use Pascal's Triangle to solve it.Is that the correct approach? I have already tried brute force but its too slow.
C[i][j], Pascal Triangle
C[i][j] = C[i - 1][j - 1] + C[i - 1][j]
T[startpos][stp]
T[pos][stp] = T[pos + 1][stp - 1] + T[pos - 1][stp - 1]
You can solve 1d problem with the formula you provided.
Let H[pos][step] be number of ways to move horizontal using given number of steps.
And V[pos][step] be number of ways to move vertical sing given number of steps.
You can iterate number of steps that will be made horizontal i = 0..M
Number of ways to move so is H[x][i]*V[y][M-i]*C[M][i], where C is binomial coefficient.
You can build H and V in O(max(dx,dy)*M) and do second step in O(M).
EDIT: Clarification on H and V. Supppose that you have line, that have d cells: 1,2,...,d. You're standing at cell number pos then T[pos][step] = T[pos-1][step-1] + T[pos+1][step-1], as you can move either forward or backward.
Base cases are T[0][step] = 0, T[d+1][step] = 0, T[pos][0] = 1.
We build H assuming d = dx and V assuming d = dy.
EDIT 2: Basically, the idea of algorithm is since we move in one of 2 dimensions and check is also based on each dimension independently, we can split 2d problem in 2 1d problems.
One way would be an O(n^3) dynamic programming solution:
Prepare a 3D array:
int Z[dx][dy][M]
Where Z[i][j][n] holds the number of paths that start from position (i,j) and last n moves.
The base case is Z[i][j][0] = 1 for all i, j
The recursive case is Z[i][j][n+1] = Z[i-1][j][n] + Z[i+1][j][n] + Z[i][j-1][n] + Z[i][j+1][n] (only include terms in the sumation that are on the map)
Once the array is filled out return Z[x][y][M]
To save space you can discard each 2D array for n after it is used.
Here's a Java solution I've built for the original hackerrank problem. For big grids runs forever. Probably some smart math is needed.
long compute(int N, int M, int[] positions, int[] dimensions) {
if (M == 0) {
return 1;
}
long sum = 0;
for (int i = 0; i < N; i++) {
if (positions[i] < dimensions[i]) {
positions[i]++;
sum += compute(N, M - 1, positions, dimensions);
positions[i]--;
}
if (positions[i] > 1) {
positions[i]--;
sum += compute(N, M - 1, positions, dimensions);
positions[i]++;
}
}
return sum % 1000000007;
}

Resources