Given a vector of maximum 10 000 natural and distinct numbers, find 4 numbers(a, b, c, d) such that a + b + c = d
I solved this problem by following a straightforward but not opti开发者_开发技巧mal algorithm. I sorted the vector in descending order and after that substracted numbers from max to min to see if I get a + b + c = d. Notice that I haven't used anywhere the fact that elements are natural, distinct and 10 000 at most. I suppose these details are the key. Does anyone here have a hint over an optimal way of solving this?
Thank you in advance!
Later Edit: My idea goes like this:
'<<quicksort in descending order>>'
for i:=0 to count { // after sorting, loop through the array
int d := v[i];
for j:=i+1 to count {
int dif1 := d - v[j];
int a := v[j];
for k:=j+1 to count {
if (v[k] > dif1)
continue;
int dif2 := dif1 - v[k];
b := v[k];
for l:=k+1 to count {
if (dif2 = v[l]) {
c := dif2;
return {a, b, c, d}
}
}
}
}
}
What do you think?(sorry for the bad indentation)
Solution in O(n2 log n):
Compute sets of all possible sums and differences:
{ai+aj: 1 <= i,j <= n}
{ai-aj: 1 <= i,j <= n}
(store them in a balanced binary search tree) and check if they have a common element. If yes, there are i,j,k,l such that ai + aj = ak - al, that is ai+aj+al=ak.
Solution in O(an log an), where an is the largest number in the vector:
Compute the polynomial
(xa1+xa2 + ... + xan)3
you can do it in O(an log an) using Fast Fourier Transform (first compute square, then third power; see here for description). Observe that after multiplication a coefficient xbi was formed from multiplication xai * xaj * xak= xai+aj+ak for some i,j,k. Check if there is a power xal in the resulting polynomial.
Unfortunately this allows some i,j,k to be used twice. Subtracting 3(x2a1+...+x2an)*(xa1+...+xan) - 2(x3a1+...+x3an) will remove those xai+aj+ak.
There is an algorithm by Shamir and Schroeppel that solves this problem in time O(N^2) and with memory O(N), when N is the number of inputs. It basically is what sdcvvc proposes, but instead of storing the sets {ai + aj} as a whole one would repeatedly compute only the sums in appropriate intervals. This saves memory, but does not increase the time complexity.
Richard Schroeppel, Adi Shamir: "A T=O(2^(n/2)), S=O(2^(n/4)) Algorithm for Certain NP-Complete Problems". SIAM J. Comput. 10(3): 456-464 (1981)
Here's @MicSim's comment to @sdcvvc's answer implemented in Python:
def abcd(nums):
sums = dict((a+b, (a,b)) for a, b in combinations(nums, 2))
for c, d in combinations(sorted(nums), 2): # c < d
if (d-c) in sums:
a, b = sums[d-c]
assert (a+b+c) == d
if a == c or b == c: continue # all a,b,c,d must be different
a,b,c = sorted((a,b,c))
assert a < b < c < d
return a,b,c,d
Where combinations()
could be itertools.combinations()
or
def combinations(arr, r):
assert r == 2 # generate all unordered pairs
for i, v in enumerate(arr):
for j in xrange(i+1, len(arr)):
yield v, arr[j]
It is O(N2) in time and space.
Example:
>>> abcd(range(1, 10000))
(1, 2, 3, 6)
精彩评论