Option Explicit
Const 収束判定値 = 0.01
Const NMAX = 10 '均等再配置回数
Sub reAllocate(範囲セル As String, 条件セル As String, 最小化セル As String)
' 最適化 - 「条件セル」の値が0の状態を維持しながら、
' 「最小化セル」の値が最小になるようにセル値を再配置
Application.ScreenUpdating = False '画面更新停止 - 速度向上な為
'----------------------
'変数
'----------------------
Dim rngShift As Range '選択範囲
Dim SigmaSum As Double '分散計
Set rngShift = Range(範囲セル)
'----------------------
'作業変数
'----------------------
Dim c1 As Integer, r1 As Integer, r2 As Integer
Dim n As Long
' Dim blRc As Boolean
Dim tmpSigmaSum As Double
SigmaSum = Range(最小化セル)
For n = 1 To NMAX '繰り返し回数
For c1 = 1 To rngShift.Columns.Count '範囲の列数
Application.StatusBar = "最適化中…" & c1 & "/" & rngShift.Columns.Count & " " & n & "/" & NMAX 'ステータスバーに進行状況表示
For r1 = 1 To rngShift.Rows.Count '範囲の行数
If rngShift(r1, c1) <> 0 Then '勤務?
For r2 = 1 To rngShift.Rows.Count
If rngShift(r1, c1) <> rngShift(r2, c1) Then '2つのセル値が異なる?
Call MySwap(rngShift(r1, c1), rngShift(r2, c1)) 'セル値を入れ替えて見る
If rngShift(r1, c1) = 0 Or rngShift(r2, c1) = 0 Then
'片方が休み
If IsChangeNext(rngShift, c1, r1, r2, SigmaSum, Range(最小化セル), Range(条件セル)) = False Then
'2つ目交換不可、または値の更新無しの場合
Call MySwap(rngShift(r1, c1), rngShift(r2, c1)) '戻す
End If
Else
'両方が勤務
If Range(条件セル) = 0 Then '交換可
Dim tmp
tmp = Range(最小化セル).Value
' If Not IsNumeric(tmp) Then GoTo MyENd
If tmp < SigmaSum Then '値更新?
SigmaSum = tmp '新たな最小値とする
Else
Call MySwap(rngShift(r1, c1), rngShift(r2, c1)) '戻す
End If
Else
Call MySwap(rngShift(r1, c1), rngShift(r2, c1)) '戻す
End If
End If
End If
Next r2
End If
Next r1
Next c1
If Abs(tmpSigmaSum - SigmaSum) < 収束判定値 Then Exit For '収束
tmpSigmaSum = SigmaSum
Next n
'--------------------------------------
'完了
'--------------------------------------
MyENd:
Application.StatusBar = ""
Application.ScreenUpdating = True '画面更新再開
End Sub
Private Function IsChangeNext(rngShift As Range, c As Integer, r1 As Integer, r2 As Integer, _
ByRef evalMin As Double, _
dsprCell As Range, cndtCell As Range) As Boolean
'---------------------------------------------------
' 処理
' 別の日の同一行(r1,r2)を交換して見て
' 交換可能の場合
' 分散値最低を比較し更新:交換、更新無:元の状態
' evalMin:分散値を返す リターン値:TRUE
' 交換不可の場合
' evalMin:元の値 リターン値:FALSE
'---------------------------------------------------
' 入力
' rngShift:対象シフト範囲
'
' 1つ目の交換
' c :列
' r1:行1
' r2:行2
' evalMin:現在の最低値 参照渡し変数
' dsprCell:評価分散値セル
' cndtCell:条件セル
'
' 戻り値
' True :交換可 2つ目:交換済み
' False:交換不可 2つ目:元の状態
IsChangeNext = False '既定値:交換不可
Dim c2 As Integer
Dim tmp
For c2 = 1 To rngShift.Columns.Count
If c2 <> c Then '別の列(日)?
If rngShift.Cells(r1, c) = 0 And rngShift.Cells(r2, c2) <> 0 Then 'c2 r1:休み r2:勤務?
Call MySwap(rngShift(r1, c2), rngShift(r2, c2)) '2つ目を交換
If Val(cndtCell.Value) <> 0 Then '条件を満たさない?
Call MySwap(rngShift(r1, c2), rngShift(r2, c2)) ' → 戻す
Else
tmp = CDbl(dsprCell.Value) '評価値
If tmp < evalMin Then '値更新?
evalMin = tmp '新たな最小値とする
IsChangeNext = True '交換可!
Else
Call MySwap(rngShift(r1, c2), rngShift(r2, c2)) '更新しない → 戻す
End If
End If
End If
End If
Next c2
End Function
Sub randomReAllocate(範囲セル As String, 条件セル As String, 回数 As Integer)
' ランダム入替
'
' 処理:「条件セル」の値を0に維持しながら「範囲セル」の値をランダムに入れ替える
Application.ScreenUpdating = False '画面更新停止
'----------------------
'変数
'----------------------
Dim rngShift As Range '選択範囲
Set rngShift = Range(範囲セル)
'----------------------
'開始
'----------------------
Dim r, c, r2 As Long
Dim n As Long
For n = 1 To 回数 '繰り返し回数
Application.StatusBar = "ランダム入替中…" & n & "/" & 回数 'ステータスバーに進行状況表示
For c = 1 To rngShift.Columns.Count '選択範囲の列数
For r = 1 To rngShift.Rows.Count '選択範囲の行数
For r2 = r + 1 To rngShift.Rows.Count
Call MySwap(rngShift(r, c), rngShift(r2, c)) 'セル入れ替え
If Val(Range("条件_調整").Value) <> 0 Then '条件を満たさない?
Call MySwap(rngShift(r, c), rngShift(r2, c)) ' →戻す
End If
Next r2
Next r
Next c
Next n
'--------------------------------------
'完了
'--------------------------------------
Application.StatusBar = ""
Application.ScreenUpdating = True '画面更新再開
End Sub
Private Sub MySwap(Rng1 As Range, Rng2 As Range)
'-----------------
'セルデータ入替
'-----------------
Dim tmp As Variant
tmp = Rng1.Value
Rng1.Value = Rng2.Value
Rng2.Value = tmp
End Sub
Public Sub 予約転記()
'
' 予約勤務を数値に変換して「変化させるセル」に転記
'
Application.ScreenUpdating = False '画面更新停止
Dim r As Integer, c As Integer
Dim Src
Src = Range("予約勤務")
With Range("変化させるセル")
For r = 1 To .Rows.Count
For c = 1 To .Columns.Count
If CStr(Src(r, c)) <> "" Then
.Cells(r, c) = CStr(Src(r, c))
End If
Next c
Next r
End With
Application.ScreenUpdating = True '画面更新再開
End Sub
'------------------------------------------
' サブルーチン
'------------------------------------------
Public Sub SetKinmuCellColor(範囲 As Range)
'
' 指定範囲を勤務に応じたセル色にセット
'
Application.ScreenUpdating = False '画面更新停止
Dim tmp As Range
For Each tmp In 範囲
tmp.Interior.Color = KinmuColor(CStr(tmp.Value))
tmp.Font.Color = KinmuFontColor(CStr(tmp.Value))
Next
Application.ScreenUpdating = True '画面更新再開
End Sub