This is a continued discussion from (4 sum challenge) by return count only.
Problem
Given four lists A, B, C, D of integer values, compute how many tuples (i, j, k, l) there are such that A[i] + B[j] + C[k] + D[l] is zero.
To make problem a bit easier, all A, B, C, D have same length of N where \0ドル \le N \le 500\$. All integers are in the range of \$-2^{28}\$ to \2ドル^{28} - 1\$ and the result is guaranteed to be at most \2ドル^{31} - 1\$.
Example:
Input:
A = [ 1, 2] B = [-2,-1] C = [-1, 2] D = [ 0, 2]
Output:
2
Explanation:
The two tuples are:
- (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
- (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
I'm wondering if there are any ideas to have a solution less than \$O(n^2)\$ time complexity.
Source code in Python 2.7,
from collections import defaultdict
def four_sum(A, B, C, D):
sum_map = defaultdict(int)
result = 0
for i in A:
for j in B:
sum_map[i+j] += 1
for i in C:
for j in D:
if -(i+j) in sum_map:
result += sum_map[-(i+j)]
return result
if __name__ == "__main__":
A = [1, 2]
B = [-2, -1]
C = [-1, 2]
D = [0, 2]
print four_sum(A,B,C,D)
2 Answers 2
Proof that it cannot be done (at least based on our current understanding) in much better than \$O(n^2)\$:
Suppose A = B = C and D is a list of zeros. Then the problem reduces to finding three numbers in A that sum to zero. This is the famous 3SUM problem, for which we do not have a "much" better general solution than \$O(n^2)\$.
-
\$\begingroup\$ Thanks Raziman, love your prove and vote up. But I am not sure you prove for a special case or general case? Or your general idea is the specific 4 sum problem cannot be faster than 3 sum problem -- where the best of 3 sum problem time complexity is
O(n^2)
? \$\endgroup\$Lin Ma– Lin Ma2017年01月24日 08:07:31 +00:00Commented Jan 24, 2017 at 8:07 -
2\$\begingroup\$ If the general solution to the 4-sum was faster, you could reduce 3-sum to 4-sum as I did here. Since 3-sum has no known "much" faster solution, that proves that 4-sum cannot be faster as well. \$\endgroup\$Raziman T V– Raziman T V2017年01月24日 09:17:38 +00:00Commented Jan 24, 2017 at 9:17
I’m not sure I can get it any better than \$O(n^2)\,ドル but I would point you towards the itertools module as a way to tidy up your code by reducing the number of nested for
loops. Like so:
def four_sum(A, B, C, D):
sums = defaultdict(int)
result = 0
for (a, b) in itertools.product(A, B):
sums[a + b] += 1
for (c, d) in itertools.product(C, D):
result += sums[-(c + d)]
return result
I’ve also tweaked the names of some of the variables to make the code easier to follow, and changed it so it only has to compute (c+d)
once.
-
1\$\begingroup\$
result += sums[-c-d]
[will add the default]0 if s not in sum
, should be faster for one less lookup insums
, two lines shorter and more symmetrical (oops - edited in). (Negating C & D en bloc before iterating their sums might save time, but look less convincing.) \$\endgroup\$greybeard– greybeard2017年01月22日 11:03:50 +00:00Commented Jan 22, 2017 at 11:03 -
2\$\begingroup\$ Hehe, apparently not. Now you wrote
sum
instead ofsums
. Incidentally, the secondfor
loop can be done asresult = sum(sums[-(c + d)] for (c, d) in itertools.product(C, D)
now. \$\endgroup\$Graipher– Graipher2017年01月22日 11:04:35 +00:00Commented Jan 22, 2017 at 11:04 -
\$\begingroup\$ Thanks alexwlchan, love your code and comments, and vote up. Wondering why do you think we cannot make it better than
O(n^2)
? Do you have some rough prove for lower bound to beO(n^2)
? \$\endgroup\$Lin Ma– Lin Ma2017年01月24日 08:05:39 +00:00Commented Jan 24, 2017 at 8:05
Explore related questions
See similar questions with these tags.
O(n^2)
, if I mis-read your comments, appreciate if you could show your idea by code, which is less time complexity thanO(n^2)
. :) \$\endgroup\$defaultdict
, which makes it more elegant without checking a key exists. BTW, do you have any ideas which time complexity less thanO(n^2)
? \$\endgroup\$