27

I received a codility test the other day for a job, as such I've been practicing using some of the problems from their training page Link

Unfortunately, I've only been able to get 83/100 on the Tape-Equilibrium question:

A non-empty zero-indexed array A consisting of N integers is given. Array A represents numbers on a tape.
Any integer P, such that 0 < P < N, splits this tape into two non−empty parts: A\[0], A\[1], …, A\[P − 1] and A\[P], A\[P + 1], …, A\[N − 1].
The difference between the two parts is the value of: |(A\[0] + A\[1] + … + A\[P − 1]) − (A\[P] + A\[P + 1] + … + A\[N − 1])|
In other words, it is the absolute difference between the sum of the first part and the sum of the second part.

Write a function that, given a non-empty zero-indexed array A of N integers, returns the minimal difference that can be achieved.

Example: A[0] = 3 A[1] = 1 A[2] = 2 A[3] = 4 A[4] = 3
We can split this tape in four places:
P = 1, difference = |3 − 10| = 7
P = 2, difference = |4 − 9| = 5
P = 3, difference = |6 − 7| = 1
P = 4, difference = |10 − 3| = 7
In this case I would return 1 as it is the smallest difference.

N is an int, range [2..100,000]; each element of A is an int, range [−1,000..1,000]. It needs to be O(n) time complexity,

My code is as follows:

import java.math.*;
class Solution {
public int solution(int[] A) {
    
    long sumright = 0;
    long sumleft = 0;
    long ans;
    
    for (int i =1;i<A.length;i++)
        sumright += A[i];
    
    sumleft = A[0];
    ans =Math.abs(Math.abs(sumright)+Math.abs(sumleft));
    
    for (int P=1; P<A.length; P++)
    {
        if (Math.abs(Math.abs(sumleft) - Math.abs(sumright))<ans)
            ans = Math.abs(Math.abs(sumleft) - Math.abs(sumright));
        sumleft += A[P];
        sumright -=A[P];
    }
    return (int) ans;  
}

I went a bit mad with the Math.abs. The two test areas it fails on are "double" (which I think is two values, -1000 and 1000, and "small". http://codility.com/demo/results/demo9DAQ4T-2HS/

Any help would be appreciated, I want to make sure I'm not making any basic mistakes.

Nouman Ghaffar
  • 3,780
  • 1
  • 29
  • 37
CTB
  • 283
  • 1
  • 3
  • 4
  • 2
    In the second for loop you should loop until A.length-1. After that it will become 100% since you will avoid an unnecessary substraction. – brainmassage Dec 02 '15 at 15:28
  • I went to check the test, and I did not understand any of their description. Thx for mentioning `In other words, it is the absolute difference between the sum of the first part and the sum of the second part.`, that hleped me.. – choz Jun 18 '16 at 08:40
  • Slightly confused, why is the if-check done first in the loop? – CyberMew Jun 20 '21 at 13:54

21 Answers21

15

Your solution is already O(N). You need to remove the abs from sumleft and sumright.

if (Math.abs( sumleft - sumright ) < ans)
{
  ans = Math.abs( sumleft - sumright );
}

Also before the second for loop,

ans =Math.abs( sumleft - sumright );

It should work.

Abhishek Bansal
  • 12,589
  • 4
  • 31
  • 46
  • 2
    How can this be O(N)? It is looping through the array twice would it not be O(2N) – Jim Collins Sep 29 '14 at 15:45
  • 17
    @JimCollins In orders of complexity the constant factors don't have any meaning. O(N) = O(2N). – Abhishek Bansal Sep 29 '14 at 16:00
  • @JimCollins: the code is not looping the array twice. The code contains two loops and each of them dont have any inner loop. – a3.14_Infinity Jun 10 '15 at 06:41
  • The condition in the second loop should be less by 1. This is because sum right would otherwise not be possible if the array is partitioned at the end. – Eugene Jul 01 '16 at 21:19
  • the difference will always keep decreasing till we hit the minimum. Once we reach this point it can only go up. So what if we break from the loop at this point? Wont that save about half the iterations? – NSRover Jan 23 '18 at 11:14
  • @AbhishekBansal to avoid confusion, the 2 in O(2N), I think it's called a coefficient (not a constant). A constant would be O(N + 2) – govgo Oct 30 '20 at 23:54
13

100%, in Javascript

var i, ll = A.length, tot = 0, upto = 0, min = Number.MAX_INT;

for (i=0; i<ll; i++) tot += A[i];

for (i=0; i<ll-1; i++)
{
    upto += A[i];
    var a1 = upto, a2 = tot - a1, dif = Math.abs(a1 - a2);
    if (dif < min)
         min = dif;
}

return min;
azurensces
  • 141
  • 1
  • 5
7

I found perfect solution for TapeEquilibrium by Cheng on Codesays. I translated it to Java for anybody who is curious about it. Cheng's solution hit 100% on Codility

    public int solution(int[] A) {

    // write your code in Java SE 7
    int N = A.length;

    int sum1 = A[0];
    int sum2 = 0;
    int P = 1;
    for (int i = P; i < N; i++) {
        sum2 += A[i];
    }
    int diff = Math.abs(sum1 - sum2);

    for (; P < N-1; P++) {
        sum1 += A[P];
        sum2 -= A[P];

        int newDiff = Math.abs(sum1 - sum2);
        if (newDiff < diff) {
            diff = newDiff;
        }
    }
    return diff;
}
vbg
  • 613
  • 6
  • 10
4

Consider this 100/100 solution in Ruby:

# Algorithm:
#
# * Compute the sum of all elements.
# * Iterate over elements, maintain left and right weights appropriately.
# * Maintain a minimum of `(left - right).abs`.
def solution(ar)
  sum = ar.inject(:+)
  left = ar[0]
  right = sum - left
  min_diff = (right - left).abs

  1.upto(ar.size - 2) do |i|
    left += ar[i]
    right -= ar[i]
    diff = (right - left).abs
    min_diff = [min_diff, diff].min
  end

  # Result.
  min_diff
end

#--------------------------------------- Tests

def test
  sets = []
  sets << ["1", 1, [1]]
  sets << ["31", 2, [3, 1]]
  sets << ["312", 0, [3, 1, 2]]
  sets << ["[1]*4", 0, [1]*4]
  sets << ["[1]*5", 1, [1]*5]
  sets << ["sample", 1, [3, 1, 2, 4, 3]]

  sets.each do |name, expected, ar|
    out = solution(ar)
    raise "FAILURE at test #{name.inspect}: #{out.inspect} != #{expected.inspect}" if out != expected
  end

  puts "SUCCESS: All tests passed"
end
Alex Fortuna
  • 1,223
  • 12
  • 16
4

Here is my 100/100 Python solution:

def TapeEquilibrium(A):
    left = A[0]
    right = sum(A[1::])
    diff = abs(left - right)

    for p in range(1, len(A)):
        ldiff = abs(left - right)
        if ldiff < diff:
            diff = ldiff
        left += A[p]
        right -= A[p]

    return diff
hgf
  • 575
  • 3
  • 14
3

Some C# for ya.

using System;
// you can also use other imports, for example:
// using System.Collections.Generic;
class Solution 
{
    public int solution(int[] A) 
    {
        // write your code in C# with .NET 2.0
        int sumRight = 0;
        for(int i=0; i<A.Length; i++)
        {
            sumRight += A[i];
        }

        int sumLeft = 0;
        int min = int.MaxValue;
        for(int P=1; P<A.Length; P++)
        {
            int currentP = A[P-1];
            sumLeft += currentP;
            sumRight -= currentP;

            int diff = Math.Abs(sumLeft - sumRight);
            if(diff < min)
            {
                min = diff;
            }
        }
        return min;
    }
}
JPK
  • 683
  • 6
  • 7
  • The condition in the second loop should be less by 1. This is because sum right would otherwise not be possible if the array is partitioned at the end. – Eugene Jul 01 '16 at 21:20
3

Here is my solution (Java) that i just wrote up for it, very simple to understand and is O(n) and does 100% score on codility:

 public int solution(int[] A) {
    if (A.length == 2)
        return Math.abs(A[0]-A[1]);

    int [] s1 = new int[A.length-1];
    s1[0] = A[0];
    for (int i=1;i<A.length-1;i++) {
        s1[i] = s1[i-1] + A[i];
    }

    int [] s2 = new int[A.length-1];
    s2[A.length-2] = A[A.length-1];
    for (int i=A.length-3;i>=0;i--) {
        s2[i] = s2[i+1] + A[i+1];
    }

    int finalSum = Integer.MAX_VALUE;
    for (int j=0;j<s1.length;j++) {
        int sum = Math.abs(s1[j]-s2[j]);
        if (sum < finalSum)
            finalSum = sum;
    }
    return finalSum;
}
olive_tree
  • 1,417
  • 16
  • 23
2

I was doing the same task, but couldn't get better than 50 something points. My algorithm was too slow. So, I searched for a hint and found your solution. I've used the idea of summing the elements in the array only once and got 100/100. My solution is in JavaScript, but it can be easily transformed into Java. You can go to my solution by using the link below.

http://codility.com/demo/results/demo8CQZY5-RQ2/

Please take a look at my code and let me know if you have some questions. I'd be very happy to help you.

function solution(A) {
// write your code in JavaScript 1.6

var p = 1;
var sumPartOne = A[p - 1];
var sumPartTwo = sumUpArray(A.slice(p, A.length));
var diff = Math.abs(sumPartOne - sumPartTwo);

for(p; p < A.length - 1; p++) {
    sumPartOne += A[p];
    sumPartTwo -= A[p];
    var tempDiff = Math.abs(sumPartOne - sumPartTwo);
    if(tempDiff < diff) {
        diff = tempDiff;
    }
}

return diff;

}

function sumUpArray(A) {
var sum = 0;

for(var i = 0; i < A.length; i++) {
    sum += A[i];
}

return sum;

}

PGrejc
  • 63
  • 5
2

I was also running into issues getting 83% just like CTB, but for my C++ solution.

For my code, my tape sum was evaluating AFTER updating rightsum and leftsum, but therein lies the problem. In this case, the second loop should evaluate up until P=A.size()-1. Otherwise, you will end up evaluating a tape pair where everything is added to leftsum, and nothing is added to rightsum (which is not allowed according to the problem description).

One possibly nice aspect about my solution below (now fixed to get 100%) is that it does one less evaluation of the sum, compared to a couple solutions above.

#include <stdlib.h>

int solution(vector<int> &A) {
    int sumright = 0;
    int sumleft;
    int result;

for (int i=1; i<A.size(); i++) {
    sumright += A[i];
}
sumleft = A[0];

result = abs(sumleft-sumright);
for (int i=1; i<A.size()-1; i++) {
    sumleft  += A[i];
    sumright -= A[i];
    if (abs(sumleft-sumright)<result) {
        result = abs(sumleft-sumright);
    }
}

return result;
}
Nadesri
  • 41
  • 4
2

My C# code 100/100:

using System;

class Solution
{
    public int solution (int[] A)
    {
        int min = int.MaxValue;

        int sumLeft  = 0;
        int sumRight = ArraySum (A);

        for (int i = 1; i < A.Length; i++)
        {
            int val = A[i - 1];

            sumLeft  += val;
            sumRight -= val;

            int diff = Math.Abs (sumLeft - sumRight);

            if (min > diff)
            {
                min = diff;
            }
        }

        return min;
    }

    private int ArraySum (int[] array)
    {
        int sum = 0;

        for (int i = 0; i < array.Length; i++)
        {
            sum += array[i];
        }

        return sum;
    }
}
apocalypse
  • 5,764
  • 9
  • 47
  • 95
2

100% Score : Tape Equilibrium : Codility : JavaScript

function solution(A) {
    // write your code in JavaScript (ECMA-262, 5th edition)
    var p=0;
    var index=0;
    var leftSum=0;
    var rightSum=0;
    var totalSum=0;
    var N = A.length;

    var last_minimum=100000;

    if(A.length == 2)
        return (Math.abs(A[0]-A[1]));
    if(A.length == 1) 
        return (Math.abs(A[0]));

    for(index=0; index < N; index++)
        totalSum = totalSum + A[index];    


    for(p=1; p <= N-1; p++){
        leftSum += A[p - 1];
        rightSum = totalSum - leftSum;

        current_min = Math.abs(leftSum - rightSum);
        last_minimum = current_min < last_minimum ? current_min : last_minimum;

        if (last_minimum === 0)
            break;
    }
    return last_minimum;
}
Ajeet Singh
  • 417
  • 7
  • 23
1

Similar algorithm of CTB posted above: This code get 100% score in JAVA;

class Solution {
public int solution(int[] A) {
    int [] diff;
    int sum1;
    int sum2=0;
    int ans, localMin;
    diff = new int[A.length-1];

    //AT P=1 sum1=A[0]
    sum1=A[0];

    for (int i =1;i<A.length;i++){
        sum2 += A[i];
    }

    ans = Math.abs(sum1- sum2);

    for (int p= 1;p<A.length;p++){
        localMin= Math.abs(sum1- sum2);

        if( localMin < ans ){
           ans = localMin;
        }
        //advance the sum1, sum2
        sum1+= A[p];
        sum2-= A[p];
        diff[p-1]=localMin;
    }
    return (getMinVal(diff));    
}  

public int getMinVal(int[] arr){ 
    int minValue = arr[0]; 
    for(int i=1;i<arr.length;i++){
        if(arr[i] < minValue){ 
            minValue = arr[i]; 
        } 
    } 
    return minValue; 
}    

}

1

this is my 100 score code in Python maybe will help you. You should take a look at if statment its prevent from "double error" if You have N=2 A=[-1,1] when you make sum You get 0 but it should return |-1-1|=|-2|=2

def solution(A):
    a=A 
    tablica=[]
    tablica1=[]
    suma=0
    if len(a) == 2:
        return abs(a[0]-a[1])
    for x in a:
        suma  = suma + x
        tablica.append(suma)
    for i in range(len(tablica)-1):
        wynik=(suma-2*tablica[i])
        tablica1.append(abs(wynik))
    tablica1.sort()
    return tablica1[0]
pietro
  • 11
  • 2
1

100% Score in C Program : Codility - TapeEquilibrium

int solution(int A[], int N) {
    int i, leftSum, rightSum, last_minimum, current_min;

    //initialise variables to store the right and left partition sum 
    //of the divided tape

    //begin dividing from position 1 (2nd element) in a 0-based array
    //therefore the left partition sum will start with 
    //just the value of the 1st element
    leftSum = A[0];

    //begin dividing from position 1 (2nd element) in a 0-based array 
    //therefore the right partition initial sum will start with 
    //the sum of all array element excluding the 1st element
    rightSum = 0;
    i = 1;                
    while (i < N) {
        rightSum += A[i];
        i++;
    }
    //get the initial sum difference between the partitions
    last_minimum = abs(leftSum - rightSum);
    if (last_minimum == 0) return last_minimum; //return immediately if 0 diff found

    //begins shifting the divider starting from position 2 (3rd element)
    //and evaluate the diff, return immediately if 0 diff found
    //otherwise shift till the end of array length
    i = 2; //shift the divider
    while (i < N){
        leftSum += A[i-1]; //increase left sum
        rightSum -= A[i-1]; //decrease right sum
        current_min = abs(leftSum - rightSum); //evaluate current diff
        if (current_min == 0) return current_min; //return immediately if 0 diff
        if (last_minimum > current_min) last_minimum = current_min; //evaluate 
                                                                    //lowest min
        i++; //shift the divider
    }   
    return last_minimum; 
}
Arif Basri
  • 11
  • 4
  • Good answers accompany code samples with an explanation for future readers. While the person asking this question may understand your answer, explaining how you arrived at it will help countless others. – Stonz2 Aug 07 '14 at 19:13
0

This is 100 score in ruby

def solution(a)

    right = 0
    left = a[0]
    ar = Array.new

    for i in 1...a.count
        right += a[i]
    end

    for i in 1...a.count

        check = (left - right).abs
        ar[i-1] = check
        left += a[i]
        right -= a[i]

    end

    find = ar.min

    if a.count == 2
        find = (a[0]-a[1]).abs
    end

    find

end
jimi
  • 1
  • 1
0

this is what I did!!! // write your code in C# with .NET 2.0

   using System;

   class Solution
 {
  public int solution(int[] A)

 {      

 int sumRight = 0, sumleft, result;

    for(int i=1; i<A.Length; i++)
    {
        sumRight += A[i];
    }

    int sumLeft = A[0];
    int min = int.MaxValue;
    for(int P=1; P<A.Length; P++)
    {
        int currentP = A[P-1];
        sumLeft += currentP;
        sumRight -= currentP;

        int diff = Math.Abs(sumLeft - sumRight);
        if(diff < min)
        {
            min = diff;
        }
    }
    return min;
   }
  }
J.A
  • 1
0

Here is an easy solution in C++ (100/100):

#include <numeric>
#include <stdlib.h>

int solution(vector<int> &A)
{
  int left = 0;
  int right = 0;
  int bestDifference = 0;
  int difference = 0;

  left = std::accumulate( A.begin(), A.begin() + 1, 0);
  right = std::accumulate( A.begin() + 1, A.end(), 0);
  bestDifference = abs(left - right);

  for( size_t i = 2; i < A.size(); i++ )
  {
    left += A[i - 1];
    right -= A[i - 1];
    difference = abs(left - right);

    if( difference < bestDifference )
    {
      bestDifference = difference;
    }
  }

  return bestDifference;
}
Krn
  • 1
  • 1
0

100% Score in C Program : Codility

int solution(int A[], int N) {
    long p;
    long index;
    long leftSum;
    long rightSum;
    long totalSum=0;

    long last_minimum=100000;
    long current_min;

    if(N==2) return abs(A[0]-A[1]);
    if(N==1) return abs(A[0]);

    for(index=0; index < N; index++)
        totalSum = totalSum + A[index];    

    leftSum = 0; rightSum = 0;

    for (p = 1; p <= N-1; p++) {

        leftSum += A[p - 1];
        rightSum = totalSum - leftSum;        

        current_min = abs((long)(leftSum - rightSum));

        last_minimum = current_min < last_minimum ? current_min : last_minimum;
        if (last_minimum == 0)
            break;
    }
    return last_minimum;
}

int abs(int n) {
    return (n >= 0) ? n : (-(n));
}
Ajeet Singh
  • 417
  • 7
  • 23
0
def TapeEquilibrium (A):
    n = len(A)
    pos = 0 
    diff= [0]
    if len(A) == 2: return abs(a[0]-a[1])
    for i in range(1,n-1,1):
        diff.sort()
        d = (sum(A[i+1:n-1]) - sum(A[0:i]))
        diff.append(abs(d) + 1)
        if abs(d) < diff[1]:
            pos = i + 1
    return pos
Kiran
  • 9
  • 1
0
public static int solution(int[] A)
    {
        int SumLeft=0;
        int SumRight = 0;
        int bestValue=0;
        for (int i = 0; i < A.Length; i++)
        {
            SumRight += A[i];
        }
        bestValue=SumRight;
        for(int i=0;i<A.Length;i++)
        {
            SumLeft += A[i];
            SumRight-=A[i];
            if (Math.Abs(SumLeft - SumRight) < bestValue)
            {
                bestValue = Math.Abs(SumLeft - SumRight);
            }

        }
        return bestValue;

    }
  • double two elements 0.064 s WRONG ANSWER got 0 expected 2000 simple_positive simple test with positive numbers, length = 5 0.064 s OK simple_negative simple test with negative numbers, length = 5 0.064 s WRONG ANSWER got -27 expected 3 small_random random small, length = 100 0.072 s WRONG ANSWER got -12771 expected 39 small_range range sequence, length = ~1,000 0.064 s OK small small elements 0.072 s WRONG ANSWER got 0 expected 20 – Divesh Pal Dec 05 '14 at 18:45
  • I think you mean length and not Length – panza Jul 20 '17 at 21:38
0

The start and end points of the indexes are not correct - hence the 'doubles' test fails, since this test only has a start and end point. Other tests may pass if the set of numbers used does not happen to contain a dependency on the endpoints.

Let N = A.length The sumright is sums from the right. The maximum value of this should exclude A[N] but include A[0]. sumleft - sums from the left. The maximum value of this should include A[0] but exclude A[N]. So the max sumright is incorrectly calculated in the first loop. Similarly in the second loop the max value of sumleft is not calculated since A[0] is excluded. Nadesri points out this problem, but I thought it would be useful if I explicitly pointed out the errors in your code, since that was what you originally asked. Here is my solution written in c99. https://codility.com/demo/results/demoQ5UWYG-5KG/

drlolly
  • 157
  • 2
  • 6