VBA不向Solver模型添加元素

时间:2012-12-22 03:56:03

标签: excel vba solver

我之前遇到过一个问题,解决方法没有通过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进行的多次调用是尝试解决之前没有将二进制约束添加到模型中的问题。所有&lt; =约束都是,但不是二进制约束。在将变量添加到模型之前,您似乎无法将变量约束为二进制。在构造by_change_string的循环完成之后,过去只有一个solverok语句,但是当我这样做时,我得到了所有的&lt; =约束而没有二进制的。

当此代码在我的测试表上运行时,生成的模型应该有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 个答案:

答案 0 :(得分:2)

我假设:

Solver在“更改单元格”参数中最多只允许255个字符。如果您不相信我,请尝试手动添加所需的单元格。你将无法做到这一点。

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

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

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

编辑:This limited documentation for Solver可能会略显过时,但会说明以下内容:

  

在“更改单元格”编辑中输入模型决策变量   框。 Excel允许一个人输入所谓的多重选择   最多包含16个范围(矩形,行或列,或单个)   (以逗号分隔)。

这意味着你只能有16个非连续范围到Solver,并期望它能够工作。

答案 1 :(得分:0)

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

答案 2 :(得分:-1)

我想补充一下这些答案:

  1. 使用代码覆盖最大值会在模型中留下一个空的Bychange区域。
  2. 命名范围无法解决问题,因为解算器会立即用常用语言翻译它并删除不适合窗口的最后范围。
  3. 来源:我刚刚使用excel 2013尝试了这些解决方案