tags:

views:

571

answers:

8

hello, I need help to find an algorithm that finds:

  • four elements in array
  • whose sum equal to a given number X
  • in O(n^2*log(n))

prefer in pseudo-code or c,c++

thanks moti

+1  A: 

Sounds like homework to me. But here's what I'd do.

First sort the numbers (there's your n*log(n)).

Now, create pointers to the list, initialize it with the first 4 numbers. Once you have that, you check the sum of your 4 current numbers with the total. It should be less than or equal to your search sum (if it's not, you can quit early). Now all you need to do is traverse rest of your list alternately replacing your pointers with the current value in the list. This only need to happen once (or really at worst 4 times) so there's your other n, which makes n^2*log(n)

You'll need to keep track of some logic to know if you're over/under your sum and what to do next, but I leave that as your homework assignment.

miked
_traverse rest of your list alternately replacing your pointers with the current value in the list_ This part sounds extremely vague to me.
Nikita Rybak
Can you give details how you traverse the list with four pointers ? Do you let the first pointer first go to the end, then the second one etc. ?
Andre Holzner
By the way, this would be O(n) + O(n * log(n)) = O(n * log(n)), not O(n) * O(n * log(n))
Andre Holzner
A: 

I'm not going to answer your question completely, since I think it is homework and also think that this is easily done. But I do think that I know why you are having difficulty with an answer, so I will help you out a little bit.

Firstly, you should look into recursion. That means calling a function from within itself.

Second, you should use a helper function, which is called by the function you want to write. This function should take as arguments: - an array of numbers - the length of the array - the value you want find members that sum up to - the number of members of the array that you want to sum up

This function will be called by your other function and passed a 4 for the last argument. It will then call itself adjusting the arguments as it tries to find results by partial trial and error.

edit 2

Upon further thought I realized that this not O(n^2), as I claimed earlier (I mentally glossed over the the middle steps). It is limited by n^4, but may have a lower limit than that due to ample opportunity to short cut in many cases. I do not believe that this short cutting improves it to the point of n^2, though.

nategoose
And how do you prove the complexity of this is as required?
Maciej Hehl
Mission accomplished :)
Maciej Hehl
So you think it's easily done and then end up realising you don't know how to do it. Nice. -1.
IVlad
@IVLad: I could have deleted the answer and avoided a down vote, but instead I left it up with a note pointing out the flaws with it.
nategoose
Thanks all, after a couple of hours today i have reached similar answers like you.I don't need all possible 4 numbers, i need only one option.My solution has also O(n^2) place complexity.
moti
+7  A: 

You can do it in O(n^2). Works fine with duplicated and negative numbers.

edit as Andre noted in comment, time is with use of hash, which has 'worst case' (although it's less likely than winning in a lottery). But you can also replace hashtable with balanced tree (TreeMap in java) and get guaranteed stable O(n^2 * log(n)) solution.

Hashtable sums will store all possible sums of two different elements. For each sum S it returns pair of indexes i and j such that a[i] + a[j] == S and i != j. But initially it's empty, we'll populate it on the way.

for (int i = 0; i < n; ++i) {
    // 'sums' hastable holds all possible sums a[k] + a[l]
    // where k and l are both less than i

    for (int j = i + 1; j < n; ++j) {
        int current = a[i] + a[j];
        int rest = X - current;
        // Now we need to find if there're different numbers k and l
        // such that a[k] + a[l] == rest and k < i and l < i
        // but we have 'sums' hashtable prepared for that
        if (sums[rest] != null) {
            // found it
        }
    }

    // now let's put in 'sums' hashtable all possible sums
    // a[i] + a[k] where k < i
    for (int k = 0; k < i; ++k) {
        sums[a[i] + a[k]] = pair(i, k);
    }
}

Let's say, X = a[1] + a[3] + a[7] + a[10]. This sum will be found when i = 7, j = 10 and rest = a[1] + a[3] (indexes 1 and 3 will be found from hash)

Nikita Rybak
If you use a Tree Map instead of a HashTable (which can have O(n) in the worst case), you're guaranteed to have O(log n) for the lookup of the sums.You should also check that you're not using one number more than once.
Andre Holzner
@Andre I don't think we should consider worst case access time for hash, it's extremely unlikely to happen :) But if that's important, then TreeMap is a solution, yes.
Nikita Rybak
@Andre _You should also check that you're not using one number more than once_ It's already done. i and j are always equals and i < j. And hashtable contains sums for all different indexes k and l less than i.
Nikita Rybak
you're right, I didn't realize the way you're updating the sums map and that it contains only indices < i. +1 from me.
Andre Holzner
The requested running time makes me think the instructors are expecting something other than hash tables. Also, I don't think we shouldn't consider the worst case for hashes. If the numbers are big enough, how will your solution fare for all equal numbers? This works if you use trees though, so +1.
IVlad
@IVlad Actually, we need only one pair for each particular sum, so in case of all equal numbers hashtable will have only one element. It's still possible that lots of different sums will have same hash, just difficult to emulate.
Nikita Rybak
+1  A: 

Abusing the fact that no memory constrain is specified. And using the usual divide and conquer approach.

Number of all permutations for 4 number subsets is C(n,4) and is O(n^4). Number of all permutations for 2 numbers is C(n,2) and is O(n^2). O(n^2) seems to be OK for the task.

  1. Input is: an array A with n elements, X.
  2. Generate all permutations for 2 number subsets (that's O(n^2)) and put their sums into array B with n^2 elements (also remembering the subsets). Let's denote as S[B[i]] the subset (consisting of the two numbers) whose sum is B[i].
  3. Sort the B, O(n^2*log(n^2)).
  4. Walk through the array B (O(n^2)) i = [0,n^2) and quick search O(log(n^2)) = O(log(n)) in it for the value (X - B[i]). There might be several of them (but not more than n^2).

    4.1. Walk through all the elements with value of (X - B[i]), using index k.

    4.2. Skip the elements B[k] where S[B[k]] intersects with S[B[i]]. Intersection of two sets with two numbers can be calculated in O(1).

    4.3 If k is the index a element where B[i] + B[k] == X and S[B[k]] doesn't intersect with S[B[i]], then the sum of the sets S[B[k]] and S[B[i]] are the four sought numbers.

Performance is: O( n^2 + n^2*log(n^2) + n^2*log(n^2) ) = O( n^2 * log(n^2) ) = O( n^2 * log(n) )

On step four, when we iterate over the multiple matching elements of B using nested loop. Yet, total number of iterations of the two nested loops is limited by the |B| which is O(n^2). The quick search is not the usual variation but the one which finds the matching element with the lowest index. (Alternatively one can use the usual bsearch and since we might have landed in the middle, use two adjacent loops, checking elements in both directions.)

Dummy00001
`log(n^2)` is `2log(n)`, so you're not exceeding the time complexity. This will, however, not account for duplicate elements. Additional checks will be needed for that but it's possible. For example given the array `[1, 2, 3]` and the sums `[ 1+2, 1+3, 2+3 ]`, a desired sum of `9` can be found with `4 + 5`, but we are counting 3 twice then.
Anurag
@Anurag: OMG. I had to repeat the math.
Dummy00001
Can you detail the 4th step? How exactly do you deal with duplicates and over/undercounting?
IVlad
@IVlad, I have tried.
Dummy00001
+2  A: 

Like a few other posters, it can be done with a hash in O(n^2)

#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
#include <math.h>
#include <map>

using namespace std;

struct Entry
{
   int a;
   int b;
};

int main () {

   typedef vector<int> VI;

   VI l(5);
   l[0] = 1;
   l[1] = 2;
   l[2] = -1;
   l[3] = -2;
   l[4] = 5;
   l[5] = 6;

   sort(l.begin(), l.end());

   int sumTo = 0;

   typedef multimap<int, Entry> Table;

   typedef pair<int,Entry> PairEntry;

   Table myTbl;

   // N
   for (int i = 0; i < l.size(); ++i)
   {
      // N
      for (int j = i+1; j < l.size(); ++j)
      {
         // Const
         int val = l[i] + l[j];

         // A is always less than B
         Entry ent = {i, j};

         myTbl.insert(PairEntry(val,ent));
      }
   }

   pair<Table::iterator, Table::iterator> range;

   // Start at beginning of array
   for (Table::iterator ita = myTbl.begin();
        ita != myTbl.end();
        ++ita)
   {
      int lookFor = sumTo - ita->first;
      // Find the complement
      range = myTbl.equal_range(lookFor);

      // Const bound
      for (Table::iterator itb = range.first;
           itb != range.second;
           ++itb)
      {
         if (ita->second.a == itb->second.a || ita->second.b == itb->second.b)
         {
            // No match
         }
         else
         {
            // Match
            cout << l[ita->second.a] << " " << l[itb->second.a] << " "
                 << l[ita->second.b] << " " << l[itb->second.b] << endl;

            return 0;
         }
      }
   }

   return 0;
}
Sibshops
This meets the requirements, so +1, but it's not O(n^2) as you say. A C++ multimap doesn't give you constant time insertions and lookups, those are logarithmic.
IVlad
A: 

1) Create an array of all possible pair sums [O(N^2)]

2) Sort this array in ascending order [O(N^2 * Log N)]

3) Now this problem reduces to finding 2 numbers in a sorted array that sum to a given number X, in linear time. Use 2 pointers: a LOW pointer starting from the lowest value, and a HIGH pointer starting from the highest value. If the sum is too low, advance LOW. If the sum is too high, advance HIGH (backwards). Eventually they will find that pair or cross each other (this can be easily proven). This process takes linear time in the size of the array, i.e. O(N ^ 2)

This gives a total time of O(N^2 * log N) as required.

NOTE : This method can be generalized for solving the case of M numbers in O(M * N^(M/2) * log N) time.

-- EDIT --

Actually my response is very similar to Dummy00001's response, except the final lookup, where I use a faster method (though the overall complexity is the same...)

Eyal Schneider
A: 

Thanks all, after a couple of hours today i have reached similar answers like you. I don't need all possible 4 numbers, i need only one option. My solution has also O(n^2) place complexity

moti
A: 

find four elements in array whose sum equal to a given number X

for me following algorithm works:

Dim Amt(1 To 10) As Long Dim i

For i = 1 To 10 Amt(i) = Val(List1.List(i)) Next i

Dim Tot As Integer Dim lenth As Integer lenth = List1.ListCount

'sum of 4 numbers

For i = 1 To lenth

 For j = 1 To lenth
     For k = 1 To lenth
         For l = 1 To lenth
             If i <> j And i <> k And i <> l And j <> k And j <> l And k <> l Then
                  Debug.Print CBAmt(i) & " , " & CBAmt(j) & " , " & CBAmt(k) & " , " & CBAmt(l)
                  If CBAmt(i) + CBAmt(j) + CBAmt(k) + CBAmt(l) = Val(Text1) Then
                       MsgBox "Found"
                       found = True
                       Exit For
                  End If
                End If
         Next
         If found = True Then Exit For
         Next
         If found = True Then Exit For
    Next
    If found = True Then Exit For
Next
Rashid