I have a pretty basic loop that I am using to run some random scenarios. On one of my worksheets I am using the =Rand() function to generate random numbers/scenarios for my workbook. What I am trying to program from there is a macro that refreshes the workbook (and the random set of numbers) and then deposits my results onto my worksheet each time the scenario is run. Ultimately, I'd like to be able to run/generate 100,000 random scenarios and deposit the results. Here is what I've coded so far:
Sub Run()
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
Application.DisplayAlerts = False
Dim wksInput As Worksheet, i As Integer
Set wksInput = Sheets("Input")
For i = 2 To 102
Application.Calculate
With wksInput
.Range("P" & i).Value = .Range("J35").Value
.Range("Q" & i).Value = .Range("K35").Value
.Range("R" & i).Value = .Range("L35").Value
.Range("S" & i).Value = .Range("G32").Value
End With
Next i
Application.ScreenUpdating = True
Application.DisplayAlerts = True
End Sub
What I have here works just fine, except that it takes 23 seconds to run just 100 scenarios. Based on my calculation that would require over 6 hours of run time to get my 100,000 random scenarios.
My question is does anyone know of a clever way to either change the code to run more efficiently or optimize what I already have. I done all the basic things like turning calculation to manual and turning off screenupdating.
Thanks for your ideas.
The problem is this line:
Application.Calculate
As it's placed inside the For i = 2 To 102, it means that for every row you add all the =rand() functions of the spreadsheet are recalculated.
May I suggest you to generate the random numbers within the code, using the Rnd() built-in function of VBA. Like that, you will generate only the random input you need avoiding to generate the N-1 other inputs that you will regenerate anyway at the Next loop when calling Application.Calculate once again.
what might that look like?
This is the logic (I cannot tell you specifically because you didn't show your whole code/spreadsheet nor the logic behind this random generation): let's say that you have three random numbers in the cells A1, A2 and A3. They are all calculated with a function =Rand() inside the cell.
Now, with your code, you want that in B1, B2 and B3 there is the sum of the random number in A + 1.
the Excel solution (i.e. Excel calculates the random inputs with the function =Rand()):
For j = 1 To 3
Application.Calculate '<-- this re-calculates 3 =rand(), but you need only the one you're going to use right after (==j)
Range("B" & j) = Range("A" & j) + 1
Next j
the VBA solution (i.e. the random numbers are not in the Excel spreadsheet but calculated through VBA):
Randomize
For j = 1 To 3
Range("A" & j) = Rnd() '<-- you insert the random value in A1, A2...
Range("B" & j) = Range("A" & j) + 1 '<-- you use it
Next j
The Excel solution calculates 3 random functions for 3 times, i.e. 9 iterations. The VBA solution calculates 1 random function for 3 times, i.e. 3 iterations.
I let you imagine the multiplication for 100,000 scenarios with 100 data each.
Related
I have an excel calculation that completes one iteration in 10 columns (+1 column for space for 11 total columns per set). My intention is to copy this range of 11 columns a number of times (ideally 350 or more) using VBA. Each time the range is copy/pasted, that iteration of the calcuation references the previous set for a "cascade" effect.
I've pieced together some VBA code that accomplishes this task but is incredibly slow to process and it is difficult to tell if the calcuation is working as intended at the larger end of the iteration scheme. I added a status bar to track the progress and I have noticed that operation grinds to a halt after 100 or so iterations. I currently have the count set to 100 iterations because that seems to be the point where it struggles.
I've looked around on here for help before asking this and discovered the Application.ScreenUpdating = False trick but that doesn't seem to solve the issue.
How do I optimize what I currently have? Is there a better way to go about doing this?
Sub CascadeCopy2()
Dim i As Integer, x As Integer
Dim count As Integer
Application.ScreenUpdating = False
count = 100
For i = 1 To count
With Sheets("Calc").Range("V3:AF250").Offset(0, x)
.Copy
.Offset(0, 11).PasteSpecial
Application.CutCopyMode = False
x = x + 11
End With
Application.StatusBar = "Progress: " & i & " of " & count & ":" & Format(i / count, "0%")
Next i
Application.StatusBar = False
Application.ScreenUpdating = True
End Sub
To copy a range repeatedly across a worksheet, you need to do a for loop. You can use the equals method, but you will have to do two actions to copy the formulas, see below.
Dim x As Long
x = 1
For i = 1 To 299 'the number of times you want to copy, change as needed
With Sheets("Calc").Cells(1, x + 32).Resize(250, 32)
.Value = Sheets("Calc").Cells(1, x).Resize(250, 32).Value
.Formula = Sheets("Calc").Cells(1, x).Resize(250, 32).Formula
End With
x = x + 32
Next i
Turning off Application.ScreenUpdating is a good start.
Copy-Pasting huge amount of data in loops can become painfully slow, because copy-paste requires using the clipboard, which isn't very efficient.
Try accessing you range directly, using something like this:
Sheets("Calc").Range("V3:AF250").Offset(0,x).value = Sheets("Calc").Range("V3:AF250").Offset(0,11).value
You can then also use the same principle to copy formatting, or colors, or font size, etc, with the same template. For example :
Sheets("Calc").Range("V3:AF250").Offset(0,x).Interior.ColorIndex = Sheets("Calc").Range("V3:AF250").Offset(0,11).Interior.ColorIndex
You can easily adapt it to your needs.
I'm working on a financial simulation. The simulation produces 8 results per run (F9/refresh). I would like to run the simulation which produces results on refresh (F9) and log the 8 values in a table.
I found a macro
Sub Macro1()
For i = 1 To 12
Calculate ' code equivalent of hitting F9
Range("d" & i).Value = Range("a1").Value
Next i
End Sub
that does this for a single cell;
What is need is this;
Something along these lines should work:
Range("d" & i).Resize(1, 8).Value = Application.Transpose(Range("a1").Resize(8, 1).Value)
Hi I am writing Excel VBA code. I just wanted to do subtraction for this kind of table
But I can't figure out how to do that using VBA. Is the code below correct?
Sub OperationO()
Dim OE As Integer
Dim i As Integer
For i = 3 To Range("F6")
Cells(6, i).Value = Cells(2, i).Value - Cells(4, i)
Next i
End Sub
You should really use formulas for something like this, but if you insists on VBA, here it goes.
Whenever you write code for VBA or any similar languages, read what you are writing, what it should/needs to do, as you read it out loud many times the errors pop up. Think of the comments below as "me reading as I write."
Sub OperationO()
'Initiate a Variable type Integer for number storage
Dim OE as Integer
'Initiate another variable same type to use in the loop
Dim i as Integer
'Start a loop from 3 to 6 (because these are the columns you are working with)
For i = 3 to 6
'Set the value in Column "i" on Row 6 to the value in Row 2 minus Row 4 in the same column
'Now here is the thing, when you subtract a negative number, you are adding it, crazy math rules i know, so if the number is negative, you need to Add instead.
Cells(6, i).Value = (Cells(2, i).Value + Cells(4, i).Value)
'If both cells don't contain a number, this will fail, additional checks may help, like IsNumber
Next i
'Its the end of the Sub and you never used the Variable OE, it was declared for nothing.
End Sub
I am trying to create a function or functions that can sum daily hours from time cards for each client to come up with the total hours worked per day. Each client has it's own sheet inside of a single workbook.
Currently, I have a function that determines the sheet that goes with the first client (the third sheet in the workbook):
Function FirstSheet()
Application.Volatile
FirstSheet = Sheets(3).Name
End Function
And one to find the last sheet:
Function LastSheet()
Application.Volatile
LastSheet = Sheets(Sheets.Count).Name
End Function
The part that I am having trouble with it getting these to work within the sum function.
=sum(FirstSheet():LastSheet()!A1
That is basically what I want to accomplish. I think the problem is that I don't know how to concatenate it without turning it into a string and it doesn't realize that it is sheet and cell references.
Any help would be greatly appreciated.
So, an example formula would look like this:
=SUM(Sheet2!A1:A5,Sheet3!A1:A5,Sheet4!A1:A5)
That would sum Sheet2-Sheet4, A1:A5 on all sheets.
Is there a reason you need to write the VBA code to do this?
Can't you just enter it as a formula once?
Also, if you're going to the trouble of writing VBA to generate a formula, it may make more sense to just do the sum entirely in VBA code.
If not, try this:
Sub GenerateTheFormula()
Dim x, Formula
Formula = "=SUM(" 'Formula begins with =SUM(
For x = 3 To Sheets.Count
Formula = Formula & Sheets(x).Name & "!A1," 'Add SheetName and Cell and Comma
Next x
Formula = Left(Formula, Len(Formula) - 1) & ")" 'Remove trailing comma and add parenthesis
Range("B1").Formula = Formula 'Where do you want to put this formula?
End Sub
Results:
The functions return strings and not actual worksheets. The Worksheet does not parse strings well. So add a third function that uses the Evaluate function:
Function MySum(rng As Range)
MySum = Application.Caller.Parent.Evaluate("SUM(" & FirstSheet & ":" & LastSheet & "!" & rng.Address & ")")
End Function
Then you would simply call it: MySum(A1)
It uses the other two function you already have created to create a string that can be evaluated as a formula.
I didn't understand ur question completely but As I understood u have different sheets of different clients which contains supoose column 1 date and column 2
contains hours on that particular date wise hours and a final sheet which column1 contains name of client and column 2 contains total hoursPlease try it
Sub countHours()
Dim last_Row As Integer
Dim sum As Double
sum = 0
'Because I know number of client
For i = 1 To 2 'i shows client particular sheet
last_Row = Range("A" & Rows.Count).End(xlUp).Row
Sheets(i).Activate
For j = 2 To last_Row
'In my Excel sheet column 1 contains dates and column 2 contains number of hours
sum = sum + Cells(j, 2)
'MsgBox sum
Next j
'Sheet 3 is my final sheet
ThisWorkbook.Sheets(3).Cells(i + 1, 2).Value = sum
sum = 0
Next i
End Sub
Happy Coding :
I'm using Excel to generate numbers from a basic formula that has 2 variables. I have 600 numbers (specifically coordinates), that I need to generate 13 new numbers from each (so I'll need a total of 8400 values/rows for each X & Y). Since the formula will need to change every 14 rows to a new absolute cell, I am having a hard time thinking of how to accomplish this in Excel using VBA.
My current thought is this:
Add rows to accommodate for the new values.
Fill down columns C and D with the repeating pattern of values.
Create a loop that runs the formula for 14 rows then repeats, keeping the absolute value based on position (?).
Admittedly, I am not a pro at VBA, so any help on how to accomplish this task is greatly appreciated.
See screen grab of data, below, for an example.
Snippet of Data
I used this code to get 13 new rows for my values.
Sub AddRows()
ScreenUpdating = False
With ActiveSheet
lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
End With
Dim AddRows As Integer: AddRows = 13
Dim i As Integer: i = lastRow
Do While i <> 1
Rows(i & ":" & i + AddRows - 1).Insert
i = i - 1
Loop
ScreenUpdating = True
End Sub