2016-04-30 337 views
3

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

回答

3

有2种方法来避免这种情况:

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

  2. 更改公式并进行迭代选项 - >公式 - >启用迭代计算。现在使用的辅助细胞持有真/假(或1/0或其他)和变化的公式,你不想计算如下(辅助细胞A1/A2公式):


=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)将运行一次。

+0

你当然是对的。解算器显然不可能在不重新计算的情况下实现其魔力,因为要求优化的函数和约束由*电子表格公式组成。 –

+0

但是为什么Solver会重新计算整个工作表/整张工作表?为什么不使用范围。计算?我遇到了这个问题,因为我正在做一个非线性OLS(可能没有机会跳过Solver?),但我想这样做N次,每次优化函数(SolverOk中的第一个参数)是简单的,因为我用它们可以得到N个不同的优化函数!),虽然它们不直接参与优化,但是它们不能改变,因为它们会改变优化函数。所以这是故事:( – Nicholas

+0

顺便说一句,那么将它们复制到一个新的区域作为价值? – Nicholas

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子到一个按钮。用 =SemiVolatileRand(trigger)替换所有的出现的=RAND()。只要按下按钮,他们就会重新调节。此外,如果您想要恢复全部波动率,只需将公式=RAND()放入触发单元格中即可。 (在这最后一种情况下获得全面波动似乎需要我的代码确实与虚拟变量x,因此有些无点x - x)。

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

Private Sub Workbook_Open() 
MsgBox Rnd() 
End Sub 

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

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

我没有把Randomize放在函数本身中的原因既是为了节省CPU周期,也是因为一个令人担忧的问题,即在一定比例的时间内你将用完全相同的系统时间重新发送随机数发生器。运行最新版本的Excel的现代体系结构可能是不可能的,但例如,如果您有Randomize Timer(您在阅读其他代码时有时会遇到),有时会发生,因为定时器功能具有1毫秒的分辨率,与系统时钟无关。

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

公共初始化为布尔

功能SemiVolatileRand(X为Variant)为双 如果未初始化然后 随机化 初始化=真 结束如果 SemiVolatileRand = x - x + Rnd() End Function

如果rnd未正确接种,这将阻止函数运行。

Excel本身会自动处理工作表函数Rand(),因此它严格是VBA并发症。

+0

为什么你要在你的ReSample()子程序中使用Randomize? – Nicholas

+0

@NicholasLiu这是种子的伪随机数发生器。查看编辑。 –