VBA - Function for Creating an n by m Matrix - excel

I am currently working with arrays and loops, and am trying to write a function that will output an n by m array (a matrix) with the numbers {1, 2, 3, ... , n*m}
I am trying to learn some basic VBA code, this is purely for educational purposes.
This is what I have come up with:
Function createMatrix(n, m)
Dim matrix(1 To n, 1 To m) As Integer
x = 1
For i = 1 To n
For j = 1 To m
matrix(i, j) = x
x = (x + 1)
Next j
Next i
createMatrix = matrix
End Function
It returns #VALUE. I cannot understand why.
I got it to work at one point (creating a 3x3 matrix) by making it a function that did not take any variables and then initializing the matrix array by
Dim matrix(1 to 3, 1 to 3) As Integer
replacing n and m in the for loops with 3s.
So I guess the variables n and m are causing the problems, but don't know why.

Array declarations must be static (where the bounds are defined by a hardcoded value); however you can resize them dynamically using the ReDim statement.
' Declare an array.
' If you want to size it based on variables, do NOT define bounds.
Dim matrix() As Integer
' Resize dynamically.
ReDim maxtrix(n, m)
Note that when you ReDim, all values will be lost. If you had values in matrix that you wanted to keep, you can add the Preserve keyword:
ReDim Preserve matrix(n, m) ' Keep any existing values in their respective indexes.

You first need to declare array as dynamic array and then redim it to your dimension.
Function createMatrix(n, m)
Dim matrix() As Integer
ReDim matrix(1 To n, 1 To m) As Integer
x = 1
For i = 1 To n
For j = 1 To m
matrix(i, j) = x
x = (x + 1)
Next j
Next i
createMatrix = matrix
End Function

Related

Need help in calculating IRR using custom function in Excel

I am using an excel sheet to track all my investments. In order to calculate my IRR, I need to have the values entered in a specific way for Excel to calculate. So I decided to create a custom function. I will feed this custom function the following values.
Total Investment
Time Period of investment
Final Value of the investment.
I used the following code for creating a custom function. But I get the #VALUE error
Function ROI(fundInvested, timePeriod, finalValue)
eachValue = fundInvested / timePeriod
Dim cashFlow() As Double
Dim n As Integer
For n = 0 To (timePeriod - 1)
cashFlow(n) = -1 * eachValue
Next n
cashFlow(timePeriod) = finalValue
ROI = IRR(cashFlow)
End Function
Where is my formula wrong?
Since you tagged it formula:
=IRR(CHOOSE(INT((ROW($ZZ$1:INDEX($ZZ:$ZZ,B2+1))-1)/B2)+1,-1*A2/B2,C2))
Depending on one's version this may need to be confirmed with Ctrl-Shift-Enter instead of Enter when exiting edit mode.
So you need to reDim it like this:
Function ROI(fundInvested, timePeriod, finalValue)
eachValue = fundInvested / timePeriod
Dim cashFlow() As Double
Dim n As Integer
ReDim cashFlow(0 to timePeriod)
'ReDim cashFlow(timePeriod) is also correct - see #Chris Nielsen comment.
For n = 0 To (timePeriod - 1)
cashFlow(n) = -1 * eachValue
Next n
cashFlow(timePeriod) = finalValue
ROI = IRR(cashFlow)
End Function
Notes
-In absence of Option Base statement, array subscripts start at zero.
-Putting both lower and upper limits in ReDim statement is recommended, but if the lower limit is omitted and only the upper limit is specified, the lower limit is taken from the Option Base currently in operation, in this case zero so
ReDim cashFlow(0 to timePeriod)
and
ReDim cashFlow(timePeriod)
are equivalent.
Try below:
Function ROI(fundInvested, timePeriod, finalValue)
eachValue = fundInvested / timePeriod
Dim cashFlow() As Double
Dim n As Integer
For n = 0 To (timePeriod - 1)
ReDim Preserve cashFlow(n)
cashFlow(n) = -1 * eachValue
Next n
ReDim Preserve cashFlow(timePeriod)
cashFlow(timePeriod) = finalValue
ROI = WorkSheetFunction.IRR(cashFlow)
End Function

Excel VBA: Transpose Vector and Matrix Multiply in a function

I have an 3x3 matrix "mat" and a vector "vec" (3x1) of real numbers I want to matrix multiply (in the linear algebra sense) in a VBA function like so: t(vec)matvec to produce a 1x1 real number I can use in an equation.
I do not want to interact with a worksheet in the function. The values in the matrix and vector are eiter hard-coded or calculated from within the function. There should be a simple way to transpose then do a couple matrix multiplications like in MATLAB or R. Here is where I am so far:
Public Function QuickMaths()
Dim vec As Variant
Dim mat As Variant
mat = Array(Array(1,1+1,3), _
Array(2^2,5,6), _
Array(7,8,9))
vec = Array(2*5,11,12)
QuickMaths = Application.WorksheetFunction.MMult(Application.WorksheetFunction.MMult(Application.WorksheetFunction.Transpose(vec), mat), vec)
End Function
I get #VALUE from this in a worksheet when I run it. I would expect the output to be a 1x1 matrix, but I don't know if Excel VBA would consider that a scalar that can be output into a sheet as a single value (e.g. Double).
Please send help.
It would have been good if you had provided expected output (the specific scalar you're expecting at the end).
Based on what I gather from your code and question, I'm going to assume you're trying to perform two steps. The first being:
The second being:
(It's been a while since I've done any matrix multiplication, so if you think I've misunderstood, let me know.)
Your first array (mat) is an array of arrays (not a two dimensional array), which I don't think MMULT handles (https://support.office.com/en-us/article/mmult-function-40593ed7-a3cd-4b6b-b9a3-e4ad3c7245eb). So you might need to replace:
mat = Array(Array(1, 1 + 1, 3), _
Array(2 ^ 2, 5, 6), _
Array(7, 8, 9))
with:
ReDim mat(0 To 2, 0 To 2)
mat(0, 0) = 1
mat(0, 1) = 2
mat(0, 2) = 3
mat(1, 0) = 4
mat(1, 1) = 5
mat(1, 2) = 6
mat(2, 0) = 7
mat(2, 1) = 8
mat(2, 2) = 9
That said, manually assigning each array element can be impractical, so maybe make a small function to do it for you (see FlattenAnArrayOfArrays function in code below).
From what I've read online in the last 30 minutes, matrix multiplication is not commutative and also requires that the number of columns in your first matrix match the number of rows in the second matrix. (You may already know all of this, but just mentioning it anyway.)
Based on the above, your code might look something like:
Option Explicit
Public Function QuickMaths() As Variant
' This function returns a value of type Variant.
' Could return a Long/Double/numeric type; scalar should be at QuickMaths(1,1)
' But MMULT can return non-numeric values, so you risk
' getting a type mismatch error if the matrix multiplication
' is not successful (for whatever reason).
' Maybe this shouldn't be this function's concern -- or maybe it should.
Dim mat As Variant
mat = Array(Array(1, 1 + 1, 3), _
Array(2 ^ 2, 5, 6), _
Array(7, 8, 9))
mat = FlattenAnArrayOfArrays(mat)
Dim vec As Variant
vec = Array(2 * 5, 11, 12)
Dim resultantMatrix As Variant
resultantMatrix = Application.MMult(vec, mat) ' Number of columns in "vec" must match number of rows in "mat"
resultantMatrix = Application.MMult(vec, Application.Transpose(resultantMatrix))
QuickMaths = resultantMatrix
End Function
Private Function FlattenAnArrayOfArrays(ByRef arrayOfArrays As Variant) As Variant()
' Given an array of arrays, returns a two-dimensional array.
' This function is very basic and has no error handling implemented.
Dim firstArray() As Variant
firstArray = arrayOfArrays(LBound(arrayOfArrays)) ' Columns inferred from first array in "arrayOfArrays"
Dim outputArray() As Variant
ReDim outputArray(LBound(arrayOfArrays) To UBound(arrayOfArrays), LBound(firstArray) To UBound(firstArray))
Dim rowIndex As Long
For rowIndex = LBound(outputArray, 1) To UBound(outputArray, 1)
Dim columnIndex As Long
For columnIndex = LBound(outputArray, 2) To UBound(outputArray, 2)
outputArray(rowIndex, columnIndex) = arrayOfArrays(rowIndex)(columnIndex)
Next columnIndex
Next rowIndex
FlattenAnArrayOfArrays = outputArray
End Function
Closing points:
The return value of the QuickMaths function is a 1x1 matrix, but you can assign it to a cell's value.
Similarly, if you call the QuickMaths function from a worksheet cell, the cell will display the return value (without any issues or need for an array formula).

Access VBA to Excel, add apostrophe [duplicate]

Having a problem with this Error. I am creating a GA and the loop is to assign my fitness value to an array.
some of the variables
Dim Chromolength as integer
Chromolength = varchromolength * aVariables
Dim i as integer, j as integer, counter as integer
Dim Poparr() As Integer
Dim FitValarr() As Integer
the code:
ReDim Poparr(1 To PopSize, 1 To Chromolength)
For i = 1 To PopSize
For j = 1 To Chromolength
If Rnd < 0.5 Then
Poparr(i, j) = 0
Else
Poparr(i, j) = 1
End If
Next j
Next i
For i = 1 To PopSize
j = 1
counter = Chromolength
Do While counter > 0
FitValarr(i) = FitValarr(i) + Poparr(i, counter) * 2 ^ (j - 1)
j = j + 1
counter = counter - 1
Loop
Next i
I am having problems with:
FitValarr(i) = FitValarr(i) + Poparr(i, counter) * 2 ^ (j - 1)
I apologize, I am fairly new to VBA.
An overflow condition arises when you create an integer expression that evaluates to a value larger than can be expressed in a 16-bit signed integer. Given the expression, either the contents of FitValarr(i), or the expression 2^(j-1) could be overflowing. Suggest all the the variables presently declared as Int be changed to Long. Long integers are 32-bit signed values and provide a correspondingly larger range of possible values.
I had the same run time error 6. After much investigation l discovered that mine was a simple 'divide by zero' error.
I set up an integer value to hold Zip codes, and Error 6 events plagued me - until I realized that a zip code of 85338 exceeded the capacity of an int...
While I didn't think of a zip code as a "value" it was nonetheless certainly interpreted as one. I suspect the same could happen with addresses as well as other "non-numeric" numeric values. Changing the variable to a string resolved the problem.
It just didn't occur to me that a zip code was a "numeric value." Lesson learned.

Choose a, c, m in Linear Congruential Generator

I am looking to implement a linear congruential generator in Excel.
As we know, we must choose the parameter of LCG is a, c, m, and Z0.
Wikipedia says that
The period of a general LCG is at most m, and for some choices of
factor a much less than that. The LCG will have a full period for all
seed values if and only if:
m and the offset c are relatively prime,
a - 1 is divisible by all prime factors of m,
a - 1 is divisible by 4 if m is divisible by 4.
Also,
m, 0 < m – the "modulus"
a, 0 < a < m – the "multiplier"
c, 0 < c < m – the "increment"
Z0, 0 < Z0 < m – the "seed" or "start value"
I need to choose those values, I want Z0 initial value is 10113383, and the rest is random. Nah, what values that has a specified period and guaranteed no collisions for the duration of that period?
I've tried to put some values, a=13, c=911, m=11584577 and it looks no collisions. But I'm not sure if I break the rules or not.
I regularly teach a number theory and cryptography course so have built up a library of programs in VBA and Python. Using these, I only needed to write one more:
Function GCD(num1 As Long, num2 As Long) As Long
Dim a As Long
Dim b As Long
a = num1
b = num2
Dim R As Long
R = 1
Do Until R = 0
R = a Mod b
a = b
b = R
Loop
GCD = a
End Function
Sub Helper_Factor(ByVal n As Long, ByVal p As Long, factors As Collection)
'Takes a passed collection and adds to it an array of the form
'(q,k) where q >= p is the smallest prime divisor of n
'p is assumed to be odd
'The function is called in such a way that
'the first divisor found is automatically prime
Dim q As Long, k As Long
q = p
Do While q <= Sqr(n)
If n Mod q = 0 Then
k = 1
Do While n Mod q ^ k = 0
k = k + 1
Loop
k = k - 1 'went 1 step too far
factors.Add Array(q, k)
Helper_Factor n / q ^ k, q + 2, factors
Exit Sub
End If
q = q + 2
Loop
'if we get here then n is prime - add it as a factor
factors.Add Array(n, 1)
End Sub
Function Factor(ByVal n As Long) As Collection
Dim factors As New Collection
Dim k As Long
Do While n Mod 2 ^ k = 0
k = k + 1
Loop
k = k - 1
If k > 0 Then
n = n / 2 ^ k
factors.Add Array(2, k)
End If
If n > 1 Then Helper_Factor n, 3, factors
Set Factor = factors
End Function
Function DisplayFactors(n As Long) As String
Dim factors As Collection
Dim i As Long, p As Long, k As Long
Dim sfactors As Variant
Set factors = Factor(n)
ReDim sfactors(1 To factors.Count)
For i = 1 To factors.Count
p = factors(i)(0)
k = factors(i)(1)
sfactors(i) = p & IIf(k > 1, "^" & k, "")
Next i
DisplayFactors = Join(sfactors, "*")
End Function
Function MaxPeriod(a As Long, c As Long, m As Long) As Boolean
'assumes 0 < a,c < m
Dim factors As Collection
Dim p As Long, i As Long
If GCD(c, m) > 1 Then Exit Function 'with default value of False
If m Mod 4 = 0 And (a - 1) Mod 4 > 0 Then Exit Function
'else:
Set factors = Factor(m)
For i = 1 To factors.Count
p = factors(i)(0)
If p < m And (a - 1) Mod p > 0 Then Exit Function
Next i
'if you survive to here:
MaxPeriod = True
End Function
For example, in the Intermediate Window you can check:
?maxperiod(13,911,11584577)
True
so you seem to be in luck

Counting rows in VBA excel

I'm designing a function in VBA of the form myFunction(x,y,z) where z is a table, and x can take the values of the column headings. As part of the function I need to find the number of rows in z.
I'm having problems with this, as everywhere I look suggests using length = z.Rows.Count, but when I try and output this value (as in, set myFunction = length), it produces a VALUE error. However, when I output myFunction = a which doesn't directly use length (it will eventually form part of an IF statement once I get it working), the function works fine. My code is below:
Public Function myFunction(x As String, y As Double, z As Range) As Double
Dim upper_threshold As Double
Dim lower_threshold As Double
Dim a As Double
Dim rates As Variant
Dim u As Byte
Dim l As Byte
Dim r As Byte
Dim length As Byte
a = 0
u = 2
l = 1
rates = Application.WorksheetFunction.Index(z, 1, 0)
r = Application.WorksheetFunction.Match(x, rates, 0)
length = z.rows.Count
upper_threshold = z(u, 1)
Do While y > upper_threshold
u = u + 1
l = l + 1
upper_threshold = z(u, 1)
lower_threshold = z(l, 1)
If y < upper_threshold Then
a = a + z(l, r) * (y - lower_threshold)
Else
a = a + z(l, r) * (upper_threshold - lower_threshold)
End If
Loop
myFunction = a
End Function
To test it out I also created another function:
Public Function myRows(myTable As Range) As Double
myRows = myTable.rows.Count
End Function
This one works fine on its own, but when I try to use it within the other function, I still get a VALUE error. I've tried declaring length as every type I can think of and it doesn't seem to help.
Can anyone see what's going on?
EDIT: I'm obviously not making myself very clear. The function without the two lines referring to length works as I intended. However, I need to add a bit of code to increase its functionality and this involves calculating the number of rows in the table z. When I add the two lines shown here into the function it continues to work, since it doesn't affect the output. However, if I then set the output to show length, i.e. change the penultimate line to myFunction = length it gives me a VALUE error. This leaves me with two options as far as I can see: either something else in the program is impacting on these two lines (some clashes of syntax or something), or I'm making a mistake in just assuming I can output length like that.
Your problem is with:
rates = Application.WorksheetFunction.Index(z, 1, 0)
Index only accepts a single row or column, otherwise you get a VALUE error.

Resources