5 people like it.

Bin Packing

Implementation of the 'best fit' heuristic algorithm for bin packing problems. Incudes an implementation of 'binary tree with duplicates'. See this blog post for details: http://fwaris.wordpress.com/2013/04/01/best-fit-bin-packing/ Update: Bug fixes and added 'worst fit' heuristic implementation

  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
module BinPacking

[<RequireQualifiedAccess>]
module Btd =

    //binary tree with duplicates: key-value, size, left subtree and right subtree
    type T<'a,'b> = T of ('a*'b)*int*T<'a,'b> option*T<'a,'b> option

    type private Direction = Left | Right

    let empty = None

    let size t = match t with None -> 0 | Some (T(_,s,_,_)) -> s
    let private (~~) = size

    let rec private percolate t path =
        match path with
        | [] -> t
        | (Left,T(kv,s,left,right))::rest ->
             percolate (Some (T(kv,s - ~~left + ~~t, t, right))) rest
        | (Right,T(kv,s,left,right))::rest -> percolate (Some (T(kv,s - ~~right + ~~t, left, t))) rest

    let rec private insert child t path =
        match t with 
        | None -> percolate child path
        | Some (T((pk,_),_,l,r) as t2) ->
            match child with
            | None -> t
            | Some (T((ck,_),_,_,_)) ->
                if ck < pk then insert child l ((Left,t2)::path)
                else insert child r ((Right,t2)::path)

    let add e t = insert (Some(T(e,1,None,None))) t []

    let merge l r =
        match l,r with
        | None, t -> t
        | t, None -> t
        | Some(T(_,ls,_,_)),Some(T(_,rs,_,_)) ->
            if ls < rs then insert r l []
            else insert l r []
            
    let remove kv t =
        let rec remove ((k,v) as kv) t path =
            match t with
            | None -> None
            | Some (T((k',v') as kv',s,l,r) as t) ->
                if kv = kv' then 
                    match path with 
                    | [] -> merge l r //root removed
                    | _  -> percolate None path
                elif k < k' then remove kv l ((Left,t)::path)
                else remove kv r ((Right,t)::path)
        match remove kv t [] with
        | None -> t
        | x -> x
    
    let findBestFit c t =
        let rec findBestFit c t currentBest =
            match t with
            | None -> currentBest
            | Some (T((k,v),_,l,r)) ->
                if c = k then Some(k,v)
                elif c < k then findBestFit c l (Some(k,v))
                else findBestFit c r currentBest
        findBestFit c t None

type Bin<'a> = {Size:int; Id:string; Data:'a}
type Item<'a> = {Size:int; Data:'a} 

let rec private fillItems (map:Map<Bin<_>,Item<_>list>, t, remaining) =
    match remaining with
    | [] -> map, t, []
    | x::rest ->
        match t |> Btd.findBestFit x.Size with
        | None -> map,t,remaining //can't add any more items into the current bin tree structure
        | Some ((capacity,bin) as kv) -> 
            let remainingCapacity = capacity - x.Size
            let t' = t |> Btd.remove kv |> Btd.add (remainingCapacity,bin)
            let map' = 
                match map |> Map.tryFind bin with
                | None -> map |> Map.add bin [x]
                | Some l -> map |> Map.add bin (x::l)
            fillItems (map', t', rest)

///implements the best-fit heuristic algorithm for 1-dimensional bin packing
let pack (availableBins:Bin<_> list) (items:Item<_> list) =
    let map,t,remaining = 
        ((Map.empty,Btd.empty,items), availableBins) ||> List.fold (fun (map,t,remaining) bin ->
            match remaining with 
            | [] -> map,t,[] //no more items remaining
            | xs -> fillItems (map,t |> Btd.add (bin.Size,bin),xs)) //add a bin to tree and fill items
    map,remaining

(* usage:

let bins = [{Size=150; Id="a"; Data="a"}; {Size=235; Id="b"; Data="b"}; {Size=215;Id="c"; Data="c"}]

let rng = System.Random()
let items = [for i in 1..40 -> {Size = rng.Next(1,30); Data=i.ToString()}]

//sort items in descending order of size for best-fit decreasing order
let sortedItems = items |> List.sortBy (fun i -> -i.Size)

let schedule,leftOverItems = pack bins sortedItems

*)
module BinPacking
Multiple items
type RequireQualifiedAccessAttribute =
  inherit Attribute
  new : unit -> RequireQualifiedAccessAttribute

Full name: Microsoft.FSharp.Core.RequireQualifiedAccessAttribute

--------------------
new : unit -> RequireQualifiedAccessAttribute
type T<'a,'b> = | T of ('a * 'b) * int * T<'a,'b> option * T<'a,'b> option

Full name: BinPacking.Btd.T<_,_>
union case T.T: ('a * 'b) * int * T<'a,'b> option * T<'a,'b> option -> T<'a,'b>
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
type private Direction =
  | Left
  | Right

Full name: BinPacking.Btd.Direction
union case Direction.Left: Direction
union case Direction.Right: Direction
val empty : 'a option

Full name: BinPacking.Btd.empty
union case Option.None: Option<'T>
val size : t:T<'a,'b> option -> int

Full name: BinPacking.Btd.size
val t : T<'a,'b> option
union case Option.Some: Value: 'T -> Option<'T>
val s : int
val private percolate : t:T<'a,'b> option -> path:(Direction * T<'a,'b>) list -> T<'a,'b> option

Full name: BinPacking.Btd.percolate
val path : (Direction * T<'a,'b>) list
val kv : 'a * 'b
val left : T<'a,'b> option
val right : T<'a,'b> option
val rest : (Direction * T<'a,'b>) list
val private insert : child:T<'a,'b> option -> t:T<'a,'b> option -> path:(Direction * T<'a,'b>) list -> T<'a,'b> option (requires comparison)

Full name: BinPacking.Btd.insert
val child : T<'a,'b> option (requires comparison)
val t : T<'a,'b> option (requires comparison)
val path : (Direction * T<'a,'b>) list (requires comparison)
val pk : 'a (requires comparison)
val l : T<'a,'b> option (requires comparison)
val r : T<'a,'b> option (requires comparison)
val t2 : T<'a,'b> (requires comparison)
val ck : 'a (requires comparison)
val add : 'a * 'b -> t:T<'a,'b> option -> T<'a,'b> option (requires comparison)

Full name: BinPacking.Btd.add
val e : 'a * 'b (requires comparison)
val merge : l:T<'a,'b> option -> r:T<'a,'b> option -> T<'a,'b> option (requires comparison)

Full name: BinPacking.Btd.merge
val ls : int
val rs : int
val remove : 'a * 'b -> t:T<'a,'b> option -> T<'a,'b> option (requires comparison and equality)

Full name: BinPacking.Btd.remove
val kv : 'a * 'b (requires comparison and equality)
val t : T<'a,'b> option (requires comparison and equality)
val remove : ('c * 'd -> T<'c,'d> option -> (Direction * T<'c,'d>) list -> T<'c,'d> option) (requires comparison and equality)
val k : 'c (requires comparison)
val v : 'd (requires equality)
val kv : 'c * 'd (requires comparison and equality)
val t : T<'c,'d> option (requires comparison and equality)
val path : (Direction * T<'c,'d>) list (requires comparison and equality)
val k' : 'c (requires comparison)
val v' : 'd (requires equality)
val kv' : 'c * 'd (requires comparison and equality)
val l : T<'c,'d> option (requires comparison and equality)
val r : T<'c,'d> option (requires comparison and equality)
val t : T<'c,'d> (requires comparison and equality)
val x : T<'a,'b> option (requires comparison and equality)
val findBestFit : c:'a -> t:T<'a,'b> option -> ('a * 'b) option (requires comparison)

Full name: BinPacking.Btd.findBestFit
val c : 'a (requires comparison)
val findBestFit : ('c -> T<'c,'d> option -> ('c * 'd) option -> ('c * 'd) option) (requires comparison)
val c : 'c (requires comparison)
val t : T<'c,'d> option (requires comparison)
val currentBest : ('c * 'd) option (requires comparison)
val v : 'd
val l : T<'c,'d> option (requires comparison)
val r : T<'c,'d> option (requires comparison)
type Bin<'a> =
  {Size: int;
   Id: string;
   Data: 'a;}

Full name: BinPacking.Bin<_>
Bin.Size: int
Bin.Id: string
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
Multiple items
Bin.Data: 'a

--------------------
namespace Microsoft.FSharp.Data
type Item<'a> =
  {Size: int;
   Data: 'a;}

Full name: BinPacking.Item<_>
Item.Size: int
Multiple items
Item.Data: 'a

--------------------
namespace Microsoft.FSharp.Data
val private fillItems : map:Map<Bin<'a>,Item<'b> list> * t:Btd.T<int,Bin<'a>> option * remaining:Item<'b> list -> Map<Bin<'a>,Item<'b> list> * Btd.T<int,Bin<'a>> option * Item<'b> list (requires comparison)

Full name: BinPacking.fillItems
val map : Map<Bin<'a>,Item<'b> list> (requires comparison)
Multiple items
module Map

from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> =
  interface IEnumerable
  interface IComparable
  interface IEnumerable<KeyValuePair<'Key,'Value>>
  interface ICollection<KeyValuePair<'Key,'Value>>
  interface IDictionary<'Key,'Value>
  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
  member Add : key:'Key * value:'Value -> Map<'Key,'Value>
  member ContainsKey : key:'Key -> bool
  override Equals : obj -> bool
  member Remove : key:'Key -> Map<'Key,'Value>
  ...

Full name: Microsoft.FSharp.Collections.Map<_,_>

--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val t : Btd.T<int,Bin<'a>> option (requires comparison)
val remaining : Item<'b> list
val x : Item<'b>
val rest : Item<'b> list
module Btd

from BinPacking
val findBestFit : c:'a -> t:Btd.T<'a,'b> option -> ('a * 'b) option (requires comparison)

Full name: BinPacking.Btd.findBestFit
val capacity : int
val bin : Bin<'a> (requires comparison)
val kv : int * Bin<'a> (requires comparison)
val remainingCapacity : int
val t' : Btd.T<int,Bin<'a>> option (requires comparison)
val remove : 'a * 'b -> t:Btd.T<'a,'b> option -> Btd.T<'a,'b> option (requires comparison and equality)

Full name: BinPacking.Btd.remove
val add : 'a * 'b -> t:Btd.T<'a,'b> option -> Btd.T<'a,'b> option (requires comparison)

Full name: BinPacking.Btd.add
val map' : Map<Bin<'a>,Item<'b> list> (requires comparison)
val tryFind : key:'Key -> table:Map<'Key,'T> -> 'T option (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.tryFind
val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.add
val l : Item<'b> list
val pack : availableBins:Bin<'a> list -> items:Item<'b> list -> Map<Bin<'a>,Item<'b> list> * Item<'b> list (requires comparison)

Full name: BinPacking.pack


implements the best-fit heuristic algorithm for 1-dimensional bin packing
val availableBins : Bin<'a> list (requires comparison)
val items : Item<'b> list
val empty<'Key,'T (requires comparison)> : Map<'Key,'T> (requires comparison)

Full name: Microsoft.FSharp.Collections.Map.empty
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val fold : folder:('State -> 'T -> 'State) -> state:'State -> list:'T list -> 'State

Full name: Microsoft.FSharp.Collections.List.fold
val xs : Item<'b> list
Next Version Raw view Test code New version

More information

Link:http://fssnip.net/hG
Posted:11 years ago
Author:Faisal Waris
Tags: scheduling , optimization , bin packing , binary tree