2

I have taken this example test before I'll try to take the real one for a job interview. The challenge is to implement the Equilibrium Index problem correctly.
I have coded some sort of solution that works only for the simple example and some of the edge cases.
Here's the code:

typedef vector<int> container_t;
typedef container_t::const_iterator iterator_t;

int find_equilibrium(const container_t &A);

int equi (const container_t &A)
{
    const std::size_t length = A.size();
    if (length  == 0)
        return -1;

    if (length == 1 && A[0] == 0)
        return -1;

    if (length == 1 && A[0] != 0)
        return 0;

    return find_equilibrium(A);
}

int find_equilibrium(const container_t &A)
{
    unsigned int i = 0;
    int sum = 0;

    for (iterator_t iter = A.begin(); iter != A.end(); ++iter, ++i)
    {
        sum += *iter; // not using std::accumulate to avoid N^2

        if (sum == 0)
            return i + 1;
    }

    return -1;
}

It is however fatally flawed for cases like [2, -2]. It does however for some reason avoid arithmetic_overflow exception. After Googling, I found the algorithm to be different then my own:

#include <numeric>
typedef vector<int> container_t;
typedef container_t::const_iterator iterator_t;

int find_equilibrium(const container_t &A);

int equi (const container_t &A)
{
    const std::size_t length = A.size();
    if (length  == 0)
        return -1;

    if (length == 1)
        return 0;

    return find_equilibrium(A);
}

int find_equilibrium(const container_t &A)
{
  unsigned int i = 0;
  int left = 0, right = std::accumulate(A.begin(), A.end(), 0);

  for (iterator_t iter = A.begin(); iter != A.end(); ++iter, ++i)
  {
    right -= *iter;

    if (left == right)
      return i;

    left += *iter;
  }

  return -1;
}

This code fails with extremely large numbers but works otherwise. I have no idea why though.

My questions are:

  • What's wrong with the way I was approaching this and how can I approach a different question of this type more correctly within a time limit of 30 minutes?
  • What really are all the corner cases for this problem exactly?
  • How can I score 100 for this example test?
the_drow
  • 18,571
  • 25
  • 126
  • 193
  • 1
    The Equilibrium Index is based on finding the location where the sum of the left portion equals the sum of the right portion. Your solution simply looks for the location where the sum of the left portion is zero and ignores the right portion completely. – sarnold Feb 01 '11 at 09:52
  • @sarnold: However if A[0] + A[1] + A[2] = A[3] + A[4] + A[5] than A[0] + A[1] + A[2] - (A[3] + A[4] + A[5]) = 0 right? So my logic seems valid, right? – the_drow Feb 01 '11 at 09:55
  • 1
    @the_drow: That equation is valid, but that's not what you're calculating! (Also you're forgetting that the element *at* the equilibrium index is to be ignored under all circumstances, but that's a minor oversight by comparison.) – j_random_hacker Feb 01 '11 at 10:03
  • 1
    @the_drow, consider an easier input array: `[1 1 1]`. Your code won't ever find a `sum == 0` with this array as no values are negative, but the answer is trivial to spot with the naked eye. (Like so many other neat programming questions..) – sarnold Feb 01 '11 at 10:11
  • @j_random_hacker: Could you explain the calculation provided by the working example and the difference between it and my non-working example? – the_drow Feb 01 '11 at 10:13
  • BTW the solution you found by googling doesn't actually need the two `if` statements in `equi()`, since its `find_equilibrium()` will do the right thing in these cases. They can be deleted. The *only* special case in this problem is that you get to the end of the vector without finding any equilibrium index, in which case you return -1 -- an empty vector just means you "get to the end" right away. My 18-line solution got 94%, failing the same "large numbers" case which I think is slightly unfair -- the only way round that is to store sums in a wider type. – j_random_hacker Feb 01 '11 at 10:15
  • @the_drow: I think the easiest way to show that your answer is computing something different than the equation you gave is this: There's a minus sign in the 2nd equation you gave; where is the minus sign in your code? – j_random_hacker Feb 01 '11 at 10:17
  • @sarnold: Why [1, 1, 1] is valid? – the_drow Feb 01 '11 at 10:17
  • @j_random_hacker: And I tried long and yet it still fails. – the_drow Feb 01 '11 at 10:18
  • @the_drow: "Why [1, 1, 1] is valid?" What do you mean??? That's the *input* s/he's proposing! *Any* vector is a valid input vector! – j_random_hacker Feb 01 '11 at 10:23
  • @the_drow: When you say you tried long and it "still fails", do you mean your code or the googled code? Your current code won't work regardless; the googled code will only work with `long` if its wider than `int`, which it usually isn't, except on some 64-bit systems. (Most compilers support `long long`, which I believe is non-standard, but likely to be 64 bits if `int` is 32 bits.) But don't worry about that yet, that's a minor issue really. – j_random_hacker Feb 01 '11 at 10:24
  • @j_random_hacker: the googled code. mine for some reason passes that test. – the_drow Feb 01 '11 at 10:26
  • @j_random_hacker: I'll rephrase, Why [1, 1, 1] is an input that doesn't return -1? Or am I missing something? – the_drow Feb 01 '11 at 10:27
  • 2
    @the_drow: I chose `[1 1 1]` because index `1` (the middle `1`) leaves left-side `1`, right-side `1` -- both left and right sides are equal. When testing code, I find it _very_ useful to feed trivially easy inputs to make sure I can hand-check results. Perhaps `[2 0 2]` would be a better input to describe: index `1` (the element `0`), left-side is `2`, right-side is `2`, they are equal, and `1` is thus the EQ Index. – sarnold Feb 01 '11 at 10:29

1 Answers1

5

Your logic it's right.

A[0] + A[1] + A[2] = A[3] + A[4] + A[5] is equivalent to A[0] + A[1] + A[2] - (A[3] + A[4] + A[5]) = 0

However, in your code, in the loop you add the values all the time, you never change the sign for those in the other half.

for (iterator_t iter = A.begin(); iter != A.end(); ++iter, ++i) {
  sum += *iter; // only increasing, where do you subtract the values of the second half?
  if (sum == 0)
    return i + 1; // from the initial value you return the next index that gives you zero sum
}
adn
  • 897
  • 3
  • 22
  • 49
  • 1
    @the_drow: By "Your logic it's right" adn means the logic in the 2nd equation you gave in your 1st comment. (And BTW it would be hard to turn that equation into *efficient* code, because you don't know when to "start subtracting" and would need to try all possibilities -- doable but there are more efficient ways.) – j_random_hacker Feb 01 '11 at 10:34
  • @j_random_hacker: So one of my mistakes is that I was over-optimizing and was naive about the results? Hmm thanks. – the_drow Feb 01 '11 at 10:56
  • @the_drow: I don't really know how to answer that sorry. You are basically calculating something *totally different* from what the question is asking you to calculate. Perhaps because you're trying to overoptimise and got it wrong -- I don't know. Please look at sarnold's latest comment on your question and explain (there) if it hasn't clicked yet. – j_random_hacker Feb 01 '11 at 11:39