Closest-pair problem: Difference between revisions

Content added Content deleted
(Previous Divide and Conquer algorithm didn't work correctly. If all points had the same X, it degenerated into a N^2 algorithm)
(Adding Divide and Conquer F# implementation)
Line 543: Line 543:
<lang fsharp>
<lang fsharp>
(0,0, 1,0)
(0,0, 1,0)
</lang>

Divide And Conquer:
<lang fsharp>
open System;
open System.Drawing;
open System.Diagnostics;

let GeneratePoints(n : int) =
let rand = new Random()
[1..n] |> List.map(fun(i) -> new PointF(float32(rand.NextDouble()), float32(rand.NextDouble())))

let Length(seg : PointF * PointF) =
let f = fst seg
let t = snd seg

let dx = f.X - t.X
let dy = f.Y - t.Y

Math.Sqrt((dx*dx + dy*dy) |> float)


let Shortest(a : PointF * PointF, b : PointF * PointF) =
if (Length(a) < Length(b)) then
a
else
b

let rec Closest(from : PointF, pts : PointF list) =
Trace.Assert(pts != [])

let toHd = (from, List.head pts)
let tl = List.tail pts

if (tl = []) then
toHd
else
Shortest(toHd, Closest(from, tl))

let rec ClosestBrute(pts : PointF list) =
let hd = List.head pts
let tl = List.tail pts

if (List.length pts = 2) then
(hd, List.head tl)
else
Shortest (Closest(hd, tl), ClosestBrute tl)

let rec ClosestBoundY(from : PointF, maxY : float32, ptsByY : PointF list, count : int) =
let hd = List.head ptsByY
let toHd = (from, hd)

if (count = 1) || (hd.Y >= maxY) then
toHd
else
let tl = List.tail pts
let bestOfRest = ClosestBoundY(from, maxY, tl, count-1)

Shortest(toHd, bestOfRest)

let rec ClosestWithinRange(ptsByY : PointF list, maxDy : float32, count : int) =
Trace.Assert(count >= 2)

let hd = List.head ptsByY
let tl = List.tail ptsByY

if (count = 2) then
(hd, List.head tl)
else
let fromHd = ClosestBoundY(hd, hd.Y + maxDy, tl, count-1)
let fromRest = ClosestWithinRange(tl, maxDy, count-1)
Shortest(fromHd, fromRest)


let rec ShiftToFirst (first : PointF list, second : PointF list, n : int) =
if (n = 0) then
(first, second)
else
let h = List.head second
let newFirst = List.Cons(h, first)
let newSecond = List.tail(second)

ShiftToFirst(newFirst, newSecond, n - 1)

let Halve(pts : PointF list) =
let n = (List.length pts) / 2
ShiftToFirst( [], pts, n)


let rec ClosestPair(pts : PointF list) =
Trace.Assert(List.length pts >= 2)

if (List.length(pts) <= 4) then
ClosestBrute pts
else
let ptsByX = pts |> List.sortBy(fun(p) -> p.X)

let (left, right) = Halve ptsByX
let leftResult = ClosestPair left
let rightResult = ClosestPair right

let bestInHalf = Shortest (leftResult, rightResult)
let bestLength = Length(bestInHalf) |> float32

let divideX = List.head(right).X
let inBand = pts |> List.filter(fun(p) -> Math.Abs(p.X - divideX) < bestLength)
let countInBand = List.length inBand

if countInBand < 2 then
bestInHalf
else
let byY = inBand |> List.sortBy(fun(p) -> p.Y)
let bestCross = ClosestWithinRange(byY, bestLength, countInBand)
Shortest(bestInHalf, bestCross)

</lang>
</lang>