Reputation: 45
I am running a Monte Carlo in Excel. To represent the distribution of demand for each product SKU during each month, I am using the following formula over 500 times:
=IFERROR(BETA.INV(RAND(),(1+4*(D35-D31)/(D27-D31)),(1+4*(D27-D35)/(D27-D31)),D31,D27),0)
So, that's over 500 RAND()
volatile functions at a time.
The output from each iteration of a formula is stored in a table. I have a VBA to handle that:
Sub QARLO_Products()
'runs on QARLO_P!, for forecasting product
Range("Q7:OC100000").ClearContents
For Iteration = 1 To Range("G1").Value
Range("I1").Value = Range("G1").Value - Iteration
'For QMIN Zinc-Bulk
Cells(6 + Iteration, 17) = Iteration
Cells(6 + Iteration, 18) = Cells(39, 4)
Cells(6 + Iteration, 19) = Cells(39, 5)
Cells(6 + Iteration, 20) = Cells(39, 6)
Cells(6 + Iteration, 21) = Cells(39, 7)
Cells(6 + Iteration, 22) = Cells(39, 8)
Cells(6 + Iteration, 23) = Cells(39, 9)
Cells(6 + Iteration, 24) = Cells(39, 10)
Cells(6 + Iteration, 25) = Cells(39, 11)
Cells(6 + Iteration, 26) = Cells(39, 12)
Cells(6 + Iteration, 27) = Cells(39, 13)
Cells(6 + Iteration, 28) = Cells(39, 14)
Cells(6 + Iteration, 29) = Cells(39, 15)
'For QMIN Zinc-Jugs
Cells(6 + Iteration, 30) = Iteration
Cells(6 + Iteration, 31) = Cells(40, 4)
.... blah, blah, blah and so on for all products....
Cells(6 + Iteration, 444) = Cells(561, 14)
Cells(6 + Iteration, 445) = Cells(561, 15)
Next Iteration
End Sub
The left-hand side of these statements represent the table location for recording output data, the right-hand side are the outputs from the above mentioned formula.
I notice that this VBA runs through each and every line in series. Each line causes all 500 volatile RAND()
functions to recalculate. One iteration of this VBA takes 30 seconds on all 8 cores of a Core i7/ 32GB RAM. I want to run 5,000 iterations on a regular basis.
Can you suggest methods to make this model run more efficiently?
RAND()
and then have all 500 reference it?I have done all the basic/general things to make Excel run more efficiently.
Upvotes: 4
Views: 1176
Reputation: 149
In addition what others said you could rewrite your Application.Worksheetfunction.Norminv(..) as in https://www.autoitscript.com/forum/topic/147479-checking-for-undefined-result-from-some-math-functions/ and replace the rnd() part in it by a Halton-Sequence. It also offers the option to repeat your results and speeds up the whole process considerably..
Upvotes: 0
Reputation: 16907
One small change might make a large difference.
Read and write a large range with a single call into / from an array. I can't check the exact code now, but you can say something like
Range(Cells(6 + Iteration, 18), Cells(6+Iteration, 455).Value = Range(Cells(39,4), Cells(561,15).Value
Or by splitting the read and write so you can check things in the Debugger
Dim TempValues As Variant ' Really a 2D array of variants
TempValues = Range(Cells(39,4), Cells(561,15)).Value
Range(Cells(6 + Iteration, 18), Cells(6 + Iteration, 455)).Value = TempValues
By doing this you will read / write everything in one shot, and by writing only once you will once force the volatile calculation to happen once, instead of on every write.
This looks like a nice write-up about reading and writing ranges as arrays, and checking things out under the debugger: https://bettersolutions.com/excel/cells-ranges/vba-working-with-arrays.htm
Some other ideas:
Rnd
internally). Then if will only get now values (and recalculate) when you explictly recalculate all.Upvotes: 2
Reputation: 421
If you want to make Excel more efficient you have to consider to run your Monte Carlo simulation entirely in VBA. Store the results in an array and plot these in the excel sheet at the end of all the simulations.
Upvotes: 1
Reputation: 421
What do you mean with "I have done all the basic/general things to make Excel run more efficiently"? Did you try to turn of screenupdating, and so on?
Application.ScreenUpdating = False
Application.DisplayStatusBar = False
Application.EnableEvents = False
'Place your macro code here
Application.Calculation = xlCalculationAutomatic
Application.ScreenUpdating = True
Application.DisplayStatusBar = True
Upvotes: 2