首页 文章

VBA不向Solver模型添加元素

提问于
浏览
2

我之前遇到过一个问题,解决方法没有通过VBA向模型添加二进制约束......我(大多数时候)想出了一个问题 . 但现在我遇到了一个新问题 . 首先,让我发布相关的违规代码部分 . 我应该注意这是在Excel 2007中运行 .

'build string of ByChange cells and set up cascading constraints
by_change_string = ""
For i = 1 To j - 1

    If Len(by_change_string) > 0 Then                   'there are already some elements in the string, so we might start with a comma
        If Not (Right(by_change_string, 1) = ",") Then  'make sure the last character isn't already a comma
            by_change_string = by_change_string & ","
        End If
    End If

    current_status = Sheets("Buyback Risk Area").Range("C1").Offset(i).Value
    Select Case current_status
        Case "Y"                'risk area is currently yellow, so green transition is available
            by_change_string = by_change_string & "$E$" & i + 1
            solverok setcell:="$B$" & j + 4, MaxMinVal:=2, ValueOf:="0", ByChange:=by_change_string

            'add binary constraints
            solveradd cellref:="$E$" & i + 1, relation:=5, formulatext:="binary"
        Case "O"                'risk area is currently orange, so green and yellow transitions are available
            by_change_string = by_change_string & "$E$" & i + 1 & ",$G$" & i + 1
            solverok setcell:="$B$" & j + 4, MaxMinVal:=2, ValueOf:="0", ByChange:=by_change_string

            'add cascading constraints
            solveradd cellref:="$E$" & i + 1, relation:=1, formulatext:="$G$" & i + 1 'says E <= G, which means you can't select green unless you've already selected yellow

            'add binary constraints
            solveradd cellref:="$E$" & i + 1, relation:=5, formulatext:="binary"'                solveradd cellref:="$G$" & i + 1, relation:=5, formulatext:="binary"
        Case "R"                'risk area is currently red, so green, yellow, and orange transitions are available
            by_change_string = by_change_string & "$E$" & i + 1 & ",$G$" & i + 1 & ",$I$" & i + 1
            'solverok setcell:="$B$" & j + 4, MaxMinVal:=2, ValueOf:="0", ByChange:=by_change_string

            'add cascading constraints
            solveradd cellref:="$E$" & i + 1, relation:=1, formulatext:="$G$" & i + 1 'says E <= G, which means you can't select green unless you've already selected yellow
            solveradd cellref:="$G$" & i + 1, relation:=1, formulatext:="$I$" & i + 1 'says G <= I, which means you can't select yellow unless you've already selected orange

            'add binary constraints
            solveradd cellref:="$E$" & i + 1, relation:=5, formulatext:="binary"
            solveradd cellref:="$G$" & i + 1, relation:=5, formulatext:="binary"
            solveradd cellref:="$I$" & i + 1, relation:=5, formulatext:="binary"
        Case "B"                'risk area is black, so green, yellow, orange and red transitions are avaailable
            by_change_string = by_change_string & "$E$" & i + 1 & ",$G$" & i + 1 & ",$I$" & i + 1 & ",$K$" & i + 1
            solverok setcell:="$B$" & j + 4, MaxMinVal:=2, ValueOf:="0", ByChange:=by_change_string

            'add cascading constraints
            solveradd cellref:="$E$" & i + 1, relation:=1, formulatext:="$G$" & i + 1 'says E <= G, which means you can't select green unless you've already selected yellow
            solveradd cellref:="$G$" & i + 1, relation:=1, formulatext:="$I$" & i + 1 'says G <= I, which means you can't select yellow unless you've already selected orange
            solveradd cellref:="$I$" & i + 1, relation:=1, formulatext:="$K$" & i + 1 'says K <= M, which means you can't select orange unless you've already selected red

            'add binary constraints
            solveradd cellref:="$E$" & i + 1, relation:=5, formulatext:="binary"
            solveradd cellref:="$G$" & i + 1, relation:=5, formulatext:="binary"
            solveradd cellref:="$I$" & i + 1, relation:=5, formulatext:="binary"
            solveradd cellref:="$K$" & i + 1, relation:=5, formulatext:="binary"
    End Select
Next i

'buyback amount constraint
solveradd cellref:="$O$" & j + 1, relation:=1, formulatext:="$B$" & j + 2

'set target cell
solverok setcell:="$B$" & j + 4, MaxMinVal:=2, ValueOf:="0", ByChange:=by_change_string

'set binary constraints
'binary_array = Split(by_change_string, ",")
'For i = 0 To UBound(binary_array)
'    solveradd cellref:=Range(binary_array(i)), relation:=5, formulatext:="binary"
'Next i


Application.ScreenUpdating = True

SolverSolve userFinish:=False

这里的基本思想是遍历工作表并查看数据(这将从执行变为执行)并构建适当的模型 .

字符串by_change_string包含所有变化变量的单元格地址,这些变量都是二进制变量 . 因此,当代码检查电子表格的每一行时,它确定模型可以考虑该行上的哪些单元格并将它们附加到字符串 .

使用不断增长的by_change_string对solverok进行的多次调用是尝试解决之前未将二进制约束添加到模型中的问题 . 所有的<=约束都是,但不是二元约束 . 在将变量添加到模型之前,您似乎无法将变量约束为二进制 . 在构造by_change_string的循环完成之后,过去只有一个solverok语句,但是当我这样做时,我得到了所有的<=约束而没有二进制的约束 .

当此代码在我的测试表上运行时,生成的模型应该有136个决策变量(通过更改单元格) . 我已经检查过,而by_change_string实际上确实有136个地址 . 但是当我查看求解器对话框时,只有前41个 . 我已经在调试模式中完成了整个执行,看到它在每次迭代后调用solverok,但由于某种原因,只有前41个由更改单元格存在 . 事情出错的数据行并没有什么特别之处......它不像是其中一个选择案例的第一个例子......

当解算器在这个缩写模型上运行时,它会返回一个它说是最佳的垃圾解决方案 . 基本上它保持一切相同,因此最终目标函数值与起始目标函数值相同 .

那么我尝试了其他的东西......你会注意到底部有一个注释掉的代码块:

'set binary constraints
'binary_array = Split(by_change_string, ",")
'For i = 0 To UBound(binary_array)
'    solveradd cellref:=Range(binary_array(i)), relation:=5, formulatext:="binary"
'Next i

这里的想法是通过从选择的案例中获取所有solverok和solveradd语句来提高效率 . 通过等到最后的solverok语句之后,所有变量现在都在模型中,所以你应该只是单步执行by_change_string并使每个变量成为二进制变量 . 这样你只需要一个solverok语句就可以在选择的情况下摆脱所有的solveradd . 所以我在选择的案例中注释掉了所有solverok和solveradd,并再次运行宏 . 我没有得到求解器的输出 . 当我在运行后查看求解器对话框时,目标单元格和通过更改单元格字段都是空白的 . 好像最后一个solverok语句从未运行过 .

所以我试着取消注释所有的solverok,但是将二进制变量的solveradd留下来了 . 我回过头来获得前41个决策变量 . 设置二进制约束的循环确实起到了作用,但solveradd没有将变量放入模型中 .

我在这一点上很难过 . 有任何想法吗?

3 回答

  • 2

    丹尼尔库克似乎对这个问题的原因是正确的 . 我会提出一个不同的解决方法 . 创建包含所有非连续范围的命名范围,并将by_change_string设置为表示该命名范围的字符串 .

  • -1

    我想补充一下这些答案:

    • 使用代码越过最大值会在模型中留下空的Bychange区域 .

    • 命名范围不能解决问题,因为解算器会立即用通常的语言翻译它并删除不适合窗口的最后范围 .

    资料来源:我刚刚使用excel 2013尝试了这些解决方案

  • 0

    我假设:

    The Solver only allows up to 255 characters in the By Changing Cells parameter. 如果您不相信我,请尝试手动添加所需的单元格 . 你将无法做到这一点 .

    您可以传递求解器的最大非连续范围是51(假设每个组只有1个单元格,格式如 $A$1, ,1个字母1个数字2个美元符号和1个逗号) . 如果您尝试手动添加更多,它将删除所有以前的选择 . 据推测,一旦你遇到长度约束,用代码做它就会忽略所有东西 .

    说实话,我从未使用过求解器,也不知道如何通过代码访问它 . 无论如何,你正试图让它做一些它无能为力的事情 .

    现在,而不仅仅是告诉你它不会起作用,这是一个可能(但在我看来是令人讨厌的)解决方案 . 如果我不得不做你正在做的事情,那就是我会尝试的 . 将重要值复制到工作簿中的其他位置,并将它们彼此相邻设置,以便将它们作为连续范围传递 . 解算器运行后,将您的值放回原位 .

    编辑:This limited documentation for Solver可能会略有过时了,但它说明如下:

    在“更改单元格”编辑框中输入模型决策变量 . Excel允许用户输入所谓的多重选择,该选择由逗号分隔的最多16个范围(矩形,行或列或单个单元格)组成 .

    这意味着您只能为Solver提供16个非连续范围,并期望它能够正常工作 .

相关问题