(Original problem description)
Pair of points
You are given the following
An integer N
A 2D array of length N denoting the points in the 2D coordinate system, that is (x, y)
Determine the number of unordered pairs (i, j) or (j, i) and i != j such that
The straight line connecting the points (A[i], A[i]) and (A[j], A[j]) passes through (0, 0)
(Context, this was a coding problem on hacker earth site and I did solve it (bruteforce) method)
def find_pairs(array, size): li =  for i in range(size): for j in range(size): if (i, j) not in li and (j, i) not in li: if ((array[i] * (array[j] - array[i])) == ((array[i] * (array[j] - array[i]))): li.append((i,j)) return len(li)
The math the code uses is, given two points (x1, y1) and (x2, y2), their line passes through the origin if they satisfy the equation (x1 * (y2 – y1)) = (y1 * (x2 – x1))
This code passed half the test cases (which were testing for correct answer) but failed the remaining which had time constraint. I tried to use itertools.combinations but it exceeded the memory limit
Is there any way to write a program with less than N2 time complexity?
itertools.combinations exceeded your memory limit then you used it incorrectly. The functions in the
itertools library are generally quite memory efficient because they work with iterators rather than creating complete lists in memory.
num_pairs = 0 for i, j in itertools.combinations(array, 2): if i_j_are_valid_points(): # your formula here num_pairs += 2 # (i, j) and (j, i) are both valid
Note that this is still O(N^2) – you can’t find all combinations of pairs in less time. But it will run over twice as fast as your solution since: 1) it doesn’t check both (i, j) and (j, i) redundantly; 2) it doesn’t need to traverse the list of already found solutions.
I suspect you can do this in O(NlogN) (merge sort) time, at least in theory. If you convert to polar coordinates, then you’re looking for two points (r_1, theta) and (r_2, theta + pi) so you can just sort them and then process from theta in [0, pi), and compare with those from theta in [pi, 2pi). If you use floating point numbers though, you’ll need to be careful how you compare the two values because floating point values are only stored approximately.
One optimization we can make in your code is not to check previously traversed co-ordinates:
def find_pairs(array, size): count = 0 for i in range(size - 1): for j in range(i + 1, size): if ((array[i] * (array[j] - array[i])) == ((array[i] * (array[j] - array[i]))): count += 1 return count
Put tangent (slope) of line origin-to-point into
Counter (in general – dictionary with value as counter for old Python without this class). Make separate counter for points with x=0 (vertical line).
After putting all slopes into the map, retieve number of pairs for every slope –
n points with the same slope give
from collections import Counter pts = [[1,0],[2,2],[-1,-1],[4,4],[0,2],[-2,0]] vert = 0 cntr = Counter() for p in pts: if p: cntr[p/p] += 1 else: vert += 1 res = vert * (vert - 1) // 2 for v in cntr.values(): res += v*(v-1) // 2 print(res) # 4 pairs
Update: accounts for (0,0):
from collections import Counter pts = [[1,0],[2,2],[-1,-1],[4,4],[0,2],[-2,0],[0,0],[0,0]] vert = 0 zeros = 0 cntr = Counter() for p in pts: if p: cntr[p/p] += 1 else: if p: vert += 1 else: zeros += 1 res = vert * (vert - 1) // 2 for v in cntr.values(): res += v*(v-1) // 2 res += (len(pts) - zeros) * zeros + zeros*(zeros-1)//2 print(res) //17 pairs
Potential pitfall – float number comparison might give wrong results for some pairs.
If points are integers, it is possible to use integer tuple
( sign(x)*y/gcd(y,x), abs(x)/gcd(x,y) ) as key