8

Given a very large integer array, I need to find the maximum value of a4, such that:

a4 = a1 + a2 + a3

Where the ai's are all values in the array.

How would I do this?

Note: Using 4 for loops is not the ideal solution.

Bernhard Barker
  • 54,589
  • 14
  • 104
  • 138
53by97
  • 425
  • 3
  • 17
  • Hint: Start by solving the problem for `a3 = a1 + a2` (that is, sum of two numbers in the list). How is it a generalization from just finding the max? – Benjamin Gruenbaum Apr 22 '14 at 05:32
  • 1
    I am not able to understand the question. Do you mean to find the maximum sum of any three values in an array? – anirudh Apr 22 '14 at 05:32
  • 1
    @anirudh the maximal sum of any three values in an array, such that there exist another element in the array that is equal that sum. For example, for `[1,2,3,777,999,111,665]` you get `a1 = 1, a2 = 665, a3 = 111, a4 = 777`. – Benjamin Gruenbaum Apr 22 '14 at 05:33
  • @BenjaminGruenbaum Your understanding of the question is totally correct. But I didnt get how a3 = a1 + a2 gonna help in solving the bigger one. – 53by97 Apr 22 '14 at 05:36
  • 1
    @user2181169 I ask it in interviews sometimes :) Second hint http://stackoverflow.com/q/12774823/1348195 – Benjamin Gruenbaum Apr 22 '14 at 05:38
  • This problem is similar to the popular 3SUM problem, so I suggest you have a look at http://en.wikipedia.org/wiki/3SUM. It is the same as 3SUM once you figure out a4. – anirudh Apr 22 '14 at 05:41
  • @BenjaminGruenbaum Understood the generalization stuff to increase complexity gradually, but still need any insight in algorithms for the same to solve it. – 53by97 Apr 22 '14 at 05:42
  • Given the solution to 3SUM, you should trivially be able to solve the problem in O(n^3). – Bernhard Barker Apr 22 '14 at 05:49
  • Unless I'm misunderstanding the problem, this is a far easier problem than 3SUM. The maximal value of a4 will be found by taking the sum of the 3 largest elements in the collection. Finding the largest 'm' objects in a collection of size n can be done in O(n log(m)). – Aurand Apr 22 '14 at 05:57
  • @Aurand yes you've misunderstood it. _'maximum'_ makes it confuse. Actually want to find the maximum sum of any three element, where the sum value also exist in that array – Baby Apr 22 '14 at 06:02
  • @Aurand The question is that a4 should also be in the same array, and we have to find the max possible value of a4. – 53by97 Apr 22 '14 at 06:02
  • I have an idea but dont know whether it is feasibe/efficient. First, we can sort the input array in descending order by O(nlogn) by quick sort & etc. Then we go on picking the larger valued element from the start of sorted array as a4 and then the next smaller to it as a3 and for a2, a1 we will start from the back-side, first picking a1 as the smallest one and keeping it fixed and go on picking bigger values for a2 while sum of (a1 + a2) is less than (a4 - a3) { if sum is equal we can output a4 as the result } else then change a3 to its next smaller array value; Please help. – 53by97 Apr 22 '14 at 06:38

3 Answers3

12

There is a simple (expected) O(n^2) solution:

  1. Iterate through all pairs of array elements (a, b) and store their sum in a hash table.
  2. Iterate through all candidate pairs (a4, a1) and check whether a4 - a1 is in the table. The maximum over all valid a4 is the solution. Of course you should process a4 from largest to smallest, but that doesn't affect the asymptotics.

If you want to avoid using an element more than once, you need some additional information stored in the hash table so that you can filter out pairs that colide with a1 or a4 fast.

If the integers in the array are bounded (max - min <= C), it might be useful to know that you can achieve O(n + C log C) using a discrete fourier transform (solvable using FFT).

Niklas B.
  • 92,950
  • 18
  • 194
  • 224
  • Can you please describe how to "filter out pairs that collide with a1 or a4 fast"? If there is no such method then the algorithm will perform badly than O(n^2) when there are many entries in the hashtable that collide with a1 or a4. For example consider the array {0, 1, 0, 1, 0, 1, 0, 1, ...}. There are many entries where a1-a4 = a1 + a4. So how to filter out those solutions very quickly in constant time? – Nikunj Banka Apr 29 '14 at 03:26
  • @NikunjBanka Let's say in the hash table from the first step we maintain h[x], a list of index pairs (i, j) i k incrementally. That way the hash table only every contains pairs that don't collide with k. To prevent collisions with l as well, we could keep the lists in the hash table sorted and use binary search... – Niklas B. Apr 29 '14 at 03:56
  • ... admittedly that adds a log n factor. Instead of keeping the lists sorted, we could use secondary, nested hash tables to find the collisions with l. That would give us overall O(n^2) time. It's complicated but uninteresting, requires lots of case work and destroys the clear separation between the two phases, so I didn't include it in the answer – Niklas B. Apr 29 '14 at 03:58
2

First of all you should ascending sort your array. then start from the last (biggest) member of the array. For example, for [1,2,3,777,999,111,665] you'll have sortedArray = {1,2,3,111,665, 777, 999} then select 999 as a4 and try to create it with other members. So you should select as a3 and try to create (999 - 777) = 222 as a1+a2 since your array is sorted you only need to consider subarray {1,2,3,111}. if there is no pair satisfying this condition, try next biggest member (777) and retry above scenario to find the solution

fasadat
  • 1,025
  • 1
  • 12
  • 27
0

Based on @Niklas answer, I wrote the following program in Java.

public static int sumOfThree(int [] arr) {
    int arrlen = arr.length;
    int arrijv [][] = new int [arrlen * (arrlen - 1) / 2][3];
    int arrijvlen = 0;

    quickSortInDescendingOrder(arr, 0, arrlen - 1); // sorts array into descending order

    System.out.println(Arrays.toString(arr));

    // populates array with sum of all pair values
    for (int i = 0; i < arrlen - 1; i++) {   
        for (int j = i + 1; j < arrlen; j++) {
            //  if ((arr[i] + arr[j]) < arr[0]) {       // can be added if no negative values
            arrijv[arrijvlen][0] = i;
            arrijv[arrijvlen][1] = j;
            arrijv[arrijvlen][2] = arr[i] + arr[j];
            arrijvlen++;
            //  }
        }
    }

    System.out.print('[');
    for (int i = 0; i < arrijvlen; i++) {
        System.out.print(arrijv[i][2] + " ");
    }
    System.out.print("]\n");

    // checks for a match of difference of other pair in the populated array 
    for (int i = 0; i < arrlen - 1; i++) {
        for (int j = i + 1; j < arrlen; j++) {
            int couldBeA4 = arr[i];
            if(isAvailable(arrijv, arrijvlen, couldBeA4 - arr[j], i, j)){
                System.out.println(" i3-" + j + " i4-" + i);
                return couldBeA4;
            }
        }
    }

    return -1;
}

private static boolean isAvailable(int[][] arrijv, int len, int diff, int i, int j) {
    boolean output = false;

    // returns true if the difference is matched with other combination of i,j
    for (int k = 0; k < len; k++) {
        if (arrijv[k][2] == diff) {
            int pi = arrijv[k][0];
            int pj = arrijv[k][1];

            if (pi != i && pi != j && pj != i && pj != j) {
                System.out.print("i1-" +  pj + " i2-" + pi);
                output = true;
                break;
            }
        }
    }
    return output;
}


private static void quickSortInDescendingOrder(int[] array, int low, int high) { // solely used for sorting input array into descending array
    if (low < high) {
        int partition = getPartitionIndex(array, low, high);
        quickSortInDescendingOrder(array, low, partition);
        quickSortInDescendingOrder(array, partition + 1, high);
    }
}

private static int getPartitionIndex(int[] arr, int lo, int hi) { 
    int pivot = arr[(lo + hi) / 2]; 

    while (true) {
        while (arr[lo] > pivot) {
            lo++;
        }

        while (arr[hi] < pivot) {
            hi--;
        }

        if (arr[lo] == arr[hi]) {   // can be removed if no duplicate values
            return lo;
        } else if (lo < hi) {
            int temp = arr[lo];
            arr[lo] = arr[hi];
            arr[hi] = temp;
        } else {
            return hi;
        }
    }
}

Please verify that it works and suggest for further improvements.

Karl-Henrik
  • 1,113
  • 1
  • 11
  • 17
53by97
  • 425
  • 3
  • 17
  • Sample input & output- If sum of three = fourth one exists, output will be like- i/p- [1, 1, 3, -1, 8, 6, 14] [14, 6, 3, 1, 8, 1, -1] <--sorted in descending order [20 17 15 22 15 13 9 7 14 7 5 4 11 4 2 9 2 0 9 7 0 ] <--all possible values for sum of two array elements [i1-3 i2-1 i3-5 i4-4] <-- ith location for all ai's element in sorted array 8 <-- maximum value sum if not- i/p- [1, 1, 3, -1, 8, 18, 15] [18, 15, 8, 3, 1, 1, -1] [33 26 21 19 19 17 23 18 16 16 14 11 9 9 7 4 4 2 2 0 0 ] -1 <-- not found – 53by97 Apr 23 '14 at 19:36
  • It seems like you should be able to use the builtin sorting in Arrays, (with a custom comparator, if you want descending order). – Teepeemm Apr 23 '14 at 19:42
  • @Teepeemm I already approached in that perspective, but what I got to know is we can only use Objects with Comparator and not primitives. So for this problem, which heavily consumes memory if size of input array is large, creating Integers array for the same will eat up all memory I guess. Also, auto-boxing & auto-unboxing will cause some delay. Correct me if I am wrong else suggest other alternatives. – 53by97 Apr 24 '14 at 02:23
  • I guess my complaint is that a good portion of your answer is given over to `quicksort`, which is not really related to the problem at hand. But I agree that autoboxing would cause undesired overhead. I think it would clarify things to use `Arrays.sort(int[])`, and modify your code to account for the increasing array. – Teepeemm Apr 24 '14 at 16:20
  • @Teepeemm Yeah thats true! The reverse implementation can be done but then the understanding of answer will become difficult. – 53by97 Apr 25 '14 at 08:49