home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / ipl / gprogs / binpack.icn < prev    next >
Text File  |  2000-07-29  |  15KB  |  628 lines

  1. ############################################################################
  2. #
  3. #    File:     binpack.icn
  4. #
  5. #    Subject:  Program to demonstrate some bin packing algorithms
  6. #
  7. #    Author:   Gregg M. Townsend
  8. #
  9. #    Date:     June 23, 2000
  10. #
  11. ############################################################################
  12. #
  13. #   This file is in the public domain.
  14. #
  15. ############################################################################
  16. #
  17. #  Usage:  binpack [window options]
  18. #
  19. #  Binpack illustrates several approximation algorithms for solving the
  20. #  one-dimensional bin packing problem.
  21. #
  22. #  For references, see the "info" screen.
  23. #
  24. ############################################################################
  25. #
  26. #  Requires:  Version 9 graphics
  27. #
  28. ############################################################################
  29. #
  30. #  Links: numbers, graphics, random, vsetup
  31. #
  32. ############################################################################
  33.  
  34. link numbers
  35. link graphics
  36. link random
  37. link vsetup
  38.  
  39. $define Version "Binpack, Version 1.0  (September, 1993)"
  40.  
  41. $define MAXK 250    # max value of `k' allowed
  42.  
  43. $define FULL 61261200    # value representing a full bin
  44.             # (least common multiple of {1 to 18, 20, and 25})
  45.  
  46. $define X0 120        # left edge of bin display
  47. $define DY 165        # vertical spacing
  48. $define YSCALE 155    # scaling for one display
  49.  
  50. $define BX1 10        # x-coord for first button column
  51. $define BX2 60        # x-coord for second button column
  52. $define BWIDTH 40    # button width
  53. $define BHEIGHT 16    # button height
  54. $define BSPACE 16    # button spacing
  55.  
  56.  
  57. # parameter values
  58. global maxsize        # maximum piece size
  59. global nreload        # number of pieces on a reload
  60. global kvalue        # constant `k' used in some algorithms
  61.  
  62. # current source set
  63. global pieces        # list of piece sizes
  64. global dx        # distance between bins
  65. global bwidth        # bin width
  66. global cdiv        # divisor for converting size to color index
  67.  
  68. # current output parameters
  69. global bin        # list of current bin sizes
  70. global nfilled        # number of bins (partially) filled
  71. global xll, yll        # lower left corner of display area
  72.  
  73.  
  74. # miscellany
  75. global width        # window width
  76. global color        # array of GCs of different colors
  77. global glossary        # list of explanations
  78.  
  79.  
  80.  
  81. #   Future possibilities:
  82. #
  83. #   better layout -- critical controls are too crowded
  84. #   add artificial delays for better visualization
  85. #   implement O(n log n) algs as such instead of O(n^2)
  86. #    n.b. this may not help because can't use Icon's native data structs
  87.  
  88.  
  89.  
  90. #########################  main program  #########################
  91.  
  92. procedure main(args)
  93.    local v, r, c, gc
  94.  
  95.    randomize()                # set irreproducible mode
  96.  
  97.    v := ui(args)            # open window, set up vib-built vidgets
  98.    r := v["root"]
  99.    glossary := []
  100.    addbutton(r, "BF", bestfit, "Best Fit", "picks the fullest possible bin")
  101.    addbutton(r, "WF", worstfit, "Worst Fit", "picks the emptiest bin")
  102.    addbutton(r, "AWF",nearworst,"Almost Worst Fit", "picks second-emptiest bin")
  103.    addbutton(r, "FF", firstfit, "First Fit", "picks the oldest possible bin")
  104.    addbutton(r, "LF", lastfit, "Last Fit", "picks the newest possible bin")
  105.    addbutton(r, "NF", nextfit, "Next Fit", "tries only the current bin")
  106.    addbutton(r, "N(k)", nextk, "Next-k Fit", "tries the k newest bins")
  107.    addbutton(r, "H(k)", harmonic, "Harmonic Algorithm",
  108.       "classifies into {1/1,1/2,...,1/k}")
  109.    addbutton(r, "G(k)", gxfit, "Group-X Fit", "groups into k equal classes")
  110.    VResize(r)
  111.  
  112.    # workaround freeing of gray highlight color seen with "binpack -Bwhite"
  113.    BevelReset()                # work around color freeing bug
  114.  
  115.    color := []
  116.    if WAttrib("depth") = 1 then
  117.       put(color, &window)
  118.    else {
  119.       # make a set of colors for different bin heights
  120.       # note that exactly half are reds/yellows and half are blues & darker
  121.       every c := Blend(
  122.             "black", 1, "deep purple-magenta", 10, "cyan-blue",
  123.             1, "reddish-yellow", 11, "orange-red") do {
  124.          gc := Clone(&window)
  125.          Shade(gc, c)
  126.          put(color, gc)
  127.          }
  128.       color := copy(color)        # ensure contiguous
  129.       }
  130.  
  131.    # keep the following initializations in sync with initial slider positionm
  132.    setmax(v["max"], 20)            # set maximum bin value
  133.    setbins(v["bins"], -100)        # set number of bins
  134.    setk(v["kval"], -10)            # set constant `k' value
  135.  
  136.    reload()                # initialize random bins
  137.    status("")                # display bin count
  138.  
  139.    &error := 1
  140.    WAttrib("resize=on")
  141.    &error := 0
  142.  
  143.    r.V.event := 1            # disable screen erase on resize
  144.    GetEvents(r, leftover)        # enter event loop
  145. end
  146.  
  147.  
  148. #  addbutton -- add a button (and a D variant) on every shelf
  149.  
  150. procedure addbutton(r, label, proc, name, defn)
  151.    local v, n, y
  152.    static yoff
  153.    initial yoff := 0
  154.  
  155.    y := yoff +:= BSPACE
  156.    while (y +:= DY) < WAttrib("height") do {
  157.       Vbutton(r, BX1, y, r.win, label, pack, proc, V_RECT, BWIDTH, BHEIGHT)
  158.       Vbutton(r, BX2, y, r.win, label||"D", pack, proc, V_RECT, BWIDTH, BHEIGHT)
  159.       }
  160.    put(glossary, left(label, 6) || left(name, 20) || defn)
  161.    return
  162. end
  163.  
  164.  
  165.  
  166. #########################  parameter setting  #########################
  167.  
  168. #  These routines are called during initialization and in response to
  169. #  slider movement.
  170.  
  171.  
  172. #  setk(v, n) -- set value of constant `k', based on 1 - 100 slider scale
  173.  
  174. procedure setk(v, n)
  175.    if n >= 0 then                # if slider call
  176.       n := integer(MAXK ^ ((n / 100.0) ^ 0.70))    # convert nonlinearly
  177.    else
  178.       n := -n                    # initial call
  179.    kvalue := roundoff(n)
  180.    GotoXY(v.ax, v.ay + v.ah + 14)
  181.    WWrites(left("k=" || kvalue, 8))
  182.    return
  183. end
  184.  
  185.  
  186. #  setmax(v, n) -- set maxsize, based on 1 - 20 slider scale.
  187.  
  188. procedure setmax(v, n)
  189.    local fract
  190.  
  191.    fract := n / 20.0
  192.    maxsize := integer(fract * FULL)
  193.    GotoXY(v.ax, v.ay + v.ah + 14)
  194.    WWrites(" max size ", ((fract || "00") ? move(4)))
  195.    return
  196. end
  197.  
  198.  
  199. #  setbins(v, n) -- set number of bins, based on 1 - 100 slider scale
  200.  
  201. procedure setbins(v, n)
  202.    local s, max
  203.  
  204.    max := WAttrib("width") - 40 - X0        # max that will fit on screen
  205.    if &shift then                # allow more if shifted
  206.       max /:= 1.1 * (maxsize / (2.0 * FULL))
  207.  
  208.    if n >= 0 then                # if slider call
  209.       n := integer(max ^ ((n / 100.0) ^ 0.40))    # convert nonlinearly
  210.    else
  211.       n := -n                    # initial call
  212.    n <:= 5
  213.    n := roundoff(n, 5)                # convert to round number
  214.  
  215.    nreload := n
  216.    s := center(nreload, 5)
  217.    GotoXY(v.ax + (v.aw - TextWidth(s)) / 2, v.ay + v.ah + 17)
  218.    WWrites(s)
  219.    return
  220. end
  221.  
  222.  
  223. #  roundoff(n) -- truncate n to a nice number divisible by m (at least)
  224.  
  225. procedure roundoff(n, m)
  226.    local d
  227.  
  228.    if n > 1000 then {
  229.       if n > 10000 then
  230.          d := 1000
  231.       else if n > 5000 then
  232.          d := 500
  233.       else
  234.          d := 100
  235.       }
  236.    else if n > 500 then
  237.       d := 50
  238.    else if n > 100 then
  239.       d := 10
  240.    else if n > 50 then
  241.       d := 5
  242.    n -:= n % \d
  243.    n -:= n % \m
  244.    return n
  245. end
  246.  
  247.  
  248. #########################  bin packing primitives  #########################
  249.  
  250.  
  251. #  empty(n) -- empty shelf n
  252.  
  253. procedure empty(n)
  254.    bin := list(*pieces, 0)
  255.    nfilled := 0
  256.    xll := X0
  257.    yll := n * DY
  258.    EraseArea(xll, yll - DY + 1, , DY)
  259.    width := WAttrib("width")
  260.    return
  261. end
  262.  
  263.  
  264. #  place(p, b) -- add a piece of size p to bin b
  265.  
  266. procedure place(p, b)
  267.    local o, t, x, y0, y1
  268.    static invfull
  269.    initial invfull := 1.0 / FULL
  270.  
  271.    o := bin[b] | fail
  272.    if (t := o + p) > FULL then
  273.       fail
  274.    bin[b] := t
  275.    nfilled <:= b
  276.    if (x := xll + (b - 1) * dx) < width then {
  277.       y0 := integer(yll - YSCALE * o * invfull)
  278.       y1 := integer(yll - YSCALE * t * invfull) + 1
  279.       FillRectangle(color[p / cdiv + 1], x, y1, bwidth, 0 < (y0 - y1))
  280.       }
  281.    return
  282. end
  283.  
  284.  
  285. #  status(s) -- write string s and shelf population at end of output shelf
  286.  
  287. procedure status(s)
  288.    local x
  289.  
  290.    x := xll + nfilled * dx + 4
  291.    x >:= width - 40
  292.    GotoXY(x, yll - 15)
  293.    WWrites(s)
  294.    GotoXY(x, yll)
  295.    WWrites(nfilled)
  296.    return
  297. end
  298.  
  299.  
  300.  
  301. #########################  source set manipulation  #########################
  302.  
  303.  
  304. #  reload() -- reload first shelf with random-sized pieces.
  305.  
  306. procedure reload()
  307.    local i, j, z, p
  308.  
  309.    pieces := list(nreload)
  310.    empty(1)
  311.    dx := (width - 40 - X0) / nreload
  312.    dx <:= 1
  313.    dx >:= 20
  314.    bwidth := 4 * dx / 5
  315.    bwidth <:= 1
  316.    cdiv := (maxsize + *color - 1) / *color
  317.    every place(pieces[i := 1 to *pieces] := ?maxsize, i)
  318.    status("new")
  319.    return
  320. end
  321.  
  322.  
  323. #  mix() -- randomly reorder the first shelf.
  324. #
  325. #  if shifted, place equally-spaced using golden ratio
  326.  
  327. procedure mix()
  328.    local i, n, p
  329.  
  330.    if &shift then {
  331.       n := integer(*pieces / &phi + 1)
  332.       while gcd(*pieces, n) > 1 do
  333.          n -:= 1
  334.       i := 0
  335.       every p := !sort(pieces) do {
  336.          i := (i + n) % *pieces
  337.          pieces[i + 1] := p
  338.          }
  339.       }
  340.    else
  341.       every i := *pieces to 2 by -1 do
  342.          pieces[?i] :=: pieces[i]
  343.  
  344.    empty(1)
  345.    every place(pieces[i := 1 to *pieces], i)
  346.    status("mix")
  347.    return
  348. end
  349.  
  350.  
  351.  
  352. #  order() -- sort the first shelf in descending order
  353. #
  354. #  if shifted, sort ascending
  355.  
  356. procedure order()
  357.    local i
  358.  
  359.    pieces := sort(pieces)
  360.    if not &shift then
  361.       every i := 1 to *pieces / 2 do    # change from ascending to descending
  362.          pieces[i] :=: pieces[-i]
  363.  
  364.    empty(1)
  365.    every place(pieces[i := 1 to *pieces], i)
  366.    status("sort")
  367.    return
  368. end
  369.  
  370.  
  371.  
  372. #########################  packing algorithms  #########################
  373.  
  374.  
  375.  
  376. #  pack(x, v) -- execute packing algorithm connected with button x
  377.  
  378. procedure pack(x, v)
  379.    local l, n, s, i
  380.  
  381.    if x.ax = BX2 then {
  382.       l := sort(pieces)            # if second-column button, sort first
  383.       every i := 1 to *l/2 do        # change from ascending to descending
  384.          l[i] :=: l[-i]
  385.       }
  386.    else
  387.       l := copy(pieces)
  388.  
  389.    n := x.ay / DY + 1            # compute shelf number
  390.    empty(n)                # clear the shelf
  391.  
  392.    s := x.id(l)                # call packing algorithm
  393.    status(\s | x.s)            # display status
  394.    return
  395. end
  396.  
  397.  
  398. #  nextfit(l) -- pack using next-fit algorithm
  399.  
  400. procedure nextfit(l)
  401.    local p
  402.  
  403.    every p := !l do
  404.       place(p, nfilled | nfilled + 1)
  405.    return
  406. end
  407.  
  408.  
  409. #  nextk(l) -- pack using next-k-fit algorithm
  410.  
  411. procedure nextk(l)
  412.    local p
  413.  
  414.    every p := !l do
  415.       if nfilled <= kvalue then
  416.          place(p, 1 to nfilled + 1)
  417.       else
  418.          place(p, nfilled - kvalue + 1 to nfilled + 1)
  419.    return "N" || kvalue
  420. end
  421.  
  422.  
  423. #  firstfit(l) -- pack using first-fit algorithm
  424.  
  425. procedure firstfit(l)
  426.    local p
  427.  
  428.    every p := !l do
  429.       place(p, 1 to nfilled + 1)
  430.    return
  431. end
  432.  
  433.  
  434. #  lastfit(l) -- pack using last-fit algorithm
  435.  
  436. procedure lastfit(l)
  437.    local p
  438.  
  439.    every p := !l do
  440.       place(p, (nfilled to 1 by -1) | (nfilled + 1))
  441.    return
  442. end
  443.  
  444.  
  445. #  bestfit(l) -- pack using best-fit algorithm
  446.  
  447. procedure bestfit(l)
  448.    local p, b, i, max, found
  449.  
  450.    every p := !l do {
  451.       max := FULL - p            # fullest acceptable bin size
  452.       found := 0            # size of best bin found so far
  453.       b := nfilled + 1            # index of where found
  454.       every i := 1 to nfilled do
  455.          if found <:= (max >= bin[i]) then
  456.             b := i
  457.       place(p, b)            # place in best bin found
  458.       }
  459.    return
  460. end
  461.  
  462.  
  463. #  worstfit(l, n) -- pack using worst-fit algorithm
  464.  
  465. procedure worstfit(l, n)
  466.    local p, b, i, found
  467.  
  468.    every p := !l do {
  469.       found := FULL - p            # size of best bin found so far
  470.       b := nfilled + 1            # index of where found
  471.       every i := 1 to nfilled do
  472.          if found >:= bin[i] then
  473.             b := i
  474.       place(p, b)            # place in best bin found
  475.       }
  476.    return
  477. end
  478.  
  479.  
  480. #  nearworst(l, n) -- pack using almost-worst-fit algorithm
  481.  
  482. procedure nearworst(l, n)
  483.    local p, a, b, i, found
  484.  
  485.    every p := !l do {
  486.       found := FULL - p            # size of best bin found so far
  487.       a := b := &null
  488.       every i := 1 to nfilled do
  489.          if found >:= bin[i] then {
  490.             a := b
  491.             b := i
  492.             }
  493.       place(p, \a | \b | (nfilled + 1))    # place in second-best bin found
  494.       }
  495.    return
  496. end
  497.  
  498.  
  499. #  harmonic(l, n) -- pack using (unmodified) harmonic algorithm
  500.  
  501. procedure harmonic(l, n)
  502.    local curr, maxv, i, p, b
  503.  
  504.    curr := list(kvalue)            # current bin for each class
  505.    maxv := list(kvalue)            # maximum for each class
  506.    every i := 1 to kvalue do
  507.       maxv[i] := FULL / (kvalue - i + 1)
  508.  
  509.    every p := !l do {
  510.       p <= maxv[i := 1 to kvalue]    # find class index i
  511.       b := curr[i]
  512.       if /b | (bin[b] + p > FULL) then
  513.          place(p, curr[i] := nfilled + 1)
  514.       else
  515.          place(p, b)
  516.       }
  517.    return "H" || kvalue
  518. end
  519.  
  520.  
  521. #  gxfit(l, n) -- pack using group-x(k)-fit algorithm
  522.  
  523. procedure gxfit(l, n)
  524.    local stk, maxv, i, s, p, b, d
  525.  
  526.    stk := []                # stacks of bins, one for each group
  527.    maxv := []                # maximum for each group
  528.  
  529.    # make k equally sized groups
  530.    d := FULL / kvalue
  531.    every i := 1 to kvalue do {
  532.       put(stk, [])
  533.       put(maxv, i * d - 1)
  534.       }
  535.  
  536.    every p := !l do {
  537.       # find group index i for piece
  538.       (p <= maxv[i := (1 to kvalue) | 0]) & (*stk[i] > 0)
  539.       b := pop(stk[i]) | (nfilled + 1)
  540.       place(p, b)
  541.       # now put bin back on a stack, if not too full
  542.       if (FULL - bin[b]) >= maxv[i := (kvalue - 1 to 1 by -1)] then
  543.          push(stk[i], b)
  544.       }
  545.    return "G" || kvalue
  546. end
  547.  
  548.  
  549.  
  550. #########################  event miscellany  #########################
  551.  
  552.  
  553.  
  554. #===<<vib:begin>>===    modify using vib; do not remove this marker line
  555. procedure ui(win, cbk)
  556. return vsetup(win, cbk,
  557.    [":Sizer:lucidasanstypewriter-bold-12::0,0,860,675:Bin Packing",],
  558.    ["bins:Slider:h::10,48,100,15:0,100,40",setbins],
  559.    ["infob:Button:regular::10,111,40,17:info",info],
  560.    ["kval:Slider:h::10,135,100,15:0,100,30",setk],
  561.    ["max:Slider:h::10,10,100,15:1,20,20",setmax],
  562.    ["mix:Button:regular::10,68,30,17:mix",mix],
  563.    ["new:Button:regular::80,68,30,17:new",reload],
  564.    ["quit:Button:regular::70,110,40,17:quit",quit],
  565.    ["sort:Button:regular::10,87,35,17:sort",order],
  566.    )
  567. end
  568. #===<<vib:end>>===    end of section maintained by vib
  569.  
  570.  
  571.  
  572. #  leftover() -- handle events that fall outside the vidgets
  573. #
  574. #  Exits when certain keys are pressed and ignores other events.
  575.  
  576. procedure leftover(e)
  577.    case e of {
  578.       QuitEvents():    exit()
  579.       &meta & !"nN":    reload()
  580.       &meta & !"mM":    mix()
  581.       &meta & !"sS":    order()
  582.       &meta & !"iI":    info()
  583.       }
  584.    return
  585. end
  586.  
  587.  
  588. #  quit() -- handle "quit" button press
  589.  
  590. procedure quit(x, v)
  591.    exit()
  592. end
  593.  
  594.  
  595. #  info() -- handle "info" button press
  596.  
  597. procedure info(x, v)
  598.    static text
  599.    initial {
  600.       text := ["",
  601.          Version,
  602.          "by Gregg Townsend, The University of Arizona",
  603.          "",
  604.          "",
  605.          "Glossary:",
  606.          ""]
  607.       every put(text, "   " || !glossary)
  608.       put(text,
  609.          "",
  610.          "A `D' suffix indicates a variation where the input is sorted",
  611.          "in descending order before applying the algorithm.",
  612.          "",
  613.          "",
  614.          "For more information about bin packing algorithms, see:",
  615.          "",
  616.          "   `Approximation Algorithms for Bin-Packing -- An Updated Survey'",
  617.          "   by E.G. Coffman, Jr., M.R. Garey, and D.S. Johnson, in",
  618.          "   Algorithm Design for Computer System Design, ed. by",
  619.          "   Ausiello, Lucertini, and Serafini, Springer-Verlag, 1984",
  620.          "",
  621.          "   `Fast Algorithms for Bin Packing' by David S. Johnson,",
  622.          "   Journal of Computer and System Sciences 8, 272-314 (1974)",
  623.          "")
  624.       }
  625.    Notice ! text
  626.    return
  627. end
  628.