views:

1292

answers:

9

Using recursion, find an index that cuts an array in two parts so that both parts have equal sum.

Cut means to cut like with a knife. All the cells with index <= to the result must be equal in their sum to the all the cells with index > to the result. No cells can be left off or be part of both sides.

The arrays contains arbitrary integers (i.e. positives, negatives, and zeros).

If there is no such index return -1.

You are not allowed to allocate heap objects.

You must do it in a single pass.

You must do it with recursion (i.e. cannot use loop constructs).

Can be in any language or pseudocode.

Forgot to add this: You cannot modify the array

+2  A: 

Iterating with recursion is a trivial transformation, so we'll assume you know how to do that.

If you use your "one pass" to build your own array of "sum to this index", and can make another pass on that array, I could see how to do it. Just iterate through that second array and subtract sum[x] from sum[last]. If you ever find a situation where the result = sum[x] you return x. If you don't then return -1.

As Neil N mentioned, if you define "pass" very loosely for recursion, such that the entire recursion can actually visit indices multiple times, then you could dispense with the second array.


After thinking about this a bit, I suspect the idea is to get you to only visit every array element once (in order), and to use recursion's built-in stack property to get rid of the need for any second array.

What you do is write your recursive routine to save off it's current index's array value in a local, add that value to a passed in "sum_of_array" value, then call itself on the next highest index (if there is one). If there isn't a next highest index, it saves the sum into a global, which is now available to every stacked recursive call. Each routine finishes by checking its sum against the global sum. If it is half, then it returns its index. Otherwise it returns -1. If a non -1 was returned from a call to itself, this last step is skipped and that value is returned. I'll show in pseudo-Ada

Total_Sum : integer;

function Split (Subject : Integer_Array; After : Integer := 0; Running_Sum : Integer := 0) is
begin
   Running_Sum := Running_Sum + Subject(After);
   if (After < Subject'last) then --'// comment Hack for SO colorizer
      Magic_Index : constant Integer := Split (Subject, After +  1, Running_Sum);
      if (Magic_Index = -1) then
         if (Total_Sum - Running_Sum = Running_Sum) then
            return After;
         else
            return -1;
         end if;
      else
         return Magic_Index;
      end if;
   else
      Total_Sum := Running_Sum;
      return -1;
   end if;
end Split;

This code should have the properties that:

  • Calling it with just an array will return the described "split" index, or -1 if there isn't one.
  • It only reads from any element in the source array once
  • It reads the source array elements in strict index order.
  • No extra structured data storage (array) is required.
T.E.D.
A: 
public static Int32 SplitIndex(Int32[] array, Int32 left, Int32 right, Int32 leftsum, Int32 rightsum)
{
    if (left == right - 1)
    {
        return (leftsum == rightsum) ? left : -1;
    }

    if (leftsum > rightsum)
    {
        return SplitIndex(array, left, right - 1, leftsum, rightsum + array[right - 1]);
    }
    else
    {
        return SplitIndex(array, left + 1, right, leftsum + array[left + 1], rightsum);
    }
}

The method is called as follows.

Int32[] a = { 1, 2, 3, 1, 6, 1 };

Console.WriteLine(SplitIndex(a, -1, a.Length, 0, 0));

This can be reduced to use only a single sum and targeting zero.

public static Int32 SplitIndex(Int32[] array, Int32 left, Int32 right, Int32 sum)
{
    if (left == right - 1)
    {
        return (sum == 0) ? left : -1;
    }

    if (sum > 0)
    {
        return SplitIndex(array, left, right - 1, sum - array[right - 1]);
    }
    else
    {
        return SplitIndex(array, left + 1, right, sum + array[left + 1]);
    }
}

The method is now called as follows.

Int32[] a = { 1, 2, 3, 1, 6, 1 };

Console.WriteLine(SplitIndex(a, -1, a.Length, 0));
Daniel Brückner
Does this work with negative values in the array? I ask because I ruled out anything conditional on a comparison of sums, on that basis.
Steve Jessop
You are right - it fails on negative numbers. I am going to look at it again.
Daniel Brückner
doesn't work for this input {1,-100,1,1,-100,3}, but the array can't be cut in {1,-100,1,1} and {-100, 3} and the sum in both cases is -97
flybywire
A: 

Take a look at the following, using only 1 index, assume array's indexes are 1-based:

int recursion(index, rightvalue, leftvalue, array)
{
if array=[] then
{
    if rightvalue=leftvalue then return index
    else return -1
}
else
{
    if rightvalue <= leftvalue
    { recursion(index+1, rightvalue+array[1], leftvalue, array[2..len(array)] }
    else 
    { recursion(index, rightvalue, leftvalue+array[len(array)], array[1..len(array)-1] }
}

int main_function(array)
{
    return recursion(1, 0, 0, array)
}
Roee Adler
+4  A: 

Here's a way to do it that takes advantage of Ruby's ability to return multiple values. The first value is the index for the split (if it exists), the second is the sum of each half (or the sum of the whole array if no split is found):

def split(arr, index = 0, sum = 0)
  return -1, arr[index] if index == arr.length - 1
  sum = sum + arr[index]
  i, tail = split(arr, index + 1, sum)

  if i > -1
    return i, tail
  elsif sum == tail
    return index, sum 
  end
  return -1, arr[index] + tail
end

Calling it like this:

p split([1, 1, 2])
p split([1])
p split([-1, 2, 1])
p split([2, 3, 4])
p split([0, 5, 4, -9])

Results in this:

[1, 2]
[-1, 1]
[1, 1]
[-1, 9]
[0, 0]

EDIT:


Here's a slightly modified version to address onebyone.livejournal.com's comments. Now each index in the array is accessed only once:

def split(arr, index = 0, sum = 0)
  curr = arr[index]
  return -1, curr if index == arr.length - 1
  sum = sum + curr
  i, tail = split(arr, index + 1, sum)

  if i > -1
    return i, tail
  elsif sum == tail
    return index, sum 
  end
  return -1, curr + tail
end
Pesto
works! and be proud of spreading the gospel of ruby, I installed it just to test your answer. (will I ever use it again?)
flybywire
While it is quite trival to do with multiple return values, I wonder if and how it can be done using only a single integer as return value. +1
Daniel Brückner
I think my answer's the same as this. I think technically you should store arr[index] into a local variable in order to satisfy the requirement "each node can only be read once": as it stands this does not complete in a single forward pass, since it reads the end of the array at the deepest part of the recursion, then reads it again backwards on the way back out. In a purely functional language of course this doesn't matter, but the algorithm can't operate on a stream (which is the only practical reason for requiring a single pass in the first place).
Steve Jessop
@Daniel +1 I asked myself the same question. I don't see an answer short of "encoding" two integers into one. Perhaps you want to ask another question in SO? If so please put a link here.
flybywire
@onebyone there is something of truth in what you say. I picked this answer because it is in code (as opposed to pseudo code) and could be easily tested).
flybywire
Once we had the following question here on SO. http://stackoverflow.com/questions/731832/interview-question-ffn-n/731857#731857 I could imagine that a similar trick could help.
Daniel Brückner
Within the terms of the question, you can use outvalues provided that your language allows you to allocate them on the stack and pass them by pointer/reference. Initialise the outvalue to -1, and write the index to it if you find a solution. So C, C++, perl are all good. Java you're out of luck.
Steve Jessop
@andy: I'm not saying the answer is wrong - it's a trivial change to make it obey the rules to the letter, which doesn't affect the "shape" of the solution. In fact, in a compiled language where the compiler knows the array can't be modified, it could even automatically make that change for you. But I wouldn't say no to you voting me up too and leaving this as the accepted answer ;-)
Steve Jessop
@onebyone: A fair point. I've edited the answer to reflect this.
Pesto
+1  A: 

Pseudo-code

def cut(array, leftsum, depth)
    if array.isempty return 0
    value = array.shift // or whatever pointer manipulation you like
    leftsum += value
    rightsum = cut(array, leftsum, depth+1);
    if (leftsum == rightsum) throw(depth + 1)
    return rightsum + value

def find_cut
    try
        if cut(array, 0, 0) == 0
            result = 0
        else
            result = -1
    catch e
        result = e
    return result

Obviously the "throw" stuff can be done with with a magic return value that's checked on each call and propagated upwards, or by returning a pair, or whatever is appropriate for the language.

Note that I don't consider "array.shift" to count as modifying the array, because the purpose of removing the value is just to "prove" that only a single pass is required: if I remove each value as I read it, then I can hardly read it twice. In C you would just advance a pointer forward over the array, and the "shift" operation would be *(p++).

Steve Jessop
+1 .
flybywire
A: 

My version:

# Returns either (right sum from the currentIndex, currentIndex, False),
# or, if the winning cut is found, (sum from the cut, its index, True)
def tryCut(anArray, currentIndex, currentLeftSum):
   if currentIndex == len(anArray):
      return (0, currentIndex, currentLeftSum==0)

   (nextRightSum, anIndex, isItTheWinner) = tryCut(anArray, currentIndex + 1, currentLeftSum + anArray[currentIndex])

   if isItTheWinner: return (nextRightSum, anIndex, isItTheWinner)
   rightSum = anArray[currentIndex] + nextRightSum
   return (rightSum, currentIndex, currentLeftSum == rightSum)

def findCut(anArray):
   (dummy, anIndex, isItTheWinner) = tryCut(anArray, 0, 0)
   if isItTheWinner: return anIndex
   return -1

Note: if the index returned is 5, I mean that sum(anArray[:5]) == sum(anArray[5:]). The "extremes" are also valid (where the sum of an empty slice is meant to be zero), i.e. if the sum of the whole array is zero, then 0 and len(anArray) are also valid cuts.

Federico Ramponi
A: 

Code in C/C++/Java:

function cut(int i, int j, int s1, int s2, int a[])
{
    if(i==j && s1==s2)
        return i;
    else if(i==j && s1!=s2)
        return -1;
    else if(s1>s2)
        return cut(i, j-1, s1, s2 + a[j-1]);
    else
        return cut(i+1, j, s1 + a[i+1], s2);
}

Call using the following syntax:

cut(0, array.length, 0, 0, array);
Niyaz
for me this is returning -1 with arrays it shouldn't be... though i am porting this into java. anything i'm getting wrong? i'm replacing function with 'public static int' then adding ,a to the recursive calls.
Victor
You can fix the code by replacing s1 + a[i+1] by s1+a[i], then it works for some cases. However, there are pathological cases where it breaks, like [3, -3, -3, 3]. This array has a split at 1, but the code will return -1.
Torsten Marek
A: 

Here's an implementation in Erlang, since I'm learning it and this seemed like an interesting challenge. Idea shamelessly cribbed from Pesto's solution.

find_split(List) -> {Idx, _Sum} = find_split(List, 1, 0), Idx.
find_split([Head], _Idx, _Sum) -> {-1, Head};
find_split([Head|Tail], Idx, Sum) ->
  case find_split(Tail, Idx + 1, Sum + Head) of
    {-1, Tailsum} when Sum + Head == Tailsum -> {Idx, Sum + Head};
    {-1, Tailsum} -> {-1, Head + Tailsum};
    Ret -> Ret
  end.
Nick Johnson
A: 

Haskell:

split' _ s [] = (-1, s)
split' idx s (x:xs) | sidx >= 0   = (sidx, s')
                    | s * 2 == s' = (idx - 1, s)
                    | otherwise   = (-1, s')
    where (sidx, s') = split' (idx + 1) (x + s) xs

split = fst . split' 0 0

Your rules are somewhat misleading. You require that no objects are to be allocated on the heap, but IMHO there is no solution where the algorithm does not have space requirements of O(n), i.e. the stack grows linearly with the length of the list and tail calls are not possible because the function has to inspect the return values from the recursive call.

Torsten Marek