Solving CountNonDivisible codility question

Viewed 389

I am going through Codility questions and I am on "CountNonDivisible" question. I tried with the brute way it worked and it's not efficient at all.

I found the answers with no explanations, so if someone could take some time and walk me through this answer it would be highly appreciated.

function solution(A) {
    const lenOfA = A.length
    const counters = Array(lenOfA*2 + 1).fill(0)
    for(let j = 0; j<lenOfA; j++) counters[A[j]]++;
    
    return A.map(number=> {
        let nonDivisor = lenOfA
        for(let i = 1; i*i <= number; i++) {
            if(number % i !== 0) continue;
            nonDivisor -= counters[i];
            if(i*i !== number) nonDivisor -= counters[number/i]
        }
        return nonDivisor
    })
}

This is the question

Task description

You are given an array A consisting of N integers.

For each number A[i] such that 0 ≤ i < N, we want to count the number of elements of the array that are not the divisors of A[i]. We say that these elements are non-divisors.

For example, consider integer N = 5 and array A such that: A[0] = 3 A[1] = 1 A[2] = 2 A[3] = 3 A[4] = 6

For the following elements:

    A[0] = 3, the non-divisors are: 2, 6,
    A[1] = 1, the non-divisors are: 3, 2, 3, 6,
    A[2] = 2, the non-divisors are: 3, 3, 6,
    A[3] = 3, the non-divisors are: 2, 6,
    A[4] = 6, there aren't any non-divisors.

Write a function:

function solution(A);

that, given an array A consisting of N integers, returns a sequence of integers representing the amount of non-divisors.

Result array should be returned as an array of integers.

For example, given: A[0] = 3 A[1] = 1 A[2] = 2 A[3] = 3 A[4] = 6

the function should return [2, 4, 3, 2, 0], as explained above.

Write an efficient algorithm for the following assumptions:

    N is an integer within the range [1..50,000];
    each element of array A is an integer within the range [1..2 * N].
1 Answers

Here is a way I can explain the above solution.

From the challenge description, it states each element of the array are within the range [1... 2*N] where N is the length of the array; this means that no element in the array can be bigger than 2*N.

So an array of counters is created with length 2*N + 1(max index equal to max possible value in array), and every element in it is initialized to 0, except the elements which actually exists in the given array, those are set to one.

Now we want to go through all the elements in the given array, assuming every number is a nondivisor and subtracting the assumed nondivisors by the number of divisors we have in our array of counters, this will give us the actual number of nondivisors. During the loop, when an element that doesn't exist in our array is a divisor, 0 is subtracted(remember our initialized values), and when we encounter a divisor that is also in our array, we subtract by 1(remember our initialized values). This is done for every element in the array to get each of their nondivisor counts.

The solution you posted makes use of map, which is just a concise way of transforming arrays. A simple for loop can also be used an will be easier to understand. Here is a for loop variation of the solution above

  function solution(A) {
    const lenOfA = A.length
    const counters = Array(lenOfA*2 + 1).fill(0)
    for(let i = 0; i<lenOfA; i++) counters[A[i]]++;

    const arrayOfNondivisors = [];

    for(let i = 0; i < A.length; i++) {
      let nonDivisor = lenOfA
        for(let j = 1; j*j <= A[i]; j++) {
            if(A[i] % j !== 0) continue;
            nonDivisor -= counters[j];
            if(j*j !== A[i]) nonDivisor -= counters[A[i]/j]
        }
        arrayOfNondivisors.push(nonDivisor);
    }
    
    return arrayOfNondivisors;
}
Related