[[20140912095905]] 『最適な箱数を求めたい』(ひな) ページの最後に飛ぶ

[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]

 

『最適な箱数を求めたい』(ひな)

関数またはマクロで、以下の条件で最適な箱数を
求めたいと思います。どなたかご教授頂けますでしょうか。

 1ロット300m巻のロープがあり、そこからランダムな長さを
 切り出していった場合、何箱必要かを求めたいです。

 例えば、
  A    B    C    D    E    F 
 150  100   80  180  140   50
  30  250  120   50  100   90

 と、表に値があります。1本のロープは途中で切ったりせず、
 効率の良い組み合わせで切っていった場合に、何箱必要と
 なるのかを算出したいです。

 A列とかB列などのグループは全く気にする必要はありません。
 上記の場合、全ての値を加算した長さは1340mとなりますが、
 300m巻の1本のロープからA1の150+B1の100を切り出した
 残りが50mなので、D2の50mを切り出すと丁度1箱となります。

 このように計算していくと、上記すべてを切り出すには
 1340÷300=4.46で5箱が必要とはなりますが、余ったロープの
 長さによっては5箱とは限らなくなります。
 実際には上記のような切りの良い長さではないからです。

 解りづらい説明で申し訳ありませんが、何か良い計算式は
 無いでしょうか。  
 よろしくお願い致します。

< 使用 Excel:Excel2007、使用 OS:Windows7 >


解を求めるためのアプローチ方法を考えてみました(あっているのかどうかわかりませんが)。

0.カットする長さを降順に並べる
1.最長のものに、次に長いものを加えて300以下になるかどうかを判定する
2.300以下だったら次に長いものを加えて300以下になるかを判定する
3.1〜2を繰り返し、300を超えたら、直前までの加算値とその組み合わせで1箱とする
4.3の結果残ったもので、1〜3を繰り返す

たぶんこの方法をVBAで実現させるといいような気がします。

ちなみに質問者のサンプルで上記アプローチで計算すると5箱と求められますが、これが最適解
なのかどうかはわかりません。

(ryopo^2) 2014/09/12(金) 10:39


[[20110117005147]]
 これ参考になりますかね?
 最適は難しい問題だと思います(私には無理)
(稲葉) 2014/09/12(金) 10:42

 質問とは全く関係ないですが。

 「箱数」でピクッとなってしまったので関係ないですがリンクします。

http://ja.wikipedia.org/wiki/Wikipedia:%E4%BA%95%E6%88%B8%E7%AB%AF/subj/%E3%80%8C%E9%96%A2%E6%95%B0%E3%80%8D%E3%81%8B%E3%80%8C%E5%87%BD%E6%95%B0%E3%80%8D%E3%81%8B
(カリーニン) 2014/09/12(金) 10:48


 ryopo^2さん、稲葉さん、カリーニンさん(?)、
 お返事ありがとうございます!

 ryopo^2さんの方法でVBAを作れれば、最適な気がします。
 また、稲葉さんが貼ってくださったリンクの「ソルバー」は、
 使えるのでしょうか?2007ですと何処になるのかわかりません・・。

(ひな) 2014/09/12(金) 10:59


 ソルバーをインストールしてみたのですが、
 私には扱えそうもありません・・。

 勝手を申しますが、どなたかryopo^2さんの方法でVBAを
 作って頂けないでしょうか?

(ひな) 2014/09/12(金) 11:18


 簡単なパターンを考えてみます。

 100m巻から、90,80,70,30,20,10 の長さを切り出すとします。
 単純に上から順に組み合わせていくと
             90,80,70,30,20,10
       ● ◇ ▲ ▲ ○ ○ ・・・・4巻必要で、あまりが70m
 最適は   90,10 80,20 70,30 と組み合わせて、
             ● ● ▲ ▲ ○ ○ ・・・・3巻必要で、あまりが 0m

 ソルバーを扱えるようになるのが良いと思います。
   
(HANA) 2014/09/12(金) 11:37

 HANAさん、ありがとうございます!

 ソルバーで出来そうな気がします。
 目的セル、目標値、変化させるセル、制約条件と、
 指定する内容が分かりかねています。

 もう少し頑張ってみます。

(ひな) 2014/09/12(金) 11:42


VBA例。 A列に、縦に各々の長さを並べてください。降順で並べ替えておくこと。

 Private Sub test()
    Const NUMMAX = 300
    Dim i As Long
    Dim j As Long
    Dim iMax As Long
    Dim iNum As Long
    Dim iAll As Long
    Dim iTest As Long
    Dim iDim() As Long

    iNum = 1
    iMax = Cells(Rows.Count, "A").End(xlUp).Row
    ReDim iDim(iMax - 1, 1)
    For i = 1 To iMax
        iDim(i - 1, 0) = Cells(i, "A").Value
    Next i

    Columns(2).ClearContents

    For i = 0 To iMax - 1
        If iDim(i, 1) = 0 Then
            iAll = iDim(i, 0)
            iDim(i, 1) = iNum
            For j = i + 1 To iMax - 1
                If iDim(j, 1) = 0 Then
                    iTest = iAll + iDim(j, 0)
                    If iTest <= NUMMAX Then
                        iDim(j, 1) = iNum
                        iAll = iTest
                    End If
                End If
            Next j
            iNum = iNum + 1
        End If
    Next i

    For i = 1 To iMax
        Cells(i, "B").Value = iDim(i - 1, 1)
    Next i

    MsgBox iNum - 1 & "箱必要です", vbInformation, "計算終了"
End Sub
(???) 2014/09/12(金) 12:35

「ひな」さん、ソルバーの使い方でこんなのありましたのでご参考まで。
 http://www-cc.gakushuin.ac.jp/~e931039/solver.htm

「???」さん、思考をVBA化してくださってありがとうございます。

(ryopo^2) 2014/09/12(金) 12:59



 皆様、本当にありがとうございます!!

 ???さんに作って頂いたVBAで必要数が算出できました。
 ありがとうございます。

 先ほどvarumさんが少しの間UPしてくださったレスのように、
 最後に書かれていた最適解のように
 切残しが把握できれば・・・と思っているところです。

 もう少し考えてみます!
(ひな) 2014/09/12(金) 13:33

 ひなさんと更新のタイミングが一致したようで
 無効になったため再度upします。

 次の記事が参考になると思います。

 材料の切断について
http://www.excel.studio-kazu.jp/kw/20120410160047.html

 この中で提示されているコードがおそらく役立つでしょう。
 少し難しいケースですと最適解が得られないこともありますが
 自分でロジックを考えて作るよりもはるかに楽なのは確か。
 必要ならもっと改善すればよろしいかと。

 もう1つ紹介しておきます。英語のブログですが、コードは関係なく
 利用できます。

 Cutting Stock Lengths
http://dailydoseofexcel.com/archives/2005/09/12/cutting-stock-lengths/

 結果がメッセージボックスに表示されるので、その部分のコードを
 修正した方が使い易いかも。

 最後に提示されたサンプルの解について

 [ 250 ,  50 ]		切残し 0
 [ 180, 120 ]		切残し 0
 [ 150, 100, 50 ]	切残し 0
 [ 100, 90, 80, 30 ]    切残し 0
 [ 140 ]                切残し 160

 以上の5箱が最適解です。

 それから箱数だけではあまり役に立たないです。
 切り方によって箱数が変わるから、カッティング・パターンは必須です。

(varum) 2014/09/12(金) 13:53


 varumさんスミマセン、お手数掛けます。

 仰るように、カッティング・パターンを把握できればと
 思っていたところでした。

 参考にさせて頂きます。
 誠にありがとうございました!

(ひな) 2014/09/12(金) 14:15


 後で思い出しました。
 切断長さの種類が10までならば下記のようなものがあります。

 On-Demand  Cutting Optimization Solution
http://www.cutsolution.com/CutSolution/jsp/freeTrial.jsp

 Web上で計算して結果を知らせてくれます。
 ただし、変なソフトの広告画面が現れるときがあるので要注意。

 上部のメニューバーの左から4つ目の"Free Trial"をマウスで
 クリックすると、データの入力画面に切り替わる。
 特に、変なソフトの広告が出る前にこの操作をするのがよい。

 Available Stock Length   材料長さを入力
 Length of demand         切断長さを入力
 No of ....(Frequency)    必要数

 1つの値を入力した後、Tab キーを押すと次の項目の枠に移動する
 ので便利です。
 全てのデータを入力後に、[Cutting Plan]のボタンをクリックすると
 すぐに結果をテキストファイルで返信してくれる。

 因みに、以下は提示されたサンプルを計算した結果をコピーしたもの。
 結果は悪くありません。

 言うまでもないけど、ご自身の責任で使ってください。

 =================== Length of available stock =============
 300
 =================== Original Length of rolls ==============
    250   180   150   140   120   100    90    80    50    30
 =================== Original Demand of rolls ==============
      1     1     1     1     1     2     1     1     2     1
 =================== Merged Length of rolls ================
    250   180   150   140   120   100    90    80    50    30
 =================== Merged Demand of rolls ================
      1     1     1     1     1     2     1     1     2     1
 =================== Cutting Patterns ======================
    <Rolls>  <Wastage in one> <TotalWastage>    Cutting Pattern (Length)Count  Length(Count)..
          1                 0              0     (250)1     (50)1
          1                 0              0     (180)1     (120)1
          1                10             10     (150)1     (140)1
          1                10             10     (100)2     (90)1
          1               140            140     (80)1     (50)1     (30)1
 =================== Analysis of wastage ===================
 Total Roll Required 	5
 % Trim 	10.6667

(varum) 2014/09/12(金) 16:19


 varumさん、ご親切にありがとうございます。

 ソフト活用も検討してみます。

(ひな) 2014/09/12(金) 16:44


 以前、
[[20120308214011]]
 ここで作成したものをちょっと変更したものです。

 新規ブックにて(Sheet1とSheet2というシートが存在する)、

 標準モジュール(Module1)に
 サンプルデータ作成コード

 '===================================================================================
 Option Explicit
 Sub レイアウト()
    With Worksheets("sheet1")
       .Range("a:h").ColumnWidth = 14
       .Range("e2:h2").Value = Array("一巻のM数", 300, "確認インターバル", #12:05:00 AM#)
       .Range("a1:b12").Value = Evaluate("{1,150;2,100;3,80;4,180;5,140;6,50;7,30;8,250;9,120;10,50;11,100;12,90}")
    End With
 end Sub

 標準モジュール(Module2)に組合せリスト作成プログラム

 '=========================================================================
 Option Explicit
    Private c_svsn As Long '抜き取り数保存
    Private c_svsmpn As Long '標本数保存
    Private c_idx() As Long '配列のカレントポインタ
    Private cs_x() As Long '配列の基盤ポインタ
    Private c_eof As Boolean
 '=========================================================================
 Function init_comb(smpnum As Long, seln As Long) As Double
    '組合せ処理ルーチンの初期化
    'Input : smpnum  標本数 seln 抜き取り数
    'output : init_comb 組合せ数
    Dim g0 As Long
    c_svsn = seln
    c_svsmpn = smpnum
    Erase c_idx()
    Erase cs_x()
    g0 = 1
    ReDim cs_x(1 To seln)
    ReDim c_idx(1 To seln)
    For g0 = 1 To UBound(c_idx())
      cs_x(g0) = g0
      c_idx(g0) = g0
    Next
    c_idx(UBound(c_idx())) = c_idx(UBound(c_idx())) - 1
    init_comb = WorksheetFunction.Combin(smpnum, seln)
    c_eof = False
 End Function
 '=========================================================================
 Function get_comb(ans()) As Long
    '組合せリストのインデックスを配列として返す
    'input : なし
    'output: ans() 組合せリストのインデックスを格納する
    '        get_comb  0:正常に取得  1 リストの終わり
    Dim g0 As Long
    Dim g1 As Long
    get_comb = 1
    If c_eof Then Exit Function
    For g0 = UBound(c_idx()) To LBound(c_idx()) Step -1
       If c_idx(g0) + 1 <= c_svsmpn - c_svsn + g0 Then
          c_idx(g0) = c_idx(g0) + 1
          get_comb = 0
          Exit For
       Else
          c_idx(g0) = cs_x(g0) + 1
          cs_x(g0) = cs_x(g0) + 1
          For g1 = g0 + 1 To UBound(cs_x())
             cs_x(g1) = cs_x(g1 - 1) + 1
             c_idx(g1) = cs_x(g1)
          Next g1
       End If
    Next
    If get_comb = 0 Then
       For g0 = LBound(c_idx()) To UBound(c_idx())
          ans(g0) = c_idx(g0)
       Next
    Else
       c_eof = True
    End If
 End Function
 '=========================================================================
 Sub close_comb()
    '組合せ処理ルーチンの終了処理
    Erase c_idx()
    Erase cs_x()
 End Sub
 '=========================================================================
 Function skip_comb(i_num As Long, ans() As Variant) As Long
    '指定したインデックスを一つ増加させる
    'input: i_num:増加させるインデックス
    'output: ans()組合せリストのインデックスを格納する
    '        skip_comb   0:正常に取得  1 リストの終わり
    Dim g0 As Long
    Dim g1 As Long
    skip_comb = 1
    g1 = c_svsmpn
    If c_eof Then Exit Function
    For g0 = UBound(c_idx()) To i_num + 1 Step -1
      c_idx(g0) = g1
      g1 = g1 - 1
    Next
    skip_comb = get_comb(ans())
 End Function
 Function set_comb(setarray(), ans()) As Long
    'setarray()で指定された組合せインデックスをカレント設定する
    'input  setarray()  カレントに設定するインデックスの配列
    'Output ans()       組合せリストのインデックスを格納する
    'set_comb    0:正常に取得  1 既に指定インデックス、または越えています
    Dim g0 As Long
    Dim ret As Long
    Dim sidx As Long
    Dim skipsw As Long  '0 skip_combを最低一回は、呼び出している  1 skip_combを一回も呼び出していない
    Dim neqsw As Long   '0 設定インデックスがインデックス限界値内にある 1 設定インデックスがインデックス限界値を越えた
    ReDim sv(1 To c_svsn)
    set_comb = 1
    skipsw = 1
    neqsw = 0
    For g0 = LBound(setarray()) To UBound(setarray())
       If c_idx(g0) > setarray(g0) Then
          Exit For
       Else
          sidx = setarray(g0)
          If sidx > c_svsmpn - c_svsn + g0 Then
             neqsw = 1
             sidx = c_svsmpn - c_svsn + g0
          End If
          Do Until c_idx(g0) = sidx
             ret = skip_comb(sidx, sv())
             skipsw = 0
          Loop
          If neqsw = 1 Then Exit For
       End If
    Next
    If skipsw = 0 Then
       For g0 = LBound(sv()) To UBound(sv())
          ans(g0) = sv(g0)
       Next
       set_comb = 0
    End If
    Erase sv()
 End Function

 標準モジュール(Module3)に今回の仕様遂行コード
 '======================================================================
 Sub main()
    Dim 組合せ As Variant
    Dim rw As Long
    Dim rng As Range
    Dim g0 As Long
    [e5:h100000].Clear
    rw = 5
    Set rng = Range("a1", Cells(Rows.count, "a").End(xlUp)).Resize(, 2)
    rng.Interior.ColorIndex = xlNone
    rng.Sort Key1:=Range("b1"), Order1:=xlDescending, Header:=xlNo
    Do While get_合計組合せ(rng, Range("f2").Value, Range("h2").Value, 組合せ, Worksheets("sheet2"))
          For g0 = LBound(組合せ) To UBound(組合せ)
             Cells(rw, g0 + 4).Value = 組合せ(g0)
          Next
          rw = rw + 1
          DoEvents
    Loop
    Set rng = Range("a1", Cells(Rows.count, "a").End(xlUp)).Resize(, 2)
    rng.Sort Key1:=Range("a1"), Order1:=xlAscending, Header:=xlNo
    With Range("a:c")
       .Interior.ColorIndex = xlNone
       .Columns(3).Clear
    End With
    Worksheets("sheet2").Range("a:b").Clear
 End Sub
 '================================================================================
 Function get_合計組合せ(ByVal rng As Range, ByVal 解 As Long, ByVal inttm As Variant, 組合せ As Variant, ByVal wksht As Worksheet) As Boolean
    Dim 有効値 As Long
    Dim Tdata() As Variant
    Dim trw() As Variant
    Dim snum As Long
    Dim nuki As Long
    Dim g0 As Long
    Dim g1 As Long
    Dim g2 As Long
    Dim ret As Long
    Dim asum As Long
    Dim tm As Double
    Dim Mret As Long
    Dim bestsum As Long
    Dim setsw As Long
    Dim sv() As Variant
    Dim ans()
    Dim cmb() As Variant
    Dim crng As Range
    get_合計組合せ = False
    Mret = vbOK
    rng.Copy wksht.Range("a1")
    g0 = 0
    For Each crng In rng.Offset(0, 1).Resize(, 1)
       With crng
          If .Interior.ColorIndex = xlNone Then
             ReDim Preserve Tdata(1 To g0 + 1)
             ReDim Preserve trw(1 To g0 + 1)
             Tdata(g0 + 1) = .Value
             trw(g0 + 1) = .Row
             g0 = g0 + 1
          End If
       End With
    Next
    If g0 <= 0 Then Exit Function
    snum = UBound(Tdata)
    asum = 0
    For g0 = UBound(Tdata) To LBound(Tdata) Step -1
      asum = asum + Tdata(g0)
      If asum > 解 Then
         snum = UBound(Tdata) - g0
         Exit For
      End If
    Next
    bestsum = 解
    有効値 = 0
    tm = [now()]
    For g0 = 1 To snum
       Call init_comb(UBound(Tdata), g0)
       ReDim ans(1 To g0)
       ReDim cmb(1 To g0)
       ret = 1
       If setsw = 1 Then
          ret = set_comb(sv(), ans())
       End If
       ReDim sv(1 To g0)
       setsw = 0
       If ret <> 0 Then ret = get_comb(ans())
       Do While ret = 0
          asum = 0
          For g1 = LBound(ans()) To UBound(ans())
             asum = asum + Tdata(ans(g1))
             cmb(g1) = Tdata(ans(g1))
             If asum > 解 Then Exit For
          Next
          nuki = g1
          If asum <= 解 And setsw = 0 Then
             For g1 = LBound(ans()) To UBound(ans())
                sv(g1) = ans(g1)
             Next
             setsw = 1
          End If
          If nuki >= UBound(ans()) Then
             If asum < 有効値 Then
                nuki = chk_seq(ans())
                If nuki > 0 Then
                   ret = skip_comb(nuki, ans())
                Else
                   Exit Do
                End If
             Else
                If asum >= 有効値 And asum <= 解 Then
                   wksht.Range(rng.Address).Copy rng.Cells(1, 1)
                   For g1 = LBound(ans()) To UBound(ans())
                      rng.Cells(trw(ans(g1)), 1).Resize(, 2).Interior.ColorIndex = 4
                   Next
                   get_合計組合せ = True
                   組合せ = cmb()
                   Cells(g2 + 1, 3).Value = UBound(ans()) & "件     合計 :   " & asum
                   g2 = g2 + 1
                   有効値 = asum
                   tm = [now()]
                End If
                If asum = 解 Then
                   Mret = vbCancel
                   Exit Do
                End If
                ret = get_comb(ans())
             End If
         Else
             ret = skip_comb(nuki, ans())
         End If
         If [now()] - tm > inttm Then
            Mret = MsgBox("抜き出し " & g0 & "件  実行中 続けますか?", vbOKCancel)
            tm = [now()]
            If Mret = vbCancel Then get_合計組合せ = False: Exit Do
         End If
       Loop
       Call close_comb
       If Mret = vbCancel Then Exit For
    Next
    Erase ans()
    Erase Tdata
    Erase trw()
    Erase cmb()
 End Function
 '==========================================
 Function chk_seq(ans()) As Long
    chk_seq = 0
    Dim g0 As Long
    For g0 = UBound(ans()) To LBound(ans()) + 1 Step -1
       If ans(g0) <> ans(g0 - 1) + 1 Then
          chk_seq = g0 - 1
          Exit For
       End If
    Next
 End Function

 以上です。sheet2は、手抜きロジックなので、プログラムが使っていますから、
 用意だけしておいてください。

 まず、Sheet1をアクティブにした状態で レイアウト というプロシジャーを
 実行してください。

 Sheet1のA列がA1から連番、B列にB1からB2,B3と切り出しメートルデータ、

 セルF2に1巻のメートル数、H2にプログラムが長い場合、動作中断確認をする時間を示します。

 A列、B列のデータは、適当に変更して見てください。

 mainを実行してみてください。

 E5から、組み合わせが表示されます。

 サンプルデータだと

     250    50		
     180   120		
     150   100    50	
     100    90    80    30
     140

 こんな結果が得られました。			

(ichinose) 2014/09/12(金) 20:26


コメント返信:

[ 一覧(最新更新順) ]


YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki. Modified by kazu.