Dynamic upper bound for two-dimensional array - excel

I'm determining values for a matrix with 92 rows and a varying range of columns (1-64 columns). Depending on the row number, my code should define the upper bound of the number of columns for that row. I am using nested loops, but my code gives me a matrix of 92x64 (so the number of columns is constant).
Dim m As Integer
Dim n As Integer
Dim o As Integer
Dim p As Integer
Dim q As Integer
Dim N_bay As Single
Dim N_b As Single
Dim D_r As Single
Dim s As Single
Dim Con_l As Single
Dim tau_s As Single
Dim N_r As Single
D_r = 394.9
s = 4.24
Con_l = 6.1
N_r = 92
N_b = 64
For n = LBound(N_rj, 1) To UBound(N_rj, 1)
For m = LBound(N_rj, 2) To UBound(N_rj, 2)
N_rj(n, 1) = n
N_rj(n, 2) = (D_r - ((n - 1) * s))
N_rj(n, 3) = WorksheetFunction.RoundDown(((D_r - ((n - 1) * s)) / Con_l), 0)
N_rj(n, 4) = 1 / (N_rj(n, 3))
size = N_rj(n, 3)
ReDim N_bz(1 To 92, 1 To size)
For o = 1 To UBound(N_bz, 1)
For p = 1 To UBound(N_bz, 2)
N_bz(o, p) = p * Con_l
Cells(o + 1, p + 6).Value = N_bz(o, p)
Next p
Next o
Cells(n + 1, m).Value = N_rj(n, m)
Next m
Next n
I expect to get a matrix with 92 rows, of which each row has a varying number of columns. Hence, row #1 has 64 columns, row #2 has 63 columns, etc.

I suspect what you're trying to do is something like the following
Sub MatrixArray()
Dim m As Long, n As Long, o As Long, p As Long, q As Long
Dim size As Long
Dim N_bay As Single, N_b As Single, D_r As Single, s As Single, Con_l As Single
Dim tau_s As Single, N_r As Single
Dim N_rj(1 To 92, 1 To 4) As Single, N_bz() As Single
Application.ScreenUpdating = False
D_r = 394.9
s = 4.24
Con_l = 6.1
N_r = 92
N_b = 64
For n = LBound(N_rj, 1) To UBound(N_rj, 1)
For m = LBound(N_rj, 2) To UBound(N_rj, 2)
N_rj(n, 1) = n
N_rj(n, 2) = (D_r - ((n - 1) * s))
N_rj(n, 3) = WorksheetFunction.RoundDown(((D_r - ((n - 1) * s)) / Con_l), 0)
N_rj(n, 4) = 1 / (N_rj(n, 3))
size = N_rj(n, 3)
Debug.Print size
ReDim N_bz(1 To 92, 1 To size)
For o = 1 To UBound(N_bz, 1)
For p = 1 To UBound(N_bz, 2)
N_bz(o, p) = p * Con_l
Next p
Next o
Cells(n, 7).Resize(1, UBound(N_bz, 2)).Value2 = Application.Index(N_bz, n, 0)
Next m
Next n
Cells(1, 1).Resize(UBound(N_rj, 1), UBound(N_rj, 2)) = N_rj
Application.ScreenUpdating = True
End Sub
Something like the following might be more efficient though
Sub MatrixArray()
Dim m As Long, n As Long, o As Long, p As Long, q As Long
Dim size As Long
Dim N_bay As Single, N_b As Single, D_r As Single, s As Single, Con_l As Single
Dim tau_s As Single, N_r As Single
Dim N_rj(1 To 92, 1 To 4) As Single, N_bz() As Single
Application.ScreenUpdating = False
D_r = 394.9
s = 4.24
Con_l = 6.1
N_r = 92
N_b = 64
Dim MaxSize As Long
MaxSize = WorksheetFunction.RoundDown(((D_r - ((LBound(N_rj, 1) - 1) * s)) / Con_l), 0)
Debug.Print MaxSize
ReDim N_bz(1 To MaxSize)
For p = 1 To UBound(N_bz)
N_bz(p) = p * Con_l
Next p
For n = LBound(N_rj, 1) To UBound(N_rj, 1)
N_rj(n, 1) = n
N_rj(n, 2) = (D_r - ((n - 1) * s))
N_rj(n, 3) = WorksheetFunction.RoundDown(((D_r - ((n - 1) * s)) / Con_l), 0)
N_rj(n, 4) = 1 / (N_rj(n, 3))
size = N_rj(n, 3)
Cells(n, 7).Resize(1, size).Value2 = N_bz
Next n
Cells(1, 1).Resize(UBound(N_rj, 1), UBound(N_rj, 2)) = N_rj
Application.ScreenUpdating = True
End Sub

Related

How to split values separated by comma and keep its row correspondence in excel

In Excel 365, I have data in this format:
Or, in text:
1,2,3,7 A
4 B
5 C
6, 8 D
And I'm trying to split the data so it becomes this:
Or, in text
1 A
2 A
3 A
4 B
5 C
6 D
7 A
8 D
The leftmost row is always composed by numbers separated by comma or a single number. The right row can be any data.
The following VBA code will do most of what you want:
Sub ExpandRows()
Dim R As Range
Dim Rw As Range
Dim I As Integer
Dim J As Integer
Dim K As Integer
Dim S As String
Dim Tokens(1 To 1000) As String
Dim NTokens As Integer
Const Delim As String = ","
Dim StartSize As Integer
Dim TopCell As Range
Dim BotCell As Range
Set R = Selection
Set TopCell = R.Cells(1, 1)
Set BotCell = R.Cells(R.Rows.Count, 1)
StartSize = R.Rows.Count
For I = StartSize To 1 Step -1
S = R(I, 1)
If (S <> "") Then
J = 0
NTokens = 0
Do
K = InStr(J + 1, S, Delim)
If (K = 0) Then
NTokens = NTokens + 1
Tokens(NTokens) = Mid$(S, J + 1, Len(S) - J)
Else
NTokens = NTokens + 1
Tokens(NTokens) = Mid$(S, J + 1, (K - J - 1))
J = K
End If
Loop Until (K = 0)
End If
If (NTokens > 1) Then
For J = NTokens To 2 Step -1
If (Tokens(J) <> "") Then
Set Rw = R.Cells(I, 1).EntireRow
Call Rw.Select
Call Rw.Copy
Call R.Cells(I + 1, 1).EntireRow.Select
Call Rw.Insert(xlDown)
If (I = 1) Then
Set TopCell = TopCell.Cells(0, 1)
Set R = Range(TopCell, BotCell)
End If
Call R.Select
Call R.Cells(I + 1, 1).Select
R(I + 1, 1) = Tokens(J)
End If
Next J
R(I, 1) = Tokens(1)
End If
Next I
End Sub
This code will split the cells and create new rows with a single entry.
To use it, select the first column and execute the method.
After that, all you have to do is sort on the first column.

VBA Excel - passing an array into a function

I want to write a function in which I can include an array. I imagine that the function will work like the function NPV, we have 3 ways of specifying the syntax in the formula bar:
1) NPV(0.5, A1:A5)
2) NPV(0.5, A1, A2, A3, A4, A5)
3) NPV(0.5, {10, 20, 23, 25, 27})
Where 10, 20, 23, 25, 27 are the value in range A1:A5.
And here is my function in VBA:
Function MyNpv(r As Double, Flows As Variant)
Dim i, n As Integer
Dim p As Double
n = Application.Count(Flows)
For i = 1 To n
p = p + Flows(i) / (r + 1) ^ i
Next i
MyNpv = p
End Function
However, my own function can only work like:
MyNpv(0.5, A1:A5)
Is there anyway that I can declare my array so that it would work flexibly like the function NPV?
Thanks.
Use a ParamArray, Loop that to test what is in each part of the array and then adjust accordingly:
Function MyNpv(r As Double, ParamArray Flows() As Variant)
Dim FlowsPart As Variant
Dim p As Double
p = 0#
Dim k As Long
k = 1
For Each FlowsPart In Flows
Dim arr As Variant
arr = FlowsPart
Dim x As Long
x = -1
Dim y As Long
y = -1
On Error Resume Next
x = UBound(arr, 2)
y = UBound(arr)
On Error GoTo 0
Dim j As Long
Dim i As Long
If x >= 0 And y >= 0 Then
For i = LBound(arr, 1) To UBound(arr, 1)
For j = LBound(arr, 2) To UBound(arr, 2)
p = p + arr(i, j) / (r + 1) ^ k
k = k + 1
Next j
Next i
ElseIf y >= 0 Then
For j = LBound(arr) To UBound(arr)
p = p + arr(j) / (r + 1) ^ k
k = k + 1
Next j
Else
p = p + arr / (r + 1) ^ k
k = k + 1
End If
Next FlowsPart
MyNpv = p
End Function

Monte Carlo simulation including gaussian elimination using VBA

I have written a code which solves a system of linear equations serval times in a row during a Monte Carlo simulation.
For each run the input is slightly changed and the solution must be calculated every time again. The purpose of this is to obtain the probability distribution function of the results (the solutions of the linear system).
So, my question is:
Is there a way to solve the system of linear equations only once and save the generic solution so that for every Monte Carlo run the solutions can be calculated directly?
This would be very time saving as for a proper simulation I need at least 20k runs and even for small systems of three unknowns this takes a long time. My code solves this linear equations every time new as in its original version the number of variables and therefore the number of input quantities should be closable and thus the generic solutions are unknow.
Here is my Gaussian elimination algorithm.
Function gaussian_elimination(w As Variant, mm As Variant, R As Variant, rb As Variant, n_iso As Integer) As Variant()
'initializing running indexes
Dim i As Integer
Dim j As Integer
Dim h As Integer
Dim n As Integer
n = n_iso
'runing variables for Gauss elimination
Dim ip As Integer
Dim q As Integer
Dim p As Integer
Dim z As Double
Dim temp1(1, 1) As Variant
Dim temp2(1, 1) As Variant
Dim sum As Variant
'initializing b vector
Dim b() As Variant
ReDim b(1 To n - 1, 0 To 1)
'initializing k vector
Dim k() As Variant
ReDim k(1 To n - 1, 0 To 1)
'initializing A matrix
Dim a() As Variant
ReDim a(1 To n - 1, 1 To n - 1)
'initializing X matrix
Dim x() As Variant
ReDim x(1 To n - 1, 1 To n)
' calculating b vector
For i = 1 To (n - 1) Step 1
b(i, 0) = mm(1, 0) / (w(i + n - 1, 0) * (rb(i, 0) - R(i * n, 0))) - mm(1, 0) / (w(i, 0) * (R(i, 0) - rb(i, 0)))
Next i
'calculating A matrix
For i = 1 To (n - 1) Step 1
For j = 1 To (n - 1) Step 1
a(i, j) = mm(j + 1, 0) * ((R(j + i * (n - 1), 0) / (w(i + n - 1, 0) * (rb(i, 0) - R(i * n, 0)))) - (R(j, 0) / (w(i, 0) * (R(i, 0) - rb(i, 0)))))
Next j
Next i
'using on board solving routine
Dim A_Inv As Variant
Dim k_vec As Variant
Dim b_dummy As Variant
'filling X matrix
For i = 1 To n - 1 Step 1
For j = 1 To n Step 1
If j = (n) Then
x(i, j) = b(i, 0)
Else: x(i, j) = a(i, j)
End If
Next j
Next i
'Gaussian elimination
For i = 1 To (n - 2) Step 1
For j = i + 1 To (n - 1) Step 1
If (Abs(x(j, i)) > Abs(x(i, i))) Then
For h = 1 To n
temp1(1, 1) = x(i, h)
temp2(1, 1) = x(j, h)
x(i, h) = temp2(1, 1)
x(j, h) = temp1(1, 1)
Next h
End If
Next
For p = i + 1 To n - 1
z = x(p, i) / x(i, i)
For q = i + 1 To n
x(p, q) = x(p, q) - z * x(i, q)
Next q
x(p, i) = 0
Next p
Next i
'calculatiing k factors backwards
If Abs(x(UBound(x, 1), UBound(x, 2) - 1)) <= 0 Then
MsgBox "Equation system can not be solved! Solving for k factors faild", vbExclamation, "Warning!"
Exit Function
End If
k((UBound(x, 1)), 0) = x((UBound(x, 1)), UBound(x, 2)) / x((UBound(x, 1)), (UBound(x, 2) - 1))
For i = ((UBound(x, 1) - 1)) To (LBound(x, 1)) Step -1
sum = x(i, UBound(x, 2))
For j = i + 1 To (UBound(x, 2) - 1) Step 1
sum = sum - x(i, j) * k(j, 0)
Next j
k(i, 0) = sum / x(i, i)
Next i
For i = 1 To n - 1
k(i, 0) = (-1) * k(i, 0)
Next i
gaussian_elimination = k
End Function

Poker Dealer Logic

I'd like to use Excel as my poker dealer. Here is the code that will generate 20 random numbers (cards) between 1 and 52. Output of first 20 numbers/cards is in column A1:A20. I'd like to have the next set of 20 numbers/cards generated in A22:A41, 3rd A43:A62, and so on. How can the code be fixed so that it displays 1000 hands in column A with one row separating each set? Thank you.
Sub cards()
Range("A:A").Clear
cardstodraw = 20
For x = 1 To cardstodraw
begL:
ActiveSheet.Cells(1, 2) = "=Randbetween(1,52)"
ActiveSheet.Cells(x, 1) = ActiveSheet.Cells(1, 2).Text
cardvalue = ActiveSheet.Cells(x, 1)
y = 1
Count = 0
Do Until ActiveSheet.Cells(y, 1) = ""
If ActiveSheet.Cells(y, 1) = cardvalue Then
Count = Count + 1
End If: y = y + 1: Loop
If Count > 1 Then GoTo begL
Next
Range("B1").Clear
End Sub
Your code is somewhat convoluted (using GoTo is usually an indication that something can be improved). For getting a sample of size 20 from 1-52, use a modified Fisher-Yates shuffle:
Option Explicit 'you really should be using this
Function deal(n As Long, k As Long) As Variant
'returns an array of length k
'consisting of k numbers in the range 1 to n
Dim deck As Variant
Dim i As Long, j As Long, temp As Long
ReDim deck(1 To n)
For i = 1 To n
deck(i) = i
Next i
With Application.WorksheetFunction
'do k steps of a Fisher-Yates shuffle on deck
For i = 1 To .Min(k, n - 1)
j = .RandBetween(i, n)
If i < j Then 'swap
temp = deck(i)
deck(i) = deck(j)
deck(j) = temp
End If
Next i
End With
ReDim Preserve deck(1 To k)
deal = deck
End Function
If you want to have 1000 hands in Column A:
Sub ManyHands()
Dim i As Long
With Application.WorksheetFunction
For i = 1 To 1000
Range(Cells(1 + 21 * (i - 1), 1), Cells(21 * i - 1, 1)).Value = .Transpose(deal(52, 20))
Next i
End With
End Sub
On Edit Here is a modified version of the code, one which deals cards to multiple players:
Function deal(n As Long, k As Long, players As Long) As Variant
'returns an array with k rows and players columns
'consisting of k*players numbers in range 1 to n
'if players = 1, then the array is 1-dimensional
'otherwise it is 2-dimensional
Dim deck As Variant
Dim i As Long, j As Long, temp As Long
Dim hands As Variant
ReDim deck(1 To n)
For i = 1 To n
deck(i) = i
Next i
With Application.WorksheetFunction
'do k*players steps of a Fisher-Yates shuffle on deck
For i = 1 To .Min(k * players, n - 1)
j = .RandBetween(i, n)
If i < j Then 'swap
temp = deck(i)
deck(i) = deck(j)
deck(j) = temp
End If
Next i
End With
ReDim Preserve deck(1 To k * players)
If players = 1 Then
deal = deck
Exit Function
Else
ReDim hands(1 To k, 1 To players)
For i = 1 To k
For j = 1 To players
hands(i, j) = deck(players * (i - 1) + j)
Next j
Next i
deal = hands
End If
End Function
It could be used like:
Sub ManyHands()
Dim i As Long
For i = 1 To 1000
Range(Cells(1 + 11 * (i - 1), 1), Cells(11 * i - 1, 2)).Value = deal(52, 10, 2)
Next i
End Sub
Try:
Sub cards()
Dim cardstodraw As Long, numberofhands As Long, i As Long, j As Long, k As Long
cardstodraw = 20
numberofhands = 50
Range("A:A").Clear
With Application.WorksheetFunction
For j = 0 To numberofhands - 1
For i = 1 To cardstodraw
begL:
Cells(i + k + (j * cardstodraw), 1) = .RandBetween(1, 52)
If .CountIf(Range(Cells(1 + k + (j * cardstodraw), 1), Cells(20 + k + (j * cardstodraw), 1)), Cells(i + k + (j * cardstodraw), 1)) > 1 Then GoTo begL
Next i
k = k + 1
Next j
End With
End Sub

Listing all possible combination without repetition,VBA

I have a code that work right now and lists numbers at 6 lenght.But they are repetive.But these numbers are repeated.I need unique non-repeated 6 digit.
I have this kind of results right now.1 1 1 3 4 6 but i need different and non repeating results.Thank you for helping me.
Sub AllCombinations()
Dim nums(): nums = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
Dim arValues(999999, 5)
Dim n1 As Integer, n2 As Integer, n3 As Integer, n4 As Integer, n5 As Integer, n6 As Integer, x As Long
For n1 = 0 To UBound(nums)
For n2 = 0 To UBound(nums)
For n3 = 0 To UBound(nums)
For n4 = 0 To UBound(nums)
For n5 = 0 To UBound(nums)
For n6 = 0 To UBound(nums)
arValues(x, 0) = nums(n1)
arValues(x, 1) = nums(n2)
arValues(x, 2) = nums(n3)
arValues(x, 3) = nums(n4)
arValues(x, 4) = nums(n5)
arValues(x, 5) = nums(n6)
x = x + 1
Next
Next
Next
Next
Next
Next
Range("A1").Resize(1000000, 6).Value2 = arValues
End Sub
As it stands, if you wanted to find combinations of different length or of an array with a different Ubound, you would have to alter your code. This can become very tedious and prone to error. Here is a more general solution that works for arrays of any type, any size, and any length of output.
Sub CombosNoRep(ByRef v() As Variant, r As Long)
Dim i As Long, k As Long, z() As Variant, comboMatrix() As Variant
Dim numRows As Long, numIter As Long, n As Long, count As Long
count = 1
n = UBound(v)
numRows = nChooseK(n, r)
ReDim z(1 To r)
ReDim comboMatrix(1 To numRows, 1 To r)
For i = 1 To r: z(i) = i: Next i
Do While (count <= numRows)
numIter = n - z(r) + 1
For i = 1 To numIter
For k = 1 To r: comboMatrix(count, k) = v(z(k)): Next k
count = count + 1
z(r) = z(r) + 1
Next i
For i = r - 1 To 1 Step -1
If Not (z(i) = (n - r + i)) Then
z(i) = z(i) + 1
For k = (i + 1) To r: z(k) = z(k - 1) + 1: Next k
Exit For
End If
Next i
Loop
Range("A1").Resize(numRows, r).Value2 = comboMatrix
End Sub
Function nChooseK(n As Long, k As Long) As Long
''returns the number of k-combinations from a set
''of n elements. Mathematically speaking, we have: n!/(k!*(n-k)!)
Dim temp As Double, i As Long
temp = 1
For i = 1 To k: temp = temp * (n - k + i) / i: Next i
nChooseK = CLng(temp)
End Function
Calling it we have:
Sub Test()
Dim myArray(1 To 9) As Variant, i As Long
For i = 1 To 9: myArray(i) = i: Next i
Call CombosNoRep(myArray, 6)
End Sub
This quickly outputs all 84 unique combinations.
Let's try it on an array with strings.
Sub Test()
Dim myArray() As Variant, i As Long
'' Added blank "" as CombosNoRep is expecting base 1 array
myArray = Array("", "Canada", "England", "Laos", "Ethiopia", "Burma", "Latvia", "Serbia", "Chile", "France", "Tonga")
Call CombosNoRep(myArray, 4)
End Sub
Here we have all 4-tuples of our array of countries (210 unique combinations).
Stagger the nested loops:
Sub AllCombinations()
Dim nums(): nums = Array(1, 2, 3, 4, 5, 6, 7, 8, 9)
Dim arValues(999999, 5)
Dim n1 As Integer, n2 As Integer, n3 As Integer, n4 As Integer, n5 As Integer, n6 As Integer, x As Long
For n1 = 0 To UBound(nums)
For n2 = n1 + 1 To UBound(nums)
For n3 = n2 + 1 To UBound(nums)
For n4 = n3 + 1 To UBound(nums)
For n5 = n4 + 1 To UBound(nums)
For n6 = n5 + 1 To UBound(nums)
arValues(x, 0) = nums(n1)
arValues(x, 1) = nums(n2)
arValues(x, 2) = nums(n3)
arValues(x, 3) = nums(n4)
arValues(x, 4) = nums(n5)
arValues(x, 5) = nums(n6)
x = x + 1
Next
Next
Next
Next
Next
Next
Range("A1").Resize(1000000, 6).Value2 = arValues
End Sub
for all 84 unique combinations.

Resources