在VBA中使用Solver时如何关闭重新计算?

时间:2016-04-30 16:07:17

标签: excel vba excel-vba

我尝试在工作表上使用Solver,比如表A.我在表A上也有一些单词“= rand()”,或者使用写有Application.Volatile的自定义函数说明工作表上的任何公式内。我希望这些挥发性细胞在我做解算器时停止重新计算,所以我在求解器程序中使用了Application.Calculation = xlCalculationManual,但在运行求解器之后,我发现那些挥发性细胞已经改变了。我哪里做错了?

2 个答案:

答案 0 :(得分:3)

有两种方法可以避免这种情况:

  1. 不要使用求解器!如果您编写自己的子来执行这些操作,您可以使用Range.Calculate,而计算是手动计算此单元格(所有其他单元格将被忽略)。

  2. 更改公式并使用迭代选项 - >公式 - >启用迭代计算。现在使用一个helper-cell,它包含true / false(或1/0或其他)并更改你不想计算的公式如下(helper-cell A1 / formula A2):

  3. =IF(A1,A2,[your old formula])
    

    这样,只要A1为true,它就会输出计算前的值。

    据我所知,没有其他方法,因为每次测试新周期时,求解器都会Calculate

    修改
    拥有不需要一直挥发的易失性UDF,你可以使用一个简单的技巧(同样是一个辅助单元):

    A1: [volatile function like =NOW() or =RAND()]
    A2: =MY_UDF(A1)
    

    在模块中:

    Public Function MY_UDF(a As Variant) As Double
      a = a
      MY_UDF = Now
    End Function
    

    只要A1保持易失性,您的UDF每次都会重新计算。但是如果你清空A1(或者使其处于非易失性状态),那么提交给你的UDF的值就不会有任何变化,这样excel / vba就会假设也不会发生任何变化,只是跳过它重新计算。通过这种方式,只要助手单元是非易失性的,您也可以构建自己的RAND - UDF(当然具有不同的名称)来停止工作簿中的所有易失性函数。

    作为注释:在使A1(在此示例中)非易失性之后,之后的第一次计算仍将运行1次,就像它是易失性的一样。将A1从一个值更改为另一个值(如0到1)也会运行一次。

答案 1 :(得分:3)

如果您希望电子表格中的随机值可以随意重新计算并且可以打开和关闭其波动率,那么这是一种可能有用的解决方法。

首先,创建1个单元格的命名范围,例如"触发"

然后,将以下代码放在标准代码模块中:

Function SemiVolatileRand(x As Variant) As Double
    SemiVolatileRand = x - x + Rnd()
End Function

Sub ReSample()
    Randomize
    Range("trigger").Value = Range("trigger").Value + 0.01
End Sub

ReSample子附加到按钮。将所有出现的=RAND()替换为 =SemiVolatileRand(trigger)。只要按下按钮,它们就会重新计算。此外,如果您想要重新开启完全波动,只需将公式=RAND()放入触发器单元格中。 (在最后一种情况下获得完全波动似乎要求我的代码使用虚拟变量x进行某事,因此有点无效x - x)。

Randomize从系统时钟重新生成随机数生成器。应该每个会话至少调用一次。如果您不这样做,那么每次打开使用VBA rnd的Excel工作簿时,您将获得相同的随机值序列。您可以通过制作一个空白工作簿并在ThisWorkbook代码模块中放置:

来验证这一点
Private Sub Workbook_Open()
MsgBox Rnd()
End Sub

每次打开工作簿时,消息块将显示相同的值。另一方面,如果您将行Randomize放在MsgBox之前,那么每次打开它时都会得到不同的值。

请注意,如果您计划使用Randomize,则工作簿打开事件是放置语句rnd的自然位置。

我没有将Randomize放入函数本身的原因既是为了节省CPU周期又是因为一个唠叨的担忧,即你将用一定比例的时间重新安排随机数发生器完全相同的系统时间。对于运行最新版Excel的现代架构而言,这可能是不可能的,例如如果您有Randomize Timer(有时在阅读其他代码时遇到),有时会发生这种情况,因为无论系统时钟如何,定时器功能的分辨率都是1毫秒。

我的代码确实有一个缺点,如果你绕过按钮只是更改触发器单元格,那么你可能会错过重新播种。如果这是一个问题,1可能性是这样的:

Public Initialized as Boolean

函数SemiVolatileRand(x As Variant)As Double         如果未初始化则             随机化             Initialized = True         万一         SemiVolatileRand = x - x + Rnd() 结束功能

如果rnd没有正确播种,这将阻止该功能运行。

Excel本身使用工作表函数Rand()自动处理种子,因此它严格来说是VBA复杂化。