I have 2 arrays. Array1 is n * n and Array2 is 1 * n.
These arrays are given in worksheets. In this case Sheet3 and Sheet4 and I need to output the answer on Sheet5.
I get multiple errors like "Subscript out of range".
I can't seem to figure out why this isn't working:
Public Sub LinearSystemSolver()
x = Sheet3.UsedRange.Rows.Count
y = Sheet3.UsedRange.Columns.Count
Z = Sheet4.UsedRange.Rows.Count
Dim a As Variant
ReDim a(1 To x, 1 To y)
Dim b As Variant
ReDim b(1 To Z, 1 To 1)
Dim g As Variant
ReDim g(1 To Z, 1 To 1)
For i = 1 To x
For j = 1 To y
a(i, j) = Sheet3.Cells(i, j)
Next
Next
For f = 1 To Z
b(f,1) = Sheet4.Cells(f,1)
Next
g = Application.WorksheetFunction.MMult((Application.WorksheetFunction.MInverse(a)), b)
For h = 1 To Z
Sheet5.Cells(h, 1) = g(h, 1)
Next
End Sub
You can speed up your code by assigning to the arrays directly and avoid loops
a = Sheet3.Range("A1").Resize(x,y).Value
b = Sheet4.Range("A1").Resize(z,1).Value
...
Sheet5.Range("A1").Resize(z,1).Value = g
Now as far as inverting the matrix (if x=y=z) the I propose to use LU decomposition. I have attached a working example which I have used for many years.
The driver code is
Private Sub solveButton_Click()
Dim lu As New LuSolver
' Get Matrix values and decompose them into L, U, P form
' Values are in B3 and matrix is a 5×5 size
lu.IntializeFromRange Range("B3"), 5
' Solve the A*x=b matrix system for x
' right hand side is in J3 and it is a 5×1 size
' resulting 5×1 matrix will be placed under H3
lu.Solve Range("J3"), 1, Range("H3")
End Sub
with the LU solver in a class called 'LuSolver"
'---------------------------------------------------------------------------------------
' Module : LuSolver
' DateTime : 6/30/2008 13:01
' Author : ja72
' Purpose : LU Decomposition of rectangular matrix.
' Remarks:
'For an n-by-n matrix A, the LU decomposition is an n-by-n
'unit lower triangular matrix L, an n-by-n upper triangular matrix U,
'and a permutation vector piv of length n so that A(piv)=L*U.
'---------------------------------------------------------------------------------------
Option Explicit
Private lu As Variant
Private sign As Integer
Private pivot() As Integer
Private size As Integer
Private Sub Class_Initialize()
Set lu = Nothing
Erase pivot
sign = 1
End Sub
Private Sub Class_Terminate()
Set lu = Nothing
Erase pivot
sign = 0
End Sub
Public Sub IntializeFromRange(ByRef r_coef As Range, ByVal matrix_size As Integer)
Dim k_max As Integer, k As Integer, p As Integer
Dim i As Integer, j As Integer
Dim s As Variant
On Error GoTo IntializeFromRange_Error
lu = r_coef.Resize(matrix_size, matrix_size).Value
size = matrix_size
'Set pivot as a sequence of integers
ReDim pivot(1 To size)
For i = 1 To size
pivot(i) = i
Next i
sign = 1
For j = 1 To size
'Apply previous transformations
For i = 1 To size
If j > i Then k_max = i Else k_max = j
s = 0
'Time consuming dot product
For k = 1 To k_max - 1
s = s + lu(i, k) * lu(k, j)
Next k
lu(i, j) = lu(i, j) - s
Next i
'Find the pivot element
p = j
For i = j + 1 To size
If Abs(lu(i, j)) > Abs(lu(p, j)) Then
p = i
End If
Next i
'Exchange pivot rows
If p <> j Then
For k = 1 To size
s = lu(p, k)
lu(p, k) = lu(j, k)
lu(j, k) = s
Next k
k = pivot(p)
pivot(p) = pivot(j)
pivot(j) = k
sign = -sign
End If
'Compute Multipliers
s = lu(j, j)
If j <= size And s <> 0 And s <> 1 Then
For i = j + 1 To size
lu(i, j) = lu(i, j) / s
Next i
End If
Next j
On Error GoTo 0
Exit Sub
IntializeFromRange_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure IntializeFromRange of Class Module LuDecomposition"
End Sub
Public Property Get IsSingular() As Boolean
IsSingular = Not IsNonSingular
End Property
Public Property Get IsNonSingular() As Boolean
IsNonSingular = True
Dim j As Integer
For j = 1 To size
If lu(j, j) = 0 Then
IsNonSingular = False
Exit Property
End If
Next j
End Property
Public Sub Solve(ByRef r_rhs As Range, ByVal no_of_columns, ByRef r_result As Range)
On Error GoTo Solve_Error
Dim rhs As Variant
Dim N As Integer, M As Integer, r As Integer
Dim i As Integer, j As Integer, k As Integer
N = size
M = size
r = no_of_columns
rhs = r_rhs.Resize(size, r).Value
'Copy rhs with pivoting
Dim X As Variant
ReDim X(1 To size, 1 To r)
For i = 1 To size
For j = 1 To r
X(i, j) = rhs(pivot(i), j)
Next j
Next i
'Solve L*Y = B
For k = 1 To M
For i = k + 1 To M
For j = 1 To r
X(i, j) = X(i, j) - X(k, j) * lu(i, k)
Next j
Next i
Next k
'Solve U*X=Y
For k = M To 1 Step -1
For j = 1 To r
X(k, j) = X(k, j) / lu(k, k)
Next j
For i = 1 To k - 1
For j = 1 To r
X(i, j) = X(i, j) - X(k, j) * lu(i, k)
Next j
Next i
Next k
r_result.Resize(size, no_of_columns).Value = X
On Error GoTo 0
Exit Sub
Solve_Error:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure Solve of Class Module LuDecomposition"
End Sub
In the following, loop Cells() needs two arguments:
For f = 1 To Z
b(f) = Sheet4.Cells(f)
Next
There may be other problems.
Related
The task I am doing requires to read and print a matrix from txt file, then create a new matrix B whose elements are the average of the row and column from A matrix, and then find the lowest valued number in the B matrix, print it and print its indexes (If the element "1" is the lowest and it is in the 2nd row and 3rd column, there should be printed below the matrix B "The lowest element is 1 with indexes 2;3".
For example the element B23 should be the average of the sum of the elements of row 2 and column 3 of matrix A. There is a short matrix example in the code below.
The input for the A matrix is coming from txt file, where on the first row are m and n (rows and columns) and below them is the actual matirx.
Example:
Thank you in advance!
Here is the code:
`
" Example for input
3 3
2 9 8
4 2 5
5 2 3
Expected answear:
Matrix A
2.00 9.00 8.00
4.00 2.00 5.00
5.00 2.00 3.00
Matrix B
15.00 16.00 17.50
11.00 12.00 13.50
10.50 11.50 13.00
The lowest element is 10.50 with indexes 3,1.
Option Explicit
Sub Matrix()
Dim m As Integer, n As Integer, A() As Single, _
MaxA As Single, r_Max As Integer
Call InputMatrix(m, n, A)
Call NewMatrixB(A, m)
End Sub
Sub InputMatrix(m As Integer, n As Integer, A() As Single)
Dim i As Integer, j As Integer
Dim FName As String
FName = InputBox("Vuvedete ime na fail s vhodni danni", _
"matrix", "H:\School\matrix.txt")
If Dir(FName) = "" Then
MsgBox ("Failut " & FName & " ne e nameren!")
Stop
End If
Close #1
Open FName For Input As #1
Input #1, m, n
ReDim A(m, n)
For i = 1 To m
For j = 1 To n
Input #1, A(i, j)
Next j
Next i
Close #1
Worksheets("Sheet1").Activate
Cells.Clear
Call OutMatrix(m, n, A, 1, "Matrix A")
End Sub
Sub OutMatrix(m As Integer, n As Integer, A() As Single, _
r As Integer, title As String)
Dim i As Integer, j As Integer
With Cells(r, 1)
.Value = title
.Font.Size = 14
.Font.Bold = True
End With
For i = 1 To m
For j = 1 To n
Cells(r + i, j).Value = A(i, j)
Cells(r + i, j).NumberFormat = "0.00"
Next j
Next i
End Sub
Sub NewMatrixB(Data As Variant, m As Integer)
Dim X As Variant
X = Data
Dim numRows As Long
Dim numCols As Long
numRows = UBound(X, 1)
numCols = UBound(X, 2)
ReDim rowSum(1 To numCols) As Double
ReDim colSum(1 To numRows) As Double
Dim r As Long
Dim c As Long
For r = 1 To numRows
For c = 1 To numCols
rowSum(c) = rowSum(c) + X(r, c)
colSum(r) = colSum(r) + X(r, c)
Next
Next
ReDim B(1 To numRows, 1 To numCols) As Double
For r = 1 To numRows
For c = 1 To numCols
B(r, c) = (rowSum(c) + colSum(r)) / 2
Next
Next
With Cells(m + 3, 1)
.Value = "Matrix B"
.Font.Size = 14
.Font.Bold = True
End With
Cells(m + 4, 1).Resize(numRows, numCols) = B
Worksheets("Sheet1").Range("A1:X100").NumberFormat = "0.00"
Dim Min As Integer
End Sub
Sub Minimum(m As Integer, Matrixxx As Single)
MsgBox Application.Min(Matrixxx)
End Sub
As far as I went, my code is up to the new matrix B and printing it, but I have trouble finding the new one and making it a type, where I can get the indexes as well. I also do have trouble with the syntacsis, Have been coding 5years ago in java.
If you need to return a value then define a function. To return more than one value use an array.
Option Explicit
Sub Process_Matrix()
' define matrices
Dim A As Variant, B As Variant
A = InputMatrix("matrix.txt") '"H:\School\matrix.txt")
Call OutMatrix("Matrix A", Sheet1.Cells(1, 1), A)
B = NewMatrixB(A)
Call OutMatrix("Matrix B", Sheet1.Cells(12, 1), B)
Dim idx, msg As String
idx = getMin(B)
msg = "Min = " & B(idx(0), idx(1)) & " at B(" & idx(0) & "," & idx(1) & ")"
MsgBox msg, vbInformation
End Sub
Function getMin(ByRef X) As Variant
Dim i As Long, j As Long, m As Double, ar(0 To 1) As Long
m = X(1, 1)
ar(0) = 1
ar(1) = 1
For i = 1 To UBound(X)
For j = 1 To UBound(X, 2)
If X(i, j) < m Then
m = X(i, j)
ar(0) = i
ar(1) = j
End If
Next
Next
getMin = ar
End Function
Function InputMatrix(Fname As String) As Variant
Dim i As Long, j As Long, m As Long, n As Long, A() As Single
Fname = InputBox("Vuvedete ime na fail s vhodni danni", _
"matrix", Fname)
If Dir(Fname) = "" Then
MsgBox "Failut " & Fname & " ne e nameren!", vbCritical
Stop
End If
Close #1
Open Fname For Input As #1
Input #1, m, n
ReDim A(1 To m, 1 To n)
For i = 1 To m
For j = 1 To n
Input #1, A(i, j)
Next j
Next i
Close #1
InputMatrix = A
End Function
Sub OutMatrix(title As String, rng As Range, ByRef X)
With rng
.Value = title
.Font.Size = 14
.Font.Bold = True
With .Offset(1, 0).Resize(UBound(X), UBound(X, 2))
.Value = X
.NumberFormat = "0.00"
End With
End With
End Sub
Function NewMatrixB(ByRef X) As Variant
Dim B, rowSum, colSum
Dim numRows As Long, numCols As Long, r As Long, c As Long
numRows = UBound(X, 1)
numCols = UBound(X, 2)
ReDim rowSum(1 To numRows) As Double
ReDim colSum(1 To numCols) As Double
For r = 1 To numRows
For c = 1 To numCols
rowSum(c) = rowSum(c) + X(r, c)
colSum(r) = colSum(r) + X(r, c)
Next
Next
ReDim B(1 To numRows, 1 To numCols) As Double
For r = 1 To numRows
For c = 1 To numCols
B(r, c) = (rowSum(c) + colSum(r)) / 2
Next
Next
NewMatrixB = B
End Function
Public Function v2(LeftMatrix As Range, RightMatrix As Range) As Variant()
Dim e As Integer, s As Integer
e = LeftMatrix.Rows.Count
s = LeftMatrix.Columns.Count
ReDim m(1 To e, 1 To s + 1)
Dim i As Integer, j As Integer, k As Integer
For i = 1 To e
For j = 1 To s + 1
If j <= s Then
m(i, j) = LeftMatrix(i, j)
Else
m(i, j) = RightMatrix(i, 1)
Next j
Next i
For i = 1 To e
For j = 1 To s + 1
Cells(i, j) = m(i, j)
Next j
Next i
End Function
I'm selecting two matrices from my worksheet and trying to merge them into a single matrix but I keep getting the ref error.
I have a MatMul function for multiplying 2 matrices and returning the resulting Array. Gomb2_Click is triggered via a button and for each trigger it applies the transformation described by the matrix to the initial point p0.
(Both the matrix and the initial p0 point are hardcoded) \
In the Gomb2_Click subroutine the p1 = MatMul(mat, p0) line gives me a ByRef argument type mismatch error and I don't know why.
Thanks!
Function MatMul(a As Range, b As Range) As Range
If a.Columns.Count <> b.Rows.Count Then
MatMul = 0
End If
ReDim res(a.Rows.Count, b.Columns.Count)
For i = 1 To a.Rows.Count
For j = 1 To b.Columns.Count
tmp = 0
For k = 1 To a.Columns.Count
tmp = tmp + a(i, k) * b(k, j)
Next k
res(i - 1, j - 1) = tmp
Next j
Next i
MatMul = res
End Function
Dim colnum As Integer
Set colnum = 0
Dim p0 As Range
Set p0 = Range("B6:B7")
Dim mat As Range
Set mat = Range("B2:C3")
Sub Gomb2_Click()
Dim p1 as Variant
Set p1 = MatMul(mat, p0)
Range(Cells(6, 3 + colnum), Cells(7, 3 + colnum)).Value = p1
colnum = colnum + 1
p0.Value = p1
End Sub
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
using excel VBA i have to generate a table of numbers counting anti clock wise with one in the middle and highlight prime numbers in red in the process the following image is an example of the out put i should have .
Thanks to you guys i have used the above code to come up with this code which works perfectly.
Option Explicit
Private Function GetPrime(MaxToCheck As Long) As Collection
Dim c As New Collection, isUnDivided As Boolean, i As Long, v
c.Add Key:="2", Item:=2
For i = 3 To MaxToCheck
isUnDivided = True
For Each v In c
If i Mod v = 0 Then isUnDivided = False: Exit For
Next v
If isUnDivided Then c.Add Key:=CStr(i), Item:=i
Next i
Set GetPrime = c
End Function
Sub prime()
Dim a, c As New Collection, i As Long, j As Long, r As Range, v
With Range("A1").CurrentRegion
a = .Value
Set c = GetPrime(Application.Max(a))
For i = 1 To UBound(a, 1)
For j = 1 To UBound(a, 2)
On Error Resume Next
v = c(CStr(a(i, j)))
If Err.Number = 0 Then
If Not r Is Nothing Then Set r = Union(r, .Cells(i, j)) Else
Set r = .Cells(i, j)
End If
On Error GoTo 0
Next j
Next i
End With
If Not r Is Nothing Then r.Font.Color = vbRed
End Sub
Here is a sample code for you to start with,
Sub primeNum()
Dim i As Long, j As Long, k As Long, x As Long, y As Long, z As Long
Dim l As Long
j = 50
x = 20
y = 20
k = 1
i = 1
Cells(x, y) = 1
Loops:
For z = 1 To 4
If z = 3 Then
k = k + 1
End If
For l = 1 To k
i = i + 1
Select Case (z)
Case "1":
y = y + 1
Cells(x, y) = i
Case "2":
x = x - 1
Cells(x, y) = i
Case "3":
y = y - 1
Cells(x, y) = i
Case "4":
x = x + 1
Cells(x, y) = i
End Select
Next l
Next z
k = k + 1
If i <= j Then
GoTo Loops
End If
End Sub
I leave the part of checking prime numbers for you to google and find,