HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Find two numbers in a sorted array whose sum is closest to a given number

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
sumnumberwhosearraynumbersclosesttwofindsortedgiven

Problem

Given a sorted array and a number x, find a pair in array whose sum is closest to x.

Here's my solution:

import Data.List (minimumBy)
import Data.Ord (comparing)

closestPairSum :: (Num a, Ord a) => [a] -> a -> (a, a)
closestPairSum xs n = snd $ minimumBy (comparing fst) pairs
  where 
    pairs = sums xs (reverse xs)
    sums xxs@(x:xs) yys@(y:ys) = let sum = x + y in
      if n < sum
      then (sum - n, (x, y)) : sums xxs ys
      else (n - sum, (x, y)) : sums xs yys
    sums _ _ = []


I'm mostly self-taught, so I would love to know in which ways my code is not according to common practice. More specifically:

  • How can I more clearly align my let-in and if-then-else expressions?



  • I use an intermediate list of (a, (a, a)) tuples, which I'm not thrilled about. Is there a more elegant way to do this, without sacrificing performance?



  • I'm on the fence about sum xs (reverse xs) vs sum xs $ reverse xs. Is this just considered nitpicking, or do people have a strong opinion about this?



  • Would it make more sense to swap the parameters of this function? I have the feeling that a would usually precede [a] with pointfree style in mind, but I'm not sure about this one.



  • Anything else that I might not have thought of :)

Solution

The "intermediate array" takes no extra space due to lazy evaluation. minimumBy . comparing is minimumOn. Yes, swapping the parameters makes sense. Inline then and else once more. I wouldn't worry so much about constant factors - have you compiled it with -O2 and tested (eg with criterion)?

import Data.List (minimumOn)

closestPairSum :: (Num a, Ord a) => a -> [a] -> (a, a)
closestPairSum n xs = minimumOn (abs . (n-) . uncurry (+)) pairs where
  pairs = sums xs (reverse xs)
  sums xxs@(x:xs) yys@(y:ys) = (x, y) : if n < x + y
    then sums xxs ys
    else sums xs yys
  sums _ _ = []

Code Snippets

closestPairSum :: (Num a, Ord a) => a -> [a] -> (a, a)
closestPairSum n xs = minimumOn (abs . (n-) . uncurry (+)) pairs where
  pairs = sums xs (reverse xs)
  sums xxs@(x:xs) yys@(y:ys) = (x, y) : if n < x + y
    then sums xxs ys
    else sums xs yys
  sums _ _ = []

Context

StackExchange Code Review Q#155039, answer score: 2

Revisions (0)

No revisions yet.