The file attached with this problem contains a list having 1 0 0 lines. Eight numbers x 1 , y 1 , x 2 , y 2 , x 3 , y 3 , x 4 , y 4 are written in each line, which correspond to a quadrilateral whose vertices have coordinates A 1 = ( x 1 , y 1 ) , A 2 = ( x 2 , y 2 ) , A 3 = ( x 3 , y 3 ) , A 4 = ( x 4 , y 4 ) (in Cartesian plane). There are N quadrilaterals in this list which are concave.
Find N + 6 .
Details and Assumptions:
As an explicit example, suppose the list had 2 lines, and the following numbers were written on those two lines: 1 , 1 , 1 , − 1 , 2 , 2 , 2 , − 2 , 3 , 3 , 3 , − 3 , 4 , 4 , 4 − 4 . We have two quadrilaterals, the first one having its vertices at points ( 1 , 1 ) , ( 2 , 2 ) , ( 3 , 3 ) , ( 4 , 4 ) , and the second one having its vertices at points ( 1 , − 1 ) , ( 2 , − 2 ) , ( 3 , − 3 ) , ( 4 , − 4 ) . Your job is to find out how many of them are concave.
Link to the file: http://pastebin.ca/2679837 .
If one of the vertices lies on the line joining two other vertices, consider the resulting figure as convex.
This is a computer science problem, and is inspired from ProjectEuler (I don't remember the problem number at the moment).
This section requires Javascript.
You are seeing this because something didn't load right. We suggest you, (a) try
refreshing the page, (b) enabling javascript if it is disabled on your browser and,
finally, (c)
loading the
non-javascript version of this page
. We're sorry about the hassle.
The right answer is , since there are 29 concave quadrilaterals. I tried to understand why you found a wrong result, although the theory you describe is generally right (except for some mistakes in the expressions for , and . So the error is in programming. First of all I couldn't run your program because you define "translate" functions and you call "transform" functions. Then I had to correct the differences within each of these 4 functions. Note that when you translate a point to (0,0), you must return the new coordinates of the other 3 points: def transform1(x1, y1, x2, y2, x3, y3, x4, y4): return [x2-x1, y2-y1, x3-x1, y3-y1, x4-x1, y4-y1] def transform2(x1, y1, x2, y2, x3, y3, x4, y4): return [x1-x2, y1-y2, x3-x2, y3-y2, x4-x2, y4-y2] def transform3(x1, y1, x2, y2, x3, y3, x4, y4): return [x1-x3, y1-y3, x2-x3, y2-y3, x4-x3, y4-y3] def transform4(x1, y1, x2, y2, x3, y3, x4, y4): return [x1-x4, y1-y4, x2-x4, y2-y4, x3-x4, y3-y4] The next error was in 2 lines of expressions in within_triangle: num2= (y1-y3) x3 + (x3-x1) y3 num3= (y2-y1) x2 + (x1-x2) y2
I am learning python and computer science is quite new for me. First, I struggled with handling the raw data. I copied the text to a text file on the desktop and wrote:
list=open('C:/Users/Admin/Desktop/text.txt').read().split()
case=[]
for i in range(100):
vertex=()
for x in range(8):
vertex+=(list[8*i+x],)
case.append(vertex)
to create a list of 100 tuples, each tuple contains the coordinates of vertex of a quadrilateral.
The next step I thought of is to write a procedure to test whether a quadrilateral is convex or not, given its vertex's coordinates. My approach was: given X,Y,Z,T is 4 vertex, consider whether X,Y in the same side with regard to ZT and wheter Z,T in the same side with regard to XY. If both cases result in True or False, then the quadrilateral is convex. If one case is True, one case is False, then the quadrilateral is concave. So I wrote:
def same(x,y,z,t):
a=(y[0]-x[0],y[1]-x[1])
b=(z[0]-x[0],z[1]-x[1])
c=(t[0]-x[0],t[1]-x[1])
d=(a[1],-a[0])
e=(b[0]*d[0]+b[1]*d[1])*(c[0]*d[0]+c[1]*d[1])
return e
def test(x,y,z,t):
if same(x,y,z,t)*same(z,t,x,y)<0:
return False
return True
The "test" procedure will return True if the quadrilateral is convex. Then I wrote:
n=0
for i in range(100):
x=(int(group[i][0]),int(group[i][1]))
y=(int(group[i][2]),int(group[i][3]))
z=(int(group[i][4]),int(group[i][5]))
t=(int(group[i][6]),int(group[i][7]))
if not (test(x,y,z,t)):
n+=1
print n
to test 100 given quadrilaterals. This printed 29, so the answer is 3 5
Could you clarify on how your concavity testing procedure works?
Log in to reply
Yes, for example, ABCD is a convex quadrilateral:
A,B in the same side with regard to CD while C,D in the same side with regard to AB
A,C in the different sides with regard to BD while B,D in the different sides with regard to AC
(Similar with AD-BC)
Therefore, the relative positions of two vertex X,Y with regard to ZT and Z,T with regard to XY are the same (both same side or both different sides).
If ABCD is a concave quadrilateral, it's different, if X,Y in same side with regard to ZT, then Z,T must be in different sides with regard to XY and vice versa.
Therefore, I wrote the procedure same(x,y,z,t), which return a positive number if Z,T in the same side with regard to XY and a negative number otherwise.
Then I wrote the test(x,y,z,t) procedure by calculate the product of same(x,y,z,t) and same(z,t,x,y).
First, note that the convexity / concavity of a quadrilateral doesn't change if it is translated. Given a quadrilateral A 1 ( x 1 , y 1 ) , A 2 ( x 2 , y 2 ) , A 3 ( x 3 , y 3 ) , A 4 ( x 4 , y 4 ) , we can translate it in four ways so that one of the vertices goes to the origin. If we want A 4 to go to the origin, the coordinates of A 1 , A 2 , A 3 should be ( x 1 − x 4 , y 1 − y 4 ) , ( x 2 − y 4 , y 2 − y 4 ) , ( x 3 − y 4 , y 3 − y 4 ) respectively. Similarly we can determine the coordinates of the other vertices given that one of the vertices goes to the origin.
In the following code, we build four functions
translate1
,
translate2
,
translate3
,
translate4
which return the coordinates of the quadrilateral when it is translated in such a way that
A
1
,
A
2
,
A
3
,
A
4
go to the origin respectively.
def transate1(x1, y1, x2, y2, x3, y3, x4, y4):
return [x1-x1, y1-y1, x2-x1, y2-y1, x3-x1, y3-y1]
def translate2(x1, y1, x2, y2, x3, y3, x4, y4):
return [x1-x2, y1-y2, x2-x2, y2-y2, x3-x2, y3-y2]
def translate3(x1, y1, x2, y2, x3, y3, x4, y4):
return [x1-x3, y1-y3, x2-x3, y2-y3, x3-x3, y3-y3]
def translate4(x1, y1, x2, y2, x3, y3, x4, y4):
return [x1-x4, y1-y4, x2-x4, y2-y4, x3-x4, y3-y4]
A quadrilateral is concave if and only if one of its vertices lies inside the triangle determined by the other three vertices.
In general, consider a triangle A ( x 1 , y 1 ) , B ( x 2 , y 2 ) , C ( x 3 , y 3 ) . We have to find out whether the origin O ( 0 , 0 ) lies within this triangle or not. Let ( B A , B B , B C ) be the normalized barycentric coordinates of O with respect to △ A B C , where B A + B B + B C = 1 . Assign the vectors O , A , B , C to points O , A , B , C respectively. Let i and j be the unit vectors along the x and y axes respectively. We then have O = = = B A A + B B B + B C C B A ( x 1 ⋅ i + y 1 ⋅ j ) + B B ( x 2 ⋅ i + y 2 ⋅ j ) + B C ( x 3 ⋅ i + y 3 ⋅ j ) ( B A x 1 + B B x 2 + B C x 3 ) ⋅ i + ( B A y 1 + B B y 2 + B C y 3 ) ⋅ j . Since O is the origin, O = 0 ⋅ i + 0 ⋅ j . We thus have three equations in B A , B B , B C . B A x 1 + B B x 2 + B C x 3 B A y 1 + B B y 2 + B C y 3 B A + B B + B C = = = 0 0 1
After some tedious calculations (I fed it in WolframAlpha, although the determinant method should work just fine. :P ), we find out that the solutions are:
B
A
B
B
B
C
=
=
=
(
y
2
−
y
3
)
(
x
1
−
x
2
)
+
(
x
3
−
x
3
)
(
y
1
−
y
3
)
x
3
(
y
3
−
y
2
)
+
y
3
(
x
2
−
x
3
)
(
y
2
−
y
3
)
(
x
1
−
x
2
)
+
(
x
3
−
x
3
)
(
y
1
−
y
3
)
x
3
(
y
1
−
y
3
)
+
y
3
(
x
1
−
x
3
)
(
y
2
−
y
3
)
(
x
1
−
x
2
)
+
(
x
3
−
x
3
)
(
y
1
−
y
3
)
x
3
(
y
2
−
y
1
)
+
y
3
(
x
2
−
x
1
)
.
O
will lie within
△
A
B
C
if and only if
B
A
,
B
B
,
B
C
>
0
.
I don't know why, but Python can't compare between two float numbers. Instead of finding the signs of
B
A
,
B
B
,
B
C
directly, we can compare the signs of the numerators and denominators of
B
A
,
B
B
,
and
B
C
.
We define
sgn
(
x
)
:
R
→
{
0
,
1
,
−
1
}
as follows.
⎩
⎪
⎨
⎪
⎧
sgn
(
x
)
=
1
sgn
(
x
)
=
0
sgn
(
x
)
=
−
1
if
x
>
0
if
x
=
0
if
x
<
0
Here's the implementation of
sgn
(
x
)
in Python.
def sgn(x):
if x==0:
return 0
if x>0:
return 1
if x<0:
return -1
Now, note that a number b a ( b = 0 ) is positive if and only if sgn ( a ) = sgn ( b ) . Therefore, our required condition is: sgn ( x 3 ( y 3 − y 2 ) + y 3 ( x 2 − x 3 ) ) sgn ( x 3 ( y 1 − y 3 ) + y 3 ( x 1 − x 3 ) ) sgn ( x 3 ( y 2 − y 1 ) + y 3 ( x 2 − x 1 ) ) = = = sgn ( ( y 2 − y 3 ) ( x 1 − x 3 ) + ( x 3 − x 2 ) ( y 1 − y 3 ) ) sgn ( ( y 2 − y 3 ) ( x 1 − x 3 ) + ( x 3 − x 2 ) ( y 1 − y 3 ) ) sgn ( ( y 2 − y 3 ) ( x 1 − x 3 ) + ( x 3 − x 2 ) ( y 1 − y 3 ) ) . In the following Python code, we define num1 num2 num3 den = = = = x 3 ( y 3 − y 2 ) + y 3 ( x 2 − x 3 ) x 3 ( y 1 − y 3 ) + y 3 ( x 1 − x 3 ) x 3 ( y 2 − y 1 ) + y 3 ( x 2 − x 1 ) ( y 2 − y 3 ) ( x 1 − x 3 ) + ( x 3 − x 3 ) ( y 1 − y 3 ) , so our required condition is sgn(num1) = sgn(num2) = sgn(num3) = sgn(den).
def within_triangle(x1, y1, x2, y2, x3, y3):
num1= (y3-y2)*x3 + (x2-x3)*y3
num2= (y1-y3)*x3 + (x3-x1)*x3
num3= (y2-y1)*x3 + (x2-x1)*y3
den= (y2-y3)*(x1-x3) + (x3-x2)*(y1-y3)
return sgn(den)==sgn(num1) & sgn(den)==sgn(num2) & sgn(den)==sgn(num3)
We just have to check whether the origin lies in atleast one of the triangles returned by the translate functions. The following code take cares of that.
def concavity(x1, y1, x2, y2, x3, y3, x4, y4):
a= transform1(x1, y1, x2, y2, x3, y3, x4, y4)
p= within_triangle(a[0], a[1], a[2], a[3], a[4], a[5])
b= transform2(x1, y1, x2, y2, x3, y3, x4, y4)
q= within_triangle(b[0], b[1], b[2], b[3], b[4], b[5])
c= transform3(x1, y1, x2, y2, x3, y3, x4, y4)
r= within_triangle(c[0], c[1], c[2], c[3], c[4], c[5])
d= transform4(x1, y1, x2, y2, x3, y3, x4, y4)
s= within_triangle(d[0], d[1], d[2], d[3], d[4], d[5])
return p==True or q==True or r==True or s==True
All that remains is to handle the input data. The following code goes through each line of the file and checks whether the quadrilateral formed is concave or not. The variable
count
stores the number of concave quadrilaterals the program has encountered.
f= open('convex_quadrilaterals.txt','r')
a= []
count=0
for line in f:
a= [int(x) for x in line.split()]
if concavity(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7])==True:
count+=1
print count
This prints 3 6 , so N − 1 = 3 5 .
The right answer is 2 8 , since there are 29 concave quadrilaterals.
I tried to understand why you found a wrong result, although the theory you describe is generally right (except for some mistakes in the expressions for B A , B B and B C . So the error is in programming.
First of all I couldn't run your program because you define "translate" functions and you call "transform" functions. Then I had to correct the differences within each of these 4 functions. Note that when you translate a point to (0,0), you must return the new coordinates of the other 3 points:
1 2 3 4 5 6 7 8 |
|
The next error was in 2 lines of expressions in within_triangle:
1 2 |
|
Finally the return command of within_triangle had a complex expression using bitwise & operator instead of logical a n d operator. Since bitwise operators have higher precedence than comparison operators, the result is not the expected. So the correct command is:
1 |
|
After the above mentioned corrections, the program executed and printed the right count: 29. So the right result is 2 8 . Please make the necessary corrections.
Log in to reply
Oh yes, you're right! Sorry for my ignorance, can't believe I made this mistake. I've corrected the problem.
I wrote program at http://jsbin.com/refub/1/edit and got answer 73.
My mistake was that I did not check whether ABCD is quadrangle at all. If it has selfcrossings, then it is not a quadrangle.
Now I have only 29 concave quadrangles.
I tried with something like this: A, B, C and D are three points. If angle between line A,B and line B,C is equal to 0 degree, that means one of the vertices is on a line. Tried to get angle betweeen every possible lines and check if any couple of line creates angle of 0 degree. Was my Idea ok? Actually none of those couples created exact 0 degree angle between them.
Log in to reply
I can't see how that works. You're just checking whether one of the quadrilaterals formed is degenerate.
Log in to reply
Checked Angle Between A-B & B-C, B-C & C-D, C-D & D-A, D-A & A-B, B-C & C-A, D-C & C-A, A-D & D-B and C-D & D-B for every row.
Log in to reply
@Moin Uddin – Yes, but you're checking if one of the angles is zero. In other words, you're checking whether three points are collinear. That isn't a test for concavity / convexity.
To test if A B C D is a concave quadrilateral, we test if A in B C D or B in A C D or C in A B D or D in A B C , that means we test if a point is inside the triangle formed by other three points. To test if A is in B C D , we test if A , B , A , C , A , D , are the same side to C D , B D , B C respectively. To test if A , B is the same side to C D , we first choose vector n ( − y C D , x C D ) , then n ⊥ C D . If ( n ⋅ C A ) ( n ⋅ C B ) ≥ 0 then A , B must be same side to C D
Here is the code to count the quadrilaterals
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
Problem Loading...
Note Loading...
Set Loading...
This was a nice problem. First of all,as a rule of thumb in Computational geometry it is not good to deal with angles.It is exceedingly difficult and things get messy soon. A nice way to handle this problem is to use the C C W algorithm. C C W takes three points A , B , and C as arguments and asks: do these three points make a counterclockwise turn,in other words is ∠ A B C a counterclockwise angle. Checking concavity is straightforward from there,sort the points in a clockwise order and check if each three pair of angles is C C W .