Solving a large system of equations using VBA - excel

I'm trying to solved a system of equations that results from estimating a 4th order differential equation. To do this requires creating a large matrix (A), usually 105 x 105, taking the inverse and multiplying by a 105 x 1 matrix (B). To do this, I'm using the linear algebra approach solving Ax = B.
Running the following code:
Dim A(1 To 105, 1 To 105) As Double
Dim B(1 To 105) As Double
Dim i As Integer
' Used to make sure all values of A are initialized to zero
For i = 1 To 105
For j = 1 To 105
A(i, j) = 0
Next
Next
For i = 1 To 105
A(i, i) = EI
A(i, i + 1) = -4 * EI + axial * h ^ 2
A(i, i + 2) = 6 * EI - 2 * axial * h ^ 2 + km(i) * h ^ 4
A(i, i + 3) = -4 * EI + axial * h ^ 2
A(i, i + 4) = EI
B(i) = W * h ^ 4
Next
Dim x(1 To 105) As Variant
x = Application.WorksheetFunction.MMult((Application.WorksheetFunction.MInverse(A)), B)
results in "Run-time error '1004': Unable to get the MInverse property of the WorksheetFunction class"
I've explored this error and it appears that it means I'm passing bad data to the function either text or blank values so I added the two loops at the top to initialize matrix A to 0 however this did nothing. After exploring some more I found some obscure post about a maximum size of matrices being 52 x 52 but wasn't able to find any more information about this.

Yes, there is a limit of 52 x 52, as mentioned in the official documentation.
https://msdn.microsoft.com/en-us/library/microsoft.office.interop.excel.worksheetfunction.minverse.aspx
EDIT
The documentation seems to be wrong. I tested =MINVERSE(A1:DA105) and it works well.
Very likely your matrix contains numbers that generate very large numbers and it fails. I populated my matrix 105 rows with numbers from 1 to 105 and it was failing. Then I put =RAND() in all the cells and it worked.
If the large numbers are the problem, perhaps there are tricks like populating the matrix with the logarithm of the numbers. It's a trick I use in other cases, I don't know if it can be used with a matrix.

Related

Unexpected modulo behavior

In excel, I have this formula:
MOD(-10 + 9, 12) + 1
And the expected result is 12
However, in PowerQuery the same formula:
Number.Mod(-10 + 9, 12) + 1
Results in 0
The strange thing is that for other numbers ( -1 ) I get the same result in both systems..
I expect this to have something to do with the nature of MOD, and how I'm using negative numbers.. But I would still like to know which is 'correct'..
I found the answer here:
https://www.youtube.com/watch?v=K4ImPRsi3vg&ab_channel=ExcelIsFun
MOD(n, d) = n - d * INT(n/d)
Number.Mod(n, d) = n - d * TRUNC(n/d)
They are calculated in different ways.

VBA generating random numbers between -35 to +5

I'm trying to code a line in VBA that would allow me to generate random numbers between -35 and 5. I've used the below formula however, it gives me random numbers between -5 and +5
Int((Rnd() * ((-1) ^ Int(Rnd() * 10))) * 5)
Fix(Rnd() * 41) - 35
will do it. This will be uniformly distributed insofar that Rnd() is. Fix scrubs the decimal portion of a number. You want every number between -35 and 5 inclusive which is 41 numbers. rnd() itself never draws 1.
Test the generator with something like
Sub test()
Dim j As Long
While True
j = Fix(Rnd() * 41) - 35
VBA.DoEvents 'so you can break the program once you're done
If j < -35 Or j > 5 Then
Stop 'oops
End If
If j = -35 Then
Debug.Print "Yup, I draw on the boundary"
End If
If j = 5 Then
Debug.Print "Yup, I draw on the boundary"
End If
Wend
End Sub
If you want an uniform distribution:
Int(Rnd*41)-35
0<=Rnd<1 (some time back I found Rnd=1, but it seams not happening any more)
0<=Rnd*41<41
0<=Int(Rnd*41)<=40
-35<=Int(Rnd*41)-35<=5
From documentation:
Int
Returns the integer portion of a number.

Simple python and need explanation

m = 0
for x in range (4,6):
for y in range (2,4):
m = m + x + y
print (m)
ANSWER: 28
not sure how this is? Excluding the last number in the range, I thought it should be 14. I add it up on paper and cannot understand what I am doing wrong.
That loop is equivalent to:
m = 4+2 + 4+3 + 5+2 + 5+3
And, that sum is 28.
(In the outer loop, x takes on the values 4 and 5. In the inner loop, y takes on values 2 and 3.)

Monte Carlo simulation for tossing a coin to get a certain pattern

Inspired by this article: Statistics of Coin-Toss Patterns, I have conducted a Monte Carlo simulation for determining the expected number of tossing a coin to get a certain pattern by using Excel VBA. The following code is the Monte Carlo simulation for tossing a fair coin to get pattern HTH, where H is head (1) and T is tail (0).
Sub Tossing_Coin()
Dim Toss(1000000) As Double, NToss(1000000) As Double, AVToss(1000000) As Double
t0 = Timer
Sheet2.Cells.Clear
a = 0
For j = 1 To 1000000
p1 = Rnd()
If p1 <= 0.5 Then
Toss(1) = 1
Else
Toss(1) = 0
End If
p2 = Rnd()
If p2 <= 0.5 Then
Toss(2) = 1
Else
Toss(2) = 0
End If
i = 2
Do
p3 = Rnd()
If p3 <= 0.5 Then
Toss(i + 1) = 1
Else
Toss(i + 1) = 0
End If
i = i + 1
Loop Until Toss(i - 2) = 1 And Toss(i - 1) = 0 And Toss(i) = 1
NToss(j) = i
a = a + NToss(j)
AVToss(j) = a / j
b = AVToss(j)
Next j
MsgBox "The expected number of tossing is " & b & "." _
& vbNewLine & "The running time of simulation is " & Round(Timer - t0, 2) & " s."
End Sub
The output of the program is as shown below:
which agrees with the result as shown in the article. Other patterns for tossing a fair coin are also match. Despite the results, I'm still feeling uncertain whether the program I wrote is correct or not. My doubt arises when the coin is unfair, meaning p1, p2, and p3 are not equal to 0.5, since I don't have any information to check its accuracy. I also want to know how to write an efficient program in VBA Excel or R to perform simulation above for a longer pattern like THTHTHTHT, THTTHHTHTTH, etc. and its looping is more than 1,000,000 (maybe 100,000,000 or 1,000,000,000) but still pretty fast? Any idea?
To make it more efficient, you could use the bits of a variable by assigning a bit with a toss. Then for each toss, rotate the bits on the left and add the toss result at the first position until the bits on the right are a match with the pattern :
pattern "HTH" : 101
mask for "XXX" : 111
1 toss "H" : 1 And 111 = 001
2 toss "T" : 10 And 111 = 010
3 toss "T" : 100 And 111 = 100
4 toss "H" : 1001 And 111 = 001
5 toss "H" : 10011 And 111 = 011
6 toss "T" : 100110 And 111 = 110
7 toss "H" : 1001101 And 111 = 101 : "HTH" matches the first 3 bits
Note that VBA doesn't have a bit shift operator, but shifting 1 bit on the left is the same as multiplying by 2 :
decimal 9 = 1001 in bits
9 + 9 = 18 = 10010 in bits
18 + 18 = 36 = 100100 in bits
Here is an example to get the average number of toss to match a sequence:
Sub UsageExample()
Const sequence = "HTH"
Const samples = 100000
MsgBox "Average: " & GetTossingAverage(sequence, samples)
End Sub
Function GetTossingAverage(sequence As String, samples As Long) As Double
Dim expected&, tosses&, mask&, tossCount#, i&
Randomize ' Initialize the random generator. '
' convert the [TH] sequence to a sequence of bits. Ex: HTH -> 00000101 '
For i = 1 To Len(sequence)
expected = expected + expected - (Mid$(sequence, i, 1) = "T")
Next
' generate the mask for the rotation of the bits. Ex: HTH -> 01110111 '
mask = (2 ^ (Len(sequence) * 2 + 1)) - (2 ^ Len(sequence)) - 1
' iterate the samples '
For i = 1 To samples
tosses = mask
' generate a new toss until we get the expected sequence '
Do
tossCount = tossCount + 1
' rotate the bits on the left and rand a new bit at position 1 '
tosses = (tosses + tosses - (Rnd < 0.5)) And mask
Loop Until tosses = expected
Next
GetTossingAverage = tossCount / samples
End Function
You'll need one string to store pattern you want to find.
Then after each toss append latest result onto end of results string.
Then check if last n digits of results string == pattern where n = length of pattern.
If match then record numbers of tosses and blank results string and go again ...
You could probably do it in about 20 lines of code! Hope that helps.

"Consolidation" algorithm name / implementation

Not quite sure how to describe this, but I have a word game I like to play that I'd like to implement as a computer program.
The basic gist is that you look at the values of the letters (A=1..Z=26), and consolidate the letters into the fewest possible, and that are the closest possible to each other.
As an example:
s t a c k
Sum the values
19 + 20 + 1 + 3 + 11 = 54
Find the fewest number of letters:
ceil(54/26) = 3
Choose letters closest to each other
54/3 = 18
Letters to be displayed should be rrr.
That happens to be an easy example. What would it look like when you need to have, say, rrs (if your initial string was 'a stack' instead)?
Does this already have a name that I can lookup and implement?
I think your problem boils down to this: given n and k, find numbers r1, r2, ..., rk such that sum(r1 + r2 + ... + rk) = n and max(r1, r2, ..., rk) - min(r1, r2, ..., rk) is as small as possible.
The solution is pick r = floor(n / k), and set n mod k of the numbers to be r + 1, and the rest r.
For example, if n = 55 and k = 3 (your example), we have floor(55/3) = 18 and 55 mod 3 is 1, so the solution is 19, 18, 18.
All that remains is converting between numbers and letters.

Resources