You can walk through the array once and keep track of the number of elements that have each remainder up to k-1
, when they are divided by k
. That takes O(k)
space, but allows you to solve the problem in O(n + k)
steps, which is much better if k
is small.
Pseudocode (AKA JavaScript):
function countDivisiblePairs( arr, k ) {
const remainders = Array( k ).fill( 0 );
for ( let i = 0; i < arr.length; i++ ) {
remainders[ arr[ i ] % k ]++;
}
// Count the pairs of elements that were individually
// divisible by `k` (had a remainder of `0`), so
// added together they are still divisible by k
//
// `remainders[ 0 ]*remainders[ 0 ]` of them if we wanted all, but
// since we only want to count when i < j, so we subtract
// `remainders[0]` to get rid of the cases where `i = j` and then
// we divide by 2 to remove the cases where `i > j`.
let numPairs = (remainders[ 0 ]*remainders[ 0 ] - remainders[ 0 ])/2;
// Count the cases where the remainder wasn't 0,
// if the remainders sum to `k`, then the sum is divisible by `k`.
for ( let i = 1; i <= k/2; i++ ) {
// Note that i + (k - i) = k, so each elements with a
// remainder of `i` can be paired with each element with
// a remainder of `k`, but, if `k` was even there will be a
// final iteration where `i = (k - i)`, so we need to add a
// special case to only count when `i < j`, we do it the same
// way as we did for the case when the remainder was 0. with
// the if statement `(2*i === k)` is another way of writing
// the expression `i === (k - i)`.
if ( 2*i === k )
numPairs += (remainders[ i ]*remainders[ i ] - remainders[ i ])/2;
else
numPairs += remainders[ i ]*remainders[ k - i ];
}
return numPairs;
}
The code above assumes that there are no duplicates in the input array. In that case you need special care EG, [2,2,4,4,4], 2
should ouput 6
but instead outputs 10
(the correct output for [2,4,6,8,10], 2
) but it should give you the jist of the algorithm. Output:
countDivisiblePairs( [1,2,3,4,5,6,7,8], 2 ); // 12
countDivisiblePairs( [1,2,3,4,5,6,7,8], 3 ); // 10
countDivisiblePairs( [1,2,3,4,5,6,7,8], 4 ); // 6
countDivisiblePairs( [1,2,3,4,5,6,7,8], 5 ); // 6
Note that the special case in the loop only happens on the last iteration and only when k
is even, EG. if k
is 4, then when i = 2
, i === (k - i)
, so if we didn't have the special handling we would count extra elements. EG for the example output where I used a k
of 4
, there are two elements that have a remainder of 2
: [2,6]
. If we didn't have the extra handling it would say there are four ways to pair them, amounting to (2,2),(2,6),(6,2),(6,6)
, but the extra logic subtracts the cases where they're paired with themselves, so we have (2,6),(6,2)
remaining and then the division by 2 subtracts the case where i > j
, so you're left with only one pair being counted: (2,6)
.