Skip to content

Commit ad5b77b

Browse files
committed
Added new coding challenge
1 parent 4ea994b commit ad5b77b

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed

src/lasershot_statues.py

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# -*- coding: utf-8 -*-
2+
import timeit
3+
import random
4+
5+
from concurrent.futures.thread import ThreadPoolExecutor
6+
7+
"""
8+
Returns the minimun number of lines required to reach all points (represented crystal statues)
9+
from position 0,0. A line represent a laser shot, so it can pass throught the statues.
10+
If two or more statues are in the same line from position 0,0 only one shot is needed
11+
to reach all of them.
12+
"""
13+
14+
15+
class Point2D:
16+
17+
def __init__(self, t):
18+
self.x = t[0]
19+
self.y = t[1]
20+
21+
def __repr__(self):
22+
return "(%d, %d)" % (self.x, self.y)
23+
24+
def __str__(self):
25+
return self.__repr__()
26+
27+
28+
def verify_points(p1, p2, p3):
29+
return quadrant(p2) == quadrant(p3) and are_collinear(p1, p2, p3)
30+
31+
32+
def are_collinear(p1, p2, p3):
33+
return (p1.y - p2.y) * (p1.x - p3.x) == (p1.y - p3.y) * (p1.x - p2.x)
34+
35+
36+
def quadrant(p):
37+
if p.x > 0 and p.y > 0:
38+
return "first"
39+
elif p.x < 0 and p.y > 0:
40+
return "second"
41+
elif p.x < 0 and p.y < 0:
42+
return "third"
43+
elif p.x > 0 and p.y < 0:
44+
return "fourth"
45+
elif p.x == 0 and p.y == 0:
46+
return "center"
47+
48+
49+
def solution_1(A):
50+
if not A:
51+
return 0
52+
center = Point2D((0, 0))
53+
points = []
54+
for p1 in A:
55+
ok = False
56+
for p2 in points:
57+
ok = verify_points(center, p1, p2)
58+
if ok:
59+
break
60+
if not ok:
61+
points.append(p1)
62+
return len(points)
63+
64+
65+
def split_by_quadrant(A):
66+
first = []
67+
second = []
68+
third = []
69+
fourth = []
70+
for p in A:
71+
if p.x > 0 and p.y > 0:
72+
first.append(p)
73+
elif p.x < 0 and p.y > 0:
74+
second.append(p)
75+
elif p.x < 0 and p.y < 0:
76+
third.append(p)
77+
elif p.x > 0 and p.y < 0:
78+
fourth.append(p)
79+
elif p.x == 0 and p.y == 0:
80+
pass
81+
return (first, second, third, fourth)
82+
83+
84+
def number_of_lines(A):
85+
center = Point2D((0, 0))
86+
points = []
87+
for p1 in A:
88+
ok = False
89+
for p2 in points:
90+
ok = are_collinear(center, p1, p2)
91+
if ok:
92+
break
93+
if not ok:
94+
points.append(p1)
95+
return len(points)
96+
97+
98+
def solution_2(A):
99+
count = 0
100+
if not A:
101+
return count
102+
A1, A2, A3, A4 = split_by_quadrant(A)
103+
count += number_of_lines(A1)
104+
count += number_of_lines(A2)
105+
count += number_of_lines(A3)
106+
count += number_of_lines(A4)
107+
return count
108+
109+
110+
def solution_3(A):
111+
count = 0
112+
if not A:
113+
return count
114+
A1, A2, A3, A4 = split_by_quadrant(A)
115+
t = ThreadPoolExecutor(4)
116+
f1 = t.submit(number_of_lines, (A1,))
117+
f2 = t.submit(number_of_lines, (A2,))
118+
f3 = t.submit(number_of_lines, (A3,))
119+
f4 = t.submit(number_of_lines, (A4,))
120+
count = f1.result() + f2.result() + f3.result() + f4.result()
121+
return count
122+
123+
124+
def solution_4(A):
125+
count = 0
126+
if not A:
127+
return count
128+
t = ThreadPoolExecutor(4)
129+
f1 = t.submit(number_of_lines, (filter(lambda p: p.x > 0 and p.y > 0, A),))
130+
f2 = t.submit(number_of_lines, (filter(lambda p: p.x < 0 and p.y > 0, A),))
131+
f3 = t.submit(number_of_lines, (filter(lambda p: p.x < 0 and p.y < 0, A),))
132+
f4 = t.submit(number_of_lines, (filter(lambda p: p.x > 0 and p.y < 0, A),))
133+
count = f1.result() + f2.result() + f3.result() + f4.result()
134+
return count
135+
136+
137+
def are_collinear_v2(p2, p3):
138+
return (0 - p2.y) * (0 - p3.x) == (0 - p3.y) * (0 - p2.x)
139+
140+
141+
def number_of_lines_v2(A):
142+
points = []
143+
for p1 in A:
144+
if not next((p2 for p2 in points if are_collinear_v2(p1, p2)), None):
145+
points.append(p1)
146+
return len(points)
147+
148+
149+
def solution_5(A):
150+
count = 0
151+
if not A:
152+
return count
153+
t = ThreadPoolExecutor(4)
154+
f1 = t.submit(number_of_lines_v2, (filter(lambda p: p.x > 0 and p.y > 0, A),))
155+
f2 = t.submit(number_of_lines_v2, (filter(lambda p: p.x < 0 and p.y > 0, A),))
156+
f3 = t.submit(number_of_lines_v2, (filter(lambda p: p.x < 0 and p.y < 0, A),))
157+
f4 = t.submit(number_of_lines_v2, (filter(lambda p: p.x > 0 and p.y < 0, A),))
158+
count = f1.result() + f2.result() + f3.result() + f4.result()
159+
return count
160+
161+
162+
def build_input(AA):
163+
A = []
164+
for t in AA:
165+
A.append(Point2D(t))
166+
return A
167+
168+
169+
def build_random_input(N=10000):
170+
A = []
171+
for _ in range(0, N):
172+
x = random.randint(0, 40000)
173+
y = random.randint(0, 40000)
174+
A.append(Point2D((x, y)))
175+
return A
176+
177+
178+
if __name__ == '__main__':
179+
A = build_input([(-1, -2), (1, 2), (2, 4), (-3, 2), (2, -2)])
180+
assert(solution_1(A) == 4)
181+
assert(solution_2(A) == 4)
182+
assert(solution_3(A) == 4)
183+
assert(solution_4(A) == 4)
184+
assert(solution_5(A) == 4)
185+
186+
A = build_input([(-1, -2), (1, 2), (2, 4), (-3, 2), (2, -2), (3, 6)])
187+
assert(solution_1(A) == 4)
188+
assert(solution_2(A) == 4)
189+
assert(solution_3(A) == 4)
190+
assert(solution_4(A) == 4)
191+
assert(solution_5(A) == 4)
192+
193+
assert(solution_1([]) == 0)
194+
assert(solution_2([]) == 0)
195+
assert(solution_3([]) == 0)
196+
assert(solution_4([]) == 0)
197+
assert(solution_5([]) == 0)
198+
199+
assert(solution_1(None) == 0)
200+
assert(solution_2(None) == 0)
201+
assert(solution_3(None) == 0)
202+
assert(solution_4(None) == 0)
203+
assert(solution_5(None) == 0)
204+
205+
A = build_random_input()
206+
g = {"solution_1": solution_1, "solution_2": solution_2,
207+
"solution_3": solution_3, "solution_4": solution_4, "solution_5": solution_5,
208+
"A": A}
209+
ans3 = solution_3(A)
210+
ans4 = solution_4(A)
211+
ans5 = solution_5(A)
212+
assert(ans3 == ans4 and ans4 == ans5)
213+
n = 10000
214+
# t = timeit.timeit("solution_1(A)", number=n, globals=g)
215+
# print("Time for solution 1: ", t)
216+
# t = timeit.timeit("solution_2(A)", number=n, globals=g)
217+
# print("Time for solution 2: ", t)
218+
t = timeit.timeit("solution_3(A)", number=n, globals=g)
219+
print("Time for solution 3: ", t)
220+
t = timeit.timeit("solution_4(A)", number=n, globals=g)
221+
print("Time for solution 4: ", t)
222+
t = timeit.timeit("solution_5(A)", number=n, globals=g)
223+
print("Time for solution 5: ", t)

0 commit comments

Comments
 (0)