Posts Tagged ‘points’

Programming Praxis – Four Points Determine A Square

January 2, 2013

In today’s Programming Praxis exercise, our goal is to determine whether four given points in a 2D plane make up a square. Let’s get started, shall we?

AnĀ  import:

import Data.List

The provided solution calculates the squares distances between the first and the three other points and considers the points a square when the smallest two are equal and the largest is the sum of the two smallest, i.e. a2 + b2 = c2. This solution is incorrect, however. If we take the points (0, 0), (1, 0), (0, 1) and (-1, -1), both conditions are satisfied despite the fact that the four points do not form a square. To handle this case correctly, we can use the same basic approach, albeit expanded a bit. Instead of just three distances, we calculate the distances between all six pairs. Of these six, the four smallest ones should be equal, as should the two larger ones. Pythagoras’ theorem can remain the same.

isSquare :: (Num a, Ord a) => (a, a) -> (a, a) -> (a, a) -> (a, a) -> Bool
isSquare p q r s = and [a == b, b == c, c == d, e == f, a + b == e] where
    [a,b,c,d,e,f] = sort [l | (h:t) <- tails [p,q,r,s], l <- map (dist h) t]
    dist (x1,y1) (x2,y2) = (x2-x1)^2 + (y2-y1)^2

Testing reveals that this version handles the old test cases correctly, as well as the example described above.

main :: IO ()
main = do print $ isSquare (0,0) (0,1) (1,1) (1,0)
          print $ isSquare (0,0) (2,1) (3,-1) (1, -2)
          print $ isSquare (0,0) (1,1) (0,1) (1,0)
          print . not $ isSquare (0,0) (0,2) (3,2) (3,0)
          print . not $ isSquare (0,0) (3,4) (8,4) (5,0)
          print . not $ isSquare (0,0) (0,0) (1,1) (0,0)
          print . not $ isSquare (0,0) (0,0) (1,0) (0,1)
          print . not $ isSquare (0,0) (1,0) (0,1) (-1,-1)

Programming Praxis – Amazon Interview Question

November 27, 2012

In today’s Programming Praxis exercise, our goal is to find the 100 points closest to (0, 0) out of a list of 1000000 random points in O(n). Let’s get started, shall we?

Some imports:

import Control.Applicative
import qualified Data.IntMap as I
import System.Random

The obvious solution is to simply sort the list and take the first 100 elements. Sorting, however, usually takes O(n log n), which is not allowed. Fortunately, by using the square of the distance rather than the distance we not only save a million square root operations, but it also makes the value we’re sorting by an integer, which allows us to use an IntMap that has O(1) insertion and thus O(n) sorting.

closest :: Int -> [(Int, Int)] -> [(Int, Int)]
closest n = take n . concat . I.elems . I.fromListWith (flip (++)) .
            map (\(x,y) -> (x*x+y*y, [(x,y)]))

To test, we need a million random points.

points :: IO [(Int, Int)]
points = take 1000000 <$> liftA2 zip (randomRs (-1000, 1000) <$> newStdGen)
                                     (randomRs (-1000, 1000) <$> newStdGen)

Finally we run the algorithm.

main :: IO ()
main = print . closest 100 =<< points

To see whether our algorithm is truly linear, let’s look at some timings:

1 million: 2.9 s
2 million: 5.7 s
4 million: 11.6 s
8 million: 23.8 s

Looks fairly linear to me.