I'm writing a code that calculates the probability given a certain amount of successes. I'm having trouble writing a for loop such that I can categorize some events as failures and some as successes. I was able to do it for Case Is = 1, but I'm not thinking of any clever way to iterate the other cases in a way that would go through all combinations of successes and failures given an amount of successes that the user inputs.
I have made an attempt for Case=2, but can't think of a way to record the failures. In Case=1, I worked around it by using modulus.
Function CustomBinomial(MoveType, SuccessAmount) As Double
'Variables to record which events succeed and fail
Dim FirstEvent As Double
Dim SecondEvent As Double
Dim ThirdEvent As Double
Dim FourthEvent As Double
Dim FifthEvent As Double
Dim SixthEvent As Double
'Rows the probability values are in
ProbSuccessArray = Array(15, 19, 23, 27, 31, 35)
Select Case SuccessAmount
Case Is = 0
'Record 0 events as successful
Case Is = 1
For i = 0 To 5
'Record 1 event as successful
FirstEvent = ProbSuccessArray(i)
'Record 5 events as failures
SecondEvent = ProbSuccessArray((i + 1) Mod 6)
ThirdEvent = ProbSuccessArray((i + 2) Mod 6)
FourthEvent = ProbSuccessArray((i + 3) Mod 6)
FifthEvent = ProbSuccessArray((i + 4) Mod 6)
SixthEvent = ProbSuccessArray((i + 5) Mod 6)
CustomBinomial = CustomBinomial + Cells(FirstEvent, 3).Value * (1 - Cells(SecondEvent, 3).Value) _
* (1 - Cells(ThirdEvent, 3).Value) * (1 - Cells(FourthEvent, 3).Value) _
* (1 - Cells(FifthEvent, 3).Value) * (1 - Cells(SixthEvent, 3).Value)
Next i
Case Is = 2
For i = 0 To 5
For j = 1 To 5
'Record 2 events as successful
If i = j Then GoTo Continue
FirstEvent = ProbSuccessArray(i)
SecondEvent = ProbSuccessArray(j)
'Record 4 events as failures
'code here
'CustomBinomial = CustomBinomial + Successes * Failures
Continue:
Next j
Next i
Case Is = 3
'Record 3 events as successful
Case Is = 4
'Record 4 events as successful
Case Is = 5
'Record 5 events as successful
Case Is = 6
'Record 6 events as successful
End Select
Related
I'm trying to write a recursive function so that it calculates the sum of the products of the combinations of values in a dynamic array. Right now I've been trying to make it work for a simpler case, but I really don't quite understand the structure I should follow for a recursive function. In this case there's supposed to be the sum of 28 two factor products, resulting 1.4
Sub SuPC()
Dim k As Long
Dim s As Long
Dim i As Long
Dim j As Long
k = 8
s = 2
HSum i, j, s, k
End Sub
Function HSum(i As Long, j As Long, s, k) As Double
Dim P As Variant
Dim z() As Double
Dim Tot As Double
ReDim z(0 To (k * s) - 1)
P = Array(1 / 2, 1 / 3, 1 / 4, 1 / 5, 1 / 6, 1 / 7, 1 / 8, 1 / 9)
If i <= k Then
HSum i + 1, j, s, k
If j <= s Then
HSum i, j + 1, s, k
If z(i) = 0 Then z(i) = 1
z(i) = P(j) * z(i)
End If
Tot = z(i) + Tot
End If
Range("J11") = Tot
End Function
If s and k were low fixated values, I could use For loops but the point is for them to be variable.
You should try to use tail recursion as this is just a sum of the products,
see here an example for tail recursion factoring.
Public Function fact_tail(n As Double) As Double
'Tail Recursion
'fact 4 = 4 * fact 3
' = 4* 3 * fact 2
' = 4* 3 * 2 * fact 1
' = 4* 3 * 2 * 1
'fact 4 = go(4, 1)
' = go((n - 1), (a * n))
' = go((4-1),(1*4))
' = go(3, 4)
' = go(3-1, 3*4)
' = go(2, 12)
' = go(2-1, 12*2)
' = go(1, 24)
' = 4* 3 * 2 * 1 = 24
fact_tail = go_fact(n, 1)
End Function
Private Function go_fact(n, a)
If n <= 1 Then
go_fact = a
Else
go_fact = go_fact((n - 1), (a * n))
End If
End Function
I previously have a Excel sheet with VBA coding that fills column, row 1 to 10 with the number 1, row 11 to 20 with number 2 and so on. The code I've used is as follows:
Sub fill()
Dim ID
ID = 1
For c = 1 To 34
ActiveWorkbook.Sheets("Sheet1").Cells(c, 1) = ID
ActiveWorkbook.Sheets("Sheet1").Cells(c + 1, 1) = ID
c = c + 1
If (c Mod 10) = 0 Then
ID = ID + 1
End If
Next c
End Sub
Now I want to change it so that the code starts at row 3 onwards. Meaning row 3 to 12 = 1, row 13 to 22 = 2 and so on. So I changed the 'For' statement to:
For c = 3 To 34
But what happens is that the number 1 appears from row 3 to row 10, and then continues with number 2 in row 11 to 20. Not what I was expecting.
Therefore, what would be the best method of changing the code?
If you want exactly the same output but two rows lower, you can use:
Sub fill()
Dim ID
ID = 1
For c = 1 To 34
ActiveWorkbook.Sheets("Sheet1").Cells(c + 2, 1) = ID
ActiveWorkbook.Sheets("Sheet1").Cells(c + 3, 1) = ID
c = c + 1
If (c Mod 10) = 0 Then
ID = ID + 1
End If
Next c
End Sub
If you still only want to go to row 34 but start in row 3, change the 34 to 32 in the above code.
You can also do it without looping and this is easier to adjust the parameters:
Sub fill()
Const NUMBER_OF_ROWS As Long = 34
Const START_ROW As Long = 3
Const ID As Long = 1
Const NUMBER_IN_GROUP As Long = 10
With ActiveWorkbook.Sheets("Sheet1").Cells(START_ROW, 1).Resize(NUMBER_OF_ROWS)
.Value = .Parent.Evaluate("INDEX(INT((ROW(" & .Address & ")-" & START_ROW & ")/" & _
NUMBER_IN_GROUP & ")+" & ID & ",)")
End With
End Sub
When i understand you write, this should work:
You can use the loop how you did at the beginning. and just add plus 2 to c in the ActiveWorkbook.Sheets("Tabelle1").Cells(c + 2, 1) = ID
Sub fill()
Dim ID
ID = 1
For c = 1 To 34
ActiveWorkbook.Sheets("Tabelle1").Cells(c + 2, 1) = ID
ActiveWorkbook.Sheets("Tabelle1").Cells(c + 3, 1) = ID
c= c+1
If (c Mod 10) = 0 Then
ID = ID + 1
End If
Next c
End Sub
something like that should be the simplest way:
Sub fill()
Dim i As Integer
Dim j As Integer
For i = 1 To 4
For j = 1 To 10
ActiveWorkbook.Sheets("Sheet1").Cells(j + (i - 1) * 10 + 2, 1) = i
Next j
Next i
End Sub
EDIT:
No, the simplest way would be type formula into A3:
=ROUNDDOWN(((ROW()-3))/10,0)+1
end drag it donw.
I have a code which takes data from a PicoLog 1012 and records it into an excel spreadsheet. It is working well but currently it always records 12 channels of data. This will make it slow if a lot of data is required so I would like to allow users to enter a value in a cell to define the number of channels and then skip running unnecessary code based on this.
The important parts are:
Dim values() As Integer 'number of datapoints in the array. Equal to channels * number of datapoints required.
Dim numChannels As Integer
numChannels = Worksheets("Sheet1").Range("W5").value 'this allows the user to set the number of channels
Dim samplenum As Long
samplenum = Worksheets("Sheet1").Range("W3").value 'Reads number of samples desired per channel
nValues = samplenum * numChannels
'The code apparently requires one of these lines per channel.
channels(0) = 1
channels(1) = 2
channels(2) = 3
channels(3) = 4
channels(4) = 5
channels(5) = 6
channels(6) = 7
channels(7) = 8
channels(8) = 9
channels(9) = 10
channels(10) = 11
channels(11) = 12
ReDim values(12 * Worksheets("Sheet1").Range("W3").value) 'allow a variable data array
Dim sampleInterval As Long
Dim microsecs_for_block As Long
Dim testlength As Integer
testlength = Worksheets("Sheet1").Range("W4").value
microsecs_for_block = testlength * 1000000
status = pl1000SetInterval(handle, microsecs_for_block, nValues, channels(0), numChannels)
status = pl1000Run(handle, nValues, 0)
'If there is a more efficient way to do what follows then I would LOVE to hear it. Currently logging begins long after I activate the macro.
ready = 0
Do While ready = 0
status = pl1000Ready(handle, ready)
Loop
Cells(14, "P").value = "RECORDING COMPLETE" 'indicate readiness
' Get a block of W3 readings...
' we can call this routine repeatedly
' to get more blocks with the same settings
Dim triggerIndex As Long
Dim overflow As Integer
status = pl1000GetValues(handle, values(0), samplenum, overflow, triggerIndex)
' Copy the data into the spreadsheet
For i = 0 To samplenum - 1
1: Cells(i + 4, "A").value = adc_to_mv(values(numChannels * i + 0))
2: Cells(i + 4, "B").value = adc_to_mv(values(numChannels * i + 1))
3: Cells(i + 4, "C").value = adc_to_mv(values(numChannels * i + 2))
4: Cells(i + 4, "D").value = adc_to_mv(values(numChannels * i + 3))
5: Cells(i + 4, "E").value = adc_to_mv(values(numChannels * i + 4))
6: Cells(i + 4, "F").value = adc_to_mv(values(numChannels * i + 5))
7: Cells(i + 4, "G").value = adc_to_mv(values(numChannels * i + 6))
8: Cells(i + 4, "H").value = adc_to_mv(values(numChannels * i + 7))
9: Cells(i + 4, "I").value = adc_to_mv(values(numChannels * i + 8))
10: Cells(i + 4, "J").value = adc_to_mv(values(numChannels * i + 9))
11: Cells(i + 4, "K").value = adc_to_mv(values(numChannels * i + 10))
12: Cells(i + 4, "L").value = adc_to_mv(values(numChannels * i + 11))
Next i
My current idea is to write 12 different subs for this and call each one depending on the number of channels required but I am sure there must be an easier way?
Is there some sort of "skip" command which causes lines to be ignored?
IF numChannels = 2
Then skip 3,4,5,6,7,8,9,10,11,12
Else
IF numChannels = 3
Then skip 4,5,6,7,8,9,10,11,12
Else
IF'.... et cetera
I believe the code you are looking for is GoTo. You can have those if statements and if the if statement is triggered, it will "GoTo" where ever your tag has been placed
MSDN Example
Sub SkipLines()
Dim intSkipToLine as Integer
If intSkipToLine = 1 Then Goto Line1:
If intSkipToLine = 2 Then Goto Line2:
If intSkipToLine = 3 Then Goto Line3:
If intSkipToLine = 4 Then Goto Line4:
Line1:
' first line code
Line2:
' second line code
Line3:
' thrid line code
Line4:
' fourth line code
End Sub
You can see the final solution I came up with here
It is not a perfect solution for logging data with picolog but IMHO it is better than the proprietary software if you don't need huge datasets or real-time visualisation.
Pros:
Instantly usable by anyone with excel experience; no need to use
picolog’s interface
No need to convert data for processing into excel
Automatic graphing using excel’s interface
Scope for easy further development (multisheet handling, application-specific processing)
Cons:
No realtime visualisation of data
May struggle to handle logs with over 10,000 data points
In VBA, I can easily pull in an sheet\range into an array, manipulate, then pass back to the sheet\range. I'm having trouble doing this in VB.Net though.
Here's my code.
Rng = .Range("a4", .Cells(.UsedRange.Rows.Count, .UsedRange.Columns.Count))
Dim SheetArray(,) As Object = DirectCast(Rng.Value(Excel.XlRangeValueDataType.xlRangeValueDefault), Object(,))
For X As Integer = 0 To SheetArray.GetUpperBound(0)
If IsNothing(SheetArray(X, 0)) Then Exit For
SheetArray(X, 6) = SheetArray(X, 3)
SheetArray(X, 7) = CDbl(SheetArray(X, 3).ToString) - CDbl(SheetArray(X, 1).ToString) - _
CDbl(SheetArray(X, 7).ToString)
For Y As Integer = 0 To 3
SheetArray(X, Y * 2 + 1) = Math.Round(CDbl(SheetArray(X, Y * 2 + 1).ToString), 3)
Next
If Math.Abs(CDbl(SheetArray(X, 7).ToString)) > 0.1 Then _
.Range(.Cells(X + 1, 1), .Cells(X + 1, 8)).Font.Color = -16776961
Next
I'm getting an error on the first If IsNothing(SheetArray(X, 0)) Then Exit For
line. It is telling me index is out of bounds of the array. Any idea why? The SheetArray object contains the data, but I just am not sure how to get to it.
In the For you have to loop from 0 to Count - 1:
For X As Integer = 0 To SheetArray.GetUpperBound(0) - 1
'...
Next
That will fix your problem.
First off, here's my code:
Sub SimulatePortfolio()
Dim lambda As Double
Dim num As Integer
Dim cycles As Long
Column = 12
q = 1.5
lambda = 0.05
cycles = 100000
Dim data(1 To 100000, 1 To 10) As Integer
Dim values(1 To 10) As Double
For i = 1 To 10
values(i) = 0
Next i
temp = lambda
For i = 1 To cycles
lambda = temp
num = 10
t = 0
Dim temps(1 To 10) As Integer
For k = 1 To 10
temps(k) = 1000
Next k
Do While (t < 10 And num > 0)
t = t + tsim(lambda, num)
For j = 1 To 10
If (j > t) Then
temps(j) = temps(j) - 50
End If
Next j
num = num - 1
If (num <= 0) Then
Exit Do
End If
lambda = lambda * q
Loop
For l = 1 To 10
values(l) = values(l) + temps(l)
data(i, l) = temps(l)
Next l
Next i
For i = 1 To 10
Cells(i + 1, Column) = values(i) / cycles
'Problem occurs on this line:
Cells(i + 1, Column + 1).Value = Application.WorksheetFunction.Var(Application.WorksheetFunction.Index(data, i, 0))
Next i
End Sub
Function tsim(lambda As Double, num As Integer) As Double
Dim v As Double
Dim min As Double
Randomize
min = (-1 / lambda) * Log(Rnd)
For i = 1 To (num - 1)
Randomize
v = (-1 / lambda) * Log(Rnd)
If (min > v) Then
min = v
End If
Next i
tsim = min
End Function
When I set the value for cycles to 10000, it runs fine without a hitch. When I go to 100000 cycles, it gets an Error 13 at the indicated line of code.
Having been aware that Application.Tranpose is limited to 65536 rows with variants (throwing the same error) I tested the same issue with Index
It appears that Application.WorksheetFunction.Index also has a limit of 65536 rows when working with variants - but standard ranges are fine
So you will need to either need to dump data to a range and work on the range with Index, or work with two arrays
Sub Test()
Dim Y
Dim Z
'works in xl07/10
Debug.Print Application.WorksheetFunction.Index(Range("A1:A100000"), 1, 1)
Y = Range("A1:A65536")
`works
Debug.Print Application.WorksheetFunction.Index(Y, 1, 1)
'fails in xl07/10
Z = Range("A1:A65537")
Debug.Print Application.WorksheetFunction.Index(Z, 1, 1)
End Sub