我正在尝试使用求解器找到182个单元的根,每个单元仅依赖于另一个变量,输出单元从O2到O183,可变单元从P2到P183。我试图更改变量单元格以使输出单元格等于1.代码可以工作,但结果不会收敛。因为我只想要固定值1,所以我没有设置MaxMin值。 代码是:
Sub Solver()
Dim setcellrange As Range, bychangerange As Range
Dim i As Long
For i = 3 To 5
Set setcellrange = Sheets("AshfordPierce").Cells(i, 15)
Set bychangerange = Sheets("AshfordPierce").Cells(i, 16)
SolverReset
SolverOk SetCell:=setcellrange.Address, ValueOf:=1, ByChange:=bychangerange.Address, Engine:=1, EngineDesc:="GRG NONLINEAR"
SolverSolve
Next i
End Sub
当我使用VBA之外的解算器功能时,它可以工作,但VBA结果并不会收敛。任何帮助表示赞赏。
答案 0 :(得分:2)
注意:我将使用这篇文章重点介绍在VBA中使用Solver的几个挑战和希望最佳做法。如果需要更新,请随时告诉我。
似乎(几乎)Solver的所有参数都是Variant类型。这可能会让您相信您在提供信息方面具有很大的灵活性。事实证明,您需要提供精心制作的文本字符串。
使用SolverSave作为示例,根据MS documentation 您必须指定SaveArea,如果SaveArea与活动工作表不同,则SaveArea必须包含工作表名称。
这有效:
SolverSave SaveArea:="Sheet2!A1"
事实上,所有MS文档都在其示例中使用了字符串文字。
这有效:
SolverSave SaveArea:="Sheet2!A1:A4"
只要Save只需要四行来存储其数据,这通常是,但并非总是如此。
这不起作用:
Set SaveRng = Sheets("Sheet2").Range("A1")
SolverSave SaveArea:=SaveRng
它不会抛出错误。它将一些信息放在Sheet2中的单元格A1中,但是活动工作表中的所有其他信息。
这不起作用:
Set SaveRng = Sheets("Sheet2").Range("A1:A4")
SolverSave SaveArea:=SaveRng
它会在SolverSave中抛出类型不匹配错误。
这项工作:
Set SaveRng = Sheets("Sheet2").Range("A1")
SaveAddress = Split(SaveRng.Address(external:=True), "[")(0) & Split(SaveRng.Address(external:=True), "]")(1)
SolverSave SaveArea:=SaveAddress
以上是我能找到的最简洁的方法,可以为包含工作表名称和单引号的范围构建完整地址"'"""在需要的时候。 (供您调查 - 为什么赢得SaveRng.Address工作?)
我建议对每个Solver例程使用最后一个方法(上面的参数需要一个地址)。默认情况下,Solver期望事物在ActiveSheet上,这可能会导致意外行为。 / p>
请参阅this post,了解SolverReset和SolverSolve的组合,将您的计算模式设置为手动(并将其留在那里)。
SolverReset将所有求解器选项设置回默认值。这可以通过SolverLoad(假设您有一组已保存的默认值)或使用SolverGet / SolverOptions管理它们来完成。
请勿使用SolverReset。
录制宏以获得Solver VBA代码示例时,如果选择默认值,您将获得" Engine:= 1"和" EngineDesc:=&#34 ; GRG Nonlinear""。根据{{3}},为Engine或EngineDesc设置值就像从“求解器参数”对话框的下拉列表中选择一个值。它还说Engine:= 1对应于Simplex LP方法,不 GRG非线性。在设定这两个参数时似乎存在冲突的机会。
测试此代码时......
Sub mySolve()
Dim SetRng As Range, ChgRng As Range
Dim SetAddr As String, ChgAddr As String
Dim i As Long
For i = 2 To 4
Set SetRng = Sheets("Sheet1").Cells(i, 5)
Set ChgRng = Sheets("Sheet1").Cells(i, 4)
SetAddr = Split(SetRng.Address(external:=True), "[")(0) & Split(SetRng.Address(external:=True), "]")(1)
ChgAddr = Split(ChgRng.Address(external:=True), "[")(0) & Split(ChgRng.Address(external:=True), "]")(1)
SolverOk SetCell:=SetAddr, MaxMinVal:=3, _
ValueOf:=i, ByChange:=ChgAddr, _
Engine:=1, EngineDesc:="GRG NONLINEAR"
SolverSolve UserFinish:=True
Next i
End Sub
它不是一次一个地解决第2,3和4行的问题,而是在第4行解决了三次问题。这是在使用此代码之前解决的最后一个问题。它的行为就好像SolverOK永远不会更新SetCell,ValueOf或ByChange值。没有错误。
但是,测试此代码(删除EngineDesc),所有行为都符合预期......
Sub mySolve()
Dim SetRng As Range, ChgRng As Range
Dim SetAddr As String, ChgAddr As String
Dim i As Long
For i = 2 To 4
Set SetRng = Sheets("Sheet1").Cells(i, 5)
Set ChgRng = Sheets("Sheet1").Cells(i, 4)
SetAddr = Split(SetRng.Address(external:=True), "[")(0) & Split(SetRng.Address(external:=True), "]")(1)
ChgAddr = Split(ChgRng.Address(external:=True), "[")(0) & Split(ChgRng.Address(external:=True), "]")(1)
SolverOk SetCell:=SetAddr, MaxMinVal:=3, _
ValueOf:=i, ByChange:=ChgAddr, _
Engine:=1
SolverSolve UserFinish:=True
Next i
End Sub
我建议只设置Engine或EngineDesc中的一个,而不是两者。
根据MS documentation,SolverSave会将Solver配置保存在一列信息中。根据我的经验,该列通常为4行。
让我解释运行此代码的结果......
Sub mySolve3()
Dim SetRng As Range, ChgRng As Range, SavRng As Range
Dim SetAddr As String, ChgAddr As String, SavAddr As String
Dim iLoop As Long
For iLoop = 2 To 4
Set SetRng = Worksheets("Sheet1").Cells(iLoop, 5)
Set ChgRng = Worksheets("Sheet1").Cells(iLoop, 4)
Set SavRng = Worksheets("Sheet2").Cells(1, iLoop - 1)
SetAddr = Split(SetRng.Address(external:=True), "[")(0) & Split(SetRng.Address(external:=True), "]")(1)
ChgAddr = Split(ChgRng.Address(external:=True), "[")(0) & Split(ChgRng.Address(external:=True), "]")(1)
SavAddr = Split(SavRng.Address(external:=True), "[")(0) & Split(SavRng.Address(external:=True), "]")(1)
SolverOk SetCell:=SetAddr, MaxMinVal:=3, ValueOf:=(iLoop - 1), ByChange:=ChgAddr, Engine:=1
SolverSolve UserFinish:=True
SolverSave SaveArea:=SavAddr
Next iLoop
End Sub
此代码将保存工作表2中A列,B列和C列中三种不同求解器运行的设置。
运行后,在Sheet2上:单元格A1包含=$E$2=1
,单元格B1包含=$E$3=2
,单元格C1包含=$E$4=3
。 SolverSave输出中的第一行是SetCell地址(您可以在SolverOK中指定),在我的情况下,将其设置为等于ValueOf值(您将在SolverOK中指定)。
单元格A2包含=COUNT($D$2)
,单元格B2包含=COUNT($D$3)
,单元格C2包含=COUNT($D$4)
。 SolverSave输出中的第二行是ByChange地址(您将在SolverOK中指定)。需要进行更多调查以了解为何使用COUNT函数。
细胞A3,B3和C3含有
={32767,32767,0.000001,0.01,FALSE,FALSE,FALSE,1,2,1,0.0001,TRUE}
通过检查,看起来这是前12 MS Documentation的数组。使用SolverGet检索这些值,而不是32767,您得到2147483647 - 我希望它们在数据类型方面存在一些内部问题。
细胞A4,B4和C4均含有
={0,0,1,100,0,FALSE,FALSE,0.075,0,0,FALSE,30}
同样,通过检查,它似乎是SolverGet中最后12个TypeNum值的数组。由于SolverGet有29个TypeNum' s,似乎有5个不可用。但是,SolverOptions只有21个参数。
使用SolverSave保存配置后,可以修改单元格的内容,SolverLoad用于更改Solver配置(而不是SolverOK)。
我需要更多地调查解算器。有几个片状的东西正在发生。我能够在VBA中使用循环来完成以下工作。
从此开始...
我得到了这些结果......
使用此代码...
Sub mySolve()
Dim LoadRng As Range
Dim i As Long
Set LoadRng = Sheets("Sheet1").Range(Cells(1, 7), Cells(4, 7))
For i = 2 To 4
LoadRng.Cells(1, 1).Value = "=$E$" & i & "=1"
LoadRng.Cells(2, 1).Value = "=COUNT($D$" & i & ")"
LoadRng.Cells(3, 1).Value = "={32767,32767,0.000001,0.01,FALSE,FALSE,FALSE,1,2,1,0.0001,TRUE}"
LoadRng.Cells(4, 1).Value = "={0,0,1,100,0,FALSE,FALSE,0.075,0,0,FALSE,30}"
SolverLoad LoadArea:=LoadRng.Address
SolverSolve UserFinish:=True
SolverFinish KeepFinal:=1
Next i
End Sub
要诊断的事情: