在VBA中使用Solver时,如何closures重新计算?

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

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

  1. 不要使用求解器! 如果你编写一个自己的子文件来做这些工作,你可以使用Range.Calculate而计算是手动的,只需要计算这个单元格(所有其他单元格将被忽略)。

  2. 更改公式并使用迭代选项 – >公式 – >启用迭代计算 。 现在使用一个辅助单元格,其中包含true / false(或者1/0或者其他),并且改变你不想计算的公式(辅助单元格A1 /公式A2):


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

这样,只要A1是true ,它将输出计算之前的值。

据我所知,没有其他办法,因为求解器每次testing一个新的循环时都会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就会假设不会发生变化,只需跳过它就可以重新计算。 这样,你也可以build立你自己的RAND -UDF(当然不同的名称),只要你的辅助单元是非易失性的,就停止工作簿中的所有易失性函数。

注意:在使A1(在本例中)非易失性之后,第一次计算之后仍然会运行一次,就像它是不稳定的。 同时将A1从一个值更改为另一个值(如0到1)将运行一次。

如果您希望在电子表格中随机select可以随意重新计算并且可以开启和closures波动率的解决scheme,则可以采取以下解决方法。

首先,创build一个名为range的单元格,例如“trigger”

然后,将下面的代码放在一个标准的代码模块中:

 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子附加到一个button。 用=SemiVolatileRand(trigger)replace所有出现的=RAND() =SemiVolatileRand(trigger) 。 只要按下button,他们就会重新调度。 此外,如果您想要恢复全部波动率,只需将公式=RAND()放入触发器单元格即可。 (在最后一种情况下获得完全的波动性似乎要求我的代码用虚拟variablesx 一些事情,因此有点无指针的x - x )。

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

 Private Sub Workbook_Open() MsgBox Rnd() End Sub 

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

请注意,如果打算使用rnd ,则工作簿打开事件是放置Randomize语句的自然地点。

我没有把Randomize放在函数本身的原因既是为了节省CPU周期,也是因为一个唠叨的问题,一定比例的时间你将会用完全相同的系统时间重新生成随机数发生器。 对于运行最新版本的Excel的现代体系结构来说,这可能是不可能的,但是,如果你有Randomize Timer (有时在读取其他代码的时候遇到的),有时会发生,因为定时器函数有1毫秒的分辨率,而不pipe系统时钟。

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

公开初始化为布尔值

Function SemiVolatileRand(x As Variant)As Double如果未初始化,则Randomize Initialized = True End If SemiVolatileRand = x – x + Rnd()End Function

如果rnd没有正确接种,这将阻止该function运行。

Excel本身会自动处理工作表函数Rand() ,所以它是一个严格的VBA复杂度。