tags:

views:

235

answers:

3

Hi,

I solved this problem by following a straightforward but not optimal 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)

+7  A: 

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.

sdcvvc
I was about to post your BST solution, but with hashtable instead. If `a < b < c < d` is a restriction, then you have to do something extra, because right now you're allowing some numbers to appear twice.
polygenelubricants
You can tag the elements in the two trees with indices of elements: {(a_i+a_j, i, j): 1 <= i,j <= n} and {(a_i-a_j, i, j): 1 <= i,j <= n}; when joining the lists, check if the tags are all different.
sdcvvc
If using a hashtable instead of the tree, you could get rid of O(log n) by using the sum/difference as key. After having computed and inserted all (a+b)-sums, you would just have to check for the differences if there is an element -(d-c) in the (a+b) table. That gives a total runtime of O(n^2).
MicSim
Assuming you can create a bit array with 2*a_n elements initialized to zero. If not, this solution is O(a_n + n^2).
sdcvvc
If you use tuples such as (a+b,a,b) for your sums and (b-a,a,b) for your differences, where a < b, in the hashtable as the keys, then you have enforced the `a < b < c < d` restriction.
Justin Peel
This is most likely a 3SUM-Hard problem. In other words, don't look for better than O(N^2) solutions :-)
Moron
@MicSim: your algorithm in Python: http://stackoverflow.com/questions/2973418/given-a-vector-of-maximum-10-000-natural-and-distinct-numbers-find-4-numbersa/2979763#2979763
J.F. Sebastian
+4  A: 

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)

abc
related "A 2010 Algorithm for the Knapsack Problem" http://rjlipton.wordpress.com/2010/02/05/a-2010-algorithm-for-the-knapsack-problem/
J.F. Sebastian
+1  A: 

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)
J.F. Sebastian
Prove it :) :) :)
Hamish Grubijan
@Hamish Grubijan: `combinations()` produces `N*(N-1)/2` pairs therefore `sums` takes O(N**2) memory and O(N**2) time to create it, and there are O(N**2) (c,d) pairs to process. `(d-c) in sums` is assumed to be O(1) therefore the whole (c,d)-loop is O(N**2) in time.
J.F. Sebastian