ページ移動:JavaScriptを有効にして下さい!
11.変化するセル範囲を可変

ここでの改良点

・変化させるセル範囲を、入力氏名数に応じて可変

方法:

  名前「変化させるセル」の範囲を固定ではなく下記のような数式で設定

  =OFFSET(変化セル範囲可変!$D$44,0,0,行数,列数)

  式の意味
     変化させるセル範囲の左上のセルを起点にし、
        氏名が入力されている分を行数、
        日付が設定されている分を列数
     として、範囲を設定する。
     行数
列数は名前として定義。

  これにより、氏名の追加/削除でも、変化させるセル範囲は自動で調整

  名前の使い方については、「名前の動的定義」を参照


コンパイルエラー発生時

全自動実行

  SolverSolve関数のパラメータ指定により、開始ダイアログを表示せず均等再配置まで全自動で実行
  中止は{ESC}キーを押下

その他

DialogBoxボタン追加:ソルバーのダイアログボックスを表示 SolverOkDialog関数



・中止ボタン撤去
  {ESC}キー押下状態により判定

・連続不可勤務リストに「-」追加
  休みを記号「-」で表す。
  休み 休み 遅出 のような連続勤務の禁止も指定可

・過不足計へのリンク表示を画面上部に設定



定義変数名


SolverSolve関数

SolverSolve(UserFinish, ShowRef) UserFinish 省略可能です。バリアント型 (Variant) の値を使用します。
  True を指定すると、[ソルバー : 探索結果] ダイアログ ボックスを表示せずに結果を返します。
  False を指定するか省略すると、結果を返し、[ソルバー : 探索結果] ダイアログ ボックスを表示します。
ShowRef 省略可能です。バリアント型 (Variant) の値を使用します。ShowRef 引数として、マクロの名前 (文字列) を渡すことができます。
戻り値
0   ソルバーによって解が見つかりました。すべての制約条件と最適化条件を満たしています。
1   ソルバーによって現在の解に収束されました。すべての制約条件を満たしています。
2   現在の解の精度を上げることはできません。すべての制約条件を満たしています。
3   反復回数が上限に達し、処理が中止されました。
4   目的セルの値が収束しません。
5   実行可能解が見つかりませんでした。
6   ユーザーの要求によってソルバーが停止しました。
7   この LP ソルバーに必要な線形条件が満たされていません。
8   問題が大きすぎるため、ソルバーで処理できません。
9   目的セルまたは制約条件セルの値でエラーが発生しました。
10   指定された制限時間に達し、処理が中止されました。
11   メモリ不足のため、問題を解決できません。
13   モデルでエラーが発生しました。すべてのセルと制約条件が正しく指定されていることを確認してください。
14   ソルバーによって公差内で整数解が見つかりました。すべての制約条件を満たしています。
15   実行可能 [整数] 解の数が上限に達し、処理が中止されました。
16   実行可能 [整数] 子問題の数が上限に達し、処理が中止されました。
17   大域解に確率収束しました。
18   すべての変数に上限と下限が必要です。
19   バイナリ制約条件または alldifferent 制約条件で、変数の範囲が競合しています。
20   変数の上限と下限には、実行可能解を使用できません。

GetAsyncKeyState関数

引数で指定したキーの押下状態を返します。

引数:キーコード

戻り値
状態 返す値(Integer)
GetAsyncKeyStateを呼び出した時点で対象のキーが押されている 最上位ビットが1(&H8000)
前回のGetAsyncKeyState呼び出し後にも対象のキーが押されていた 最下位ビットが1(&H1)

詳しくは、ヘルプ GetAsyncKeyState関数の仕様 を参考にしてください。

Application.ScreenUpdating

Application.ScreenUpdating = False

画面の更新を停止します。True とすると、更新を再開。
画面表示は大変時間を要する処理です。
特に途中経過を見る必要が無い場合、画面の更新を停止すると、処理速度がかなり早くなります。(場合により10倍以上)

条件付きコンパイル

   32/64ビット版Windowsへの対応

#If Win64 Then
     Public Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As LongLong
#Else
     Public Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long
#End If

参考:Office 2010 の 32 ビット バージョンと 64 ビット バージョンとの互換性


VBAコード部 変更/追加点のみ

'--------------------------------------
' コマンドボタン制御
'--------------------------------------

Private Sub cmdGo_Click()
'
' 全自動実行
'
    Do
        cmdInit_Click   '初期化
        If SolverDialog(True) >= 3 Then Exit Do '中止?
        If Val(Range("目的セル")) = 0 Then
            Application.ScreenUpdating = False '画面更新停止
            cmdReallocate_Click '均等再配置
            Application.ScreenUpdating = True '画面更新再開
            Exit Do
        End If
    Loop

End Sub

Private Sub cmdInit_Click() 'ソルバー実行
    Range("変化させるセル").ClearContents
End Sub



Private Sub cmdSolverDialog_Click()
'
' ソルバーダイアログ表示
'
    SolverOkDialog SetCell:="目的セル", MaxMinVal:=2, ValueOf:=0, ByChange:="変化させるセル", _
                    engine:=3, EngineDesc:="Evolutionary"

End Sub

Private Sub cmdGoSolver_Click()
'
' ソルバー実行
'
    Call SolverDialog(True)
    
End Sub
Private Function SolverDialog(Optional boolHideDialog As Boolean = False) As Integer
'
' ソルバー実行
'
'  boolHideDialog:ダイアログを非表示 True:非表示 False:表示
'   戻り値:SolverSolveからの戻り値
'
    Dim rc
    SolverDialog = SolverSolve(boolHideDialog) '実行

    
End Function



Private Sub cmdReallocate_Click()   '均等再配置実行

    If Range("目的セル") > 0 Then
        MsgBox "再度、初期化→ソルバー実行、を行ってみて下さい。", vbOKOnly, "過不足が0になっていません。"
        Exit Sub
    End If
    
    Call reAllocate("変化させるセル", "条件セル", "最小化セル")  '均等再配置

End Sub





標準モジュール  均等再配置

Option Explicit

#If Win64 Then
    Public Declare PtrSafe Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As LongLong
#Else
    Public Declare Function GetAsyncKeyState Lib "User32.dll" (ByVal vKey As Long) As Long
#End If


Const 収束判定値 = 0.01

Sub reAllocate(範囲セル As String, 条件セル As String, 最小化セル As String)

' 均等再配置

    '----------------------
    '変数
    '----------------------
    Dim rngShift As Range   '選択範囲
    Dim SigmaSum As Double  '分散計
    
    Set rngShift = Range(範囲セル)
    
    '----------------------
    '開始
    '----------------------

    SigmaSum = 99999    '分散初期値

    '----------------------
    '作業変数
    '----------------------
    
    Dim r, c, r2 As Long
    Dim n As Long
    
    Dim tmpSigmaSum As Double
    
    tmpSigmaSum = SigmaSum
        
    For n = 1 To 10 '繰り返し回数
               
        For c = 1 To rngShift.Columns.Count         '選択範囲の列数
            For r = 1 To rngShift.Rows.Count        '選択範囲の行数
            

            
                For r2 = r + 1 To rngShift.Rows.Count
                    '-----------------------------
                    If (GetAsyncKeyState(vbKeyEscape) And 1) = 1 Then GoTo MyAbort
                    '-----------------------------
                    Call MySwap(rngShift(r, c), rngShift(r2, c))   'セル入れ替え
                    
                    If Range(条件セル).Value <> 0 Then              '条件を満たすか?
                        Call MySwap(rngShift(r, c), rngShift(r2, c))   '満たさない→戻す
                    Else
                        Dim tmp
                        tmp = Range(最小化セル).Value   '最小値取り出し
                        If tmp < SigmaSum Then          '値更新?
                            SigmaSum = tmp              '新たな最小値とする
                        Else
                            Call MySwap(rngShift(r, c), rngShift(r2, c))   '戻す
                        End If
                    End If
                Next r2
                

                                
            Next r
        Next c

        If Abs(tmpSigmaSum - SigmaSum) < 収束判定値 Then Exit For '収束
        tmpSigmaSum = SigmaSum
        
    Next n

    '--------------------------------------
    '完了
    '--------------------------------------
    MsgBox "均等再配置完了!", , "正常終了"
    
    GoTo MyEnd

MyAbort:
    MsgBox "均等再配置中止!", , "中止"
MyEnd:


    
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



Private Function IsRange(RangeName As String) As Boolean
' 範囲名の正当性チェック
    On Error GoTo MyEnd
    IsRange = False
    If Range(RangeName).Address <> "" Then IsRange = True   'アドレスを取得できたら正しい!
MyEnd:
    On Error GoTo 0
End Function



次項では「予約勤務」(指定日に勤務を予約しておく)機能を搭載します。
次第に複雑になってきます。 数式も高度に・・・・・!

ページ移動:JavaScriptを有効にして下さい!

関連

×
PageTop