Home > other >  What is the most efficient way in Kotlin to check if values in two arrays don't differ by much?
What is the most efficient way in Kotlin to check if values in two arrays don't differ by much?

Time:12-10

I need to check if none of the values in two IntArray objects differ by more than 1.

This is the working code, which takes too long:

         var pixelOutsideOfTolerance = false
         val PIXEL_VALUE_TOLERANCE = 1
            for (i in 0 until pixels1.lastIndex) {
                if (pixels1[i] - pixels2[i] > PIXEL_VALUE_TOLERANCE && pixels1[i] - pixels2[i] < - PIXEL_VALUE_TOLERANCE) {
                    pixelOutsideOfTolerance = true
                }
            }
         // Do something with pixelOutsideOfTolerance

What would be a more performant and eloquently way to do this?

CodePudding user response:

Before fixing performance, you must ensure correctness:

  • you have an off-by-one error because lastIndex is the actual last index, not the size. Use for (i in pixels1.indices) instead of explicit ranges to avoid this kind of mistakes
  • your condition can never be true, because you're using && instead of ||. Use abs(pixels1[i] - pixels2[i]) > PIXEL_VALUE_TOLERANCE instead of the more complicated condition checking both positive and negative, it will be simpler to read and less likely to get wrong (you'll need import kotlin.math.abs)

Now you're doing the whole loop even when you already know some pixel differences are outside the tolerance. Extract a function for this loop, and return early when the "outside tolerance" condition is met.

Applying the above, you should now have:

import kotlin.math.abs

private const val PIXEL_VALUE_TOLERANCE = 1

private fun areSimilar(pixels1: IntArray, pixels2: IntArray): Boolean {
    for (i in pixels1.indices) {
        if (abs(pixels1[i] - pixels2[i]) > PIXEL_VALUE_TOLERANCE) {
            return false
        }
    }
    return true
}

// then just use
val pixelsOutsideOfTolerance = !areSimilar(pixels1, pixels2)

If performance was not a requirement, you could also use a more functional approach here. For instance:

val pixelsOutsideOfTolerance = pixels1.indices.any {
    abs(pixels1[it] - pixels2[it]) > PIXEL_VALUE_TOLERANCE
}

Or:

val pixelsOutsideOfTolerance = pixels1.asSequence().zip(pixels2.asSequence())
    .any { abs(it.first - it.second) > PIXEL_VALUE_TOLERANCE }

But if it's really a hot path that you are trying to speed up, that will be counter-productive because of boxing.

  • Related