How can I make this vba work the way I need? - excel

I modified this vba code to generate combinations of six from 10 groups of pairs where only one number of a pair is used for each combination. but I can't get exactly what i want, and there many repetitions of the same combinations and some numbers are not even used in combinations. Can I get a little help to fix this code, please? thanks.
Here is the vba:
Sub Combs()
Dim a As Long
Dim b As Long
Dim c As Long
Dim d As Long
Dim e As Long
Dim f As Long
Dim g As Long
Dim h As Long
Dim j As Long
Dim k As Long
Dim i As Long
Dim grp
grp = Range("B1:C10").Value
Dim arr(1 To 1025, 1 To 10) As Long
For a = 1 To 2
For b = 1 To 2
For c = 1 To 2
For d = 1 To 2
For e = 1 To 2
For f = 1 To 2
For g = 1 To 2
For h = 1 To 2
For j = 1 To 2
For k = 1 To 2
i = i + 1
arr(i, 1) = grp(1, a)
arr(i, 2) = grp(2, b)
arr(i, 3) = grp(3, c)
arr(i, 4) = grp(4, d)
arr(i, 5) = grp(5, e)
arr(i, 6) = grp(6, f)
arr(i, 7) = grp(7, g)
arr(i, 8) = grp(8, h)
arr(i, 9) = grp(9, j)
arr(i, 10) = grp(10, k)
Next k
Next j
Next h
Next g
Next f
Next e
Next d
Next c
Next b
Next a
Application.ScreenUpdating = False
Range("H2").Resize(1025, 6).Value = arr
Application.ScreenUpdating = True
End Sub
Here is a sample data:
Group1 1 2
Group2 3 4
Group3 5 6
Group4 7 8
Group5 9 10
Group6 11 12
Group7 13 14
Group8 15 16
Group9 17 18
Group10 19 20

I'm guessing your want to randomly select 6 from 10
Sub Combos()
Dim i As Integer, j As Integer, k As Integer, n As Integer
Dim c As Integer, digit As Integer
Dim b As String, bb As String
Dim grp, arr(1 To 1025, 1 To 10) As Long, cols
' group data
grp = Range("B1:C10").Value
For j = 0 To 31
b = CStr(WorksheetFunction.Dec2Bin(j, 5))
For k = 0 To 31
bb = b & CStr(WorksheetFunction.Dec2Bin(k, 5))
i = j * 32 + k + 1
' select 6 of the 10 columns
cols = SixFromTen
Range("V" & i + 1) = Join(cols, ",") ' show combo
For c = 0 To 5
n = cols(c) '
'Debug.Print j, k, n
digit = CInt(Mid(bb, n, 1)) ' binary digit
arr(i, c + 1) = grp(n, digit + 1)
Next
Next
Next
Range("O2").Resize(1025, 6).Value = arr
MsgBox "Done"
End Sub
Function SixFromTen() As Variant
Dim num As New Collection, ar(5) As String
Dim i As Integer, n As Integer
Dim a As Integer, b As Integer, tmp As Integer
For n = 1 To 10
num.Add n
Next
For i = 0 To 5
n = 1 + Int(Rnd() * num.Count)
ar(i) = num(n)
num.Remove n
Next
'bubble sort
For a = 0 To 4
For b = a + 1 To 5
If CInt(ar(a)) > CInt(ar(b)) Then
tmp = ar(a)
ar(a) = ar(b)
ar(b) = tmp
End If
Next
Next
SixFromTen = ar
End Function
Alternatively remove 4 from 10 to leave 6 in correct order.
Function SixFromTen() As Variant
Dim num As New Collection, ar(5) As String
Dim i As Integer, n As Integer
For n = 1 To 10: num.Add n: Next
' remove 4
For i = 0 To 3
n = 1 + Int(Rnd() * num.Count)
num.Remove n
Next
For i = 0 To 5: ar(i) = num(i + 1): Next
SixFromTen = ar
End Function

As AcsErno pointed out, the issue is that your program is generating ten columns of digits but the output is only for six columns. Removing four columns results in many repeated combinations because the unique part has been removed in those four columns.
The solution is to only generate six columns, therefore allowing each of them to remain unique. I have rewritten your program to only generate six columns:
Sub Combs()
Dim a As Long, b As Long, c As Long, d As Long, e As Long, f As Long
Dim i As Long, arr(1 To 1025, 1 To 6) As Long
Dim grp() As Variant
grp = Range("B1:C6").Value
For a = 1 To 2
For b = 1 To 2
For c = 1 To 2
For d = 1 To 2
For e = 1 To 2
For f = 1 To 2
i = i + 1
arr(i, 1) = grp(1, a)
arr(i, 2) = grp(2, b)
arr(i, 3) = grp(3, c)
arr(i, 4) = grp(4, d)
arr(i, 5) = grp(5, e)
arr(i, 6) = grp(6, f)
Next f
Next e
Next d
Next c
Next b
Next a
Application.ScreenUpdating = False
Range("H2").Resize(1025, 6).Value = arr
Application.ScreenUpdating = True
End Sub
Note: The upper bound of the array can be reduced from 1025 since we are no longer using as many rows. If you require additional combinations, you can add a third number to any of the pairs.
An Example:
How to input the combination numbers
Sub Combs()
Dim a As Long, b As Long, c As Long, d As Long, e As Long, f As Long
Dim i As Long, arr(1 To 1025, 1 To 6) As Long
Dim grp() As Variant
grp = Range("B1:E6").Value 'Increasing the width
For a = 1 To 2
For b = 1 To 2
For c = 1 To 4 'Changed from 2 to 4
For d = 1 To 4 'Changed from 2 to 4
For e = 1 To 4 'Changed from 2 to 4
For f = 1 To 4 'Changed from 2 to 4
i = i + 1
arr(i, 1) = grp(1, a)
arr(i, 2) = grp(2, b)
arr(i, 3) = grp(3, c)
arr(i, 4) = grp(4, d)
arr(i, 5) = grp(5, e)
arr(i, 6) = grp(6, f)
Next f
Next e
Next d
Next c
Next b
Next a
Application.ScreenUpdating = False
Range("H2").Resize(1025, 6).Value = arr
Application.ScreenUpdating = True
End Sub

After reading your comments I think I better understand what you are looking for. It sounds like you want the combination to be a subset of six numbers from a set of ten. And each of those ten numbers is one of two values. If I understand that correctly, your issue is that you are having trouble iterating the six columns from the set of ten.
I replaced four of your For Loops with one that loops through the columns instead:
Sub Combs()
Dim a As Long, b As Long, c As Long, d As Long, e As Long, f As Long, alt As Long
Dim i As Long, arr(1 To 1025, 1 To 6) As Long
Dim grp() As Variant
grp = Range("B1:c10").Value
For a = 1 To 2
For b = 1 To 2
For c = 1 To 2
For d = 1 To 2
For e = 1 To 2
For f = 1 To 2
For alt = 0 To 4
'Interating the used section through the full 10 columns.
'Only six columns are used in each combo, but we change which six are used from those 10
i = i + 1
arr(i, 1) = grp(1 + alt, a)
arr(i, 2) = grp(2 + alt, b)
arr(i, 3) = grp(3 + alt, c)
arr(i, 4) = grp(4 + alt, d)
arr(i, 5) = grp(5 + alt, e)
arr(i, 6) = grp(6 + alt, f)
Next alt
Next f
Next e
Next d
Next c
Next b
Next a
Application.ScreenUpdating = False
Range("H2").Resize(1025, 6).Value = arr
Application.ScreenUpdating = True
End Sub
Note: This produces 320 unique combinations. This is not all possible combinations with these number pairs, for a more complete list, you could add in additional arrays with column positions swapped.

Related

Finding and printing the lowest value number and its indexes from a newly generated matrix

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

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.

Any suggestions on speeding this code up?

Ok, I have the code below, which takes 18 different words, all in Column A rows 1 to 18, and tries them in all different combos to find a seven word palindrome. I am pretty sure the code will get it done, but it just searches for a LONG time. I know there's a way to check the first and last letters of the combos, to make sure they're the same, before the code runs them through the REVERSE function, I just can't figure out how to do it. I am very new to this.In other words, each time it puts together 7 of the words, if it didn't have to go through the REVERSE function, a ton of time would be saved, and verification that the first and last letters match would do that. Thanks in advance for any help
Sub SevenDrome()
Dim count As Integer
count = 0
Dim wordtest As String
Dim wordpal As String
For j = 1 To 18
For k = 1 To 18
For l = 1 To 18
For m = 1 To 18
For n = 1 To 18
For o = 1 To 18
For p = 1 To 18
wordtest = Cells(j, 1) & Cells(k, 1) & Cells(l, 1) & Cells(m, 1) & Cells(n, 1) & Cells(o, 1) & Cells(p, 1)
wordpal = REVERSE(wordtest)
If wordtest = wordpal Then
count = count + 1
Cells(count, 7) = wordtest
End If
Next p
Next o
Next n
Next m
Next l
Next k
Next j
End Sub
Try, This results in 104,976 which takes less than 2 seconds.
Sub test()
Dim a(1 To 18)
Dim vR(1 To 1000000, 1 To 1)
Dim cnt As Long
Dim i As Integer, k As Integer, l As Integer
Dim m As Integer, n As Integer, o As Integer
For i = 1 To 18
a(i) = Range("a" & i)
Next i
For j = 1 To 18
For k = 1 To 18
If a(j) = a(k) Then
For l = 1 To 18
For m = 1 To 18
If a(l) = a(m) Then
For n = 1 To 18
For o = 1 To 18
If a(n) = a(o) Then
For p = 1 To 18
cnt = cnt + 1
vR(cnt, 1) = a(j) & a(l) & a(n) & a(p) & a(o) & a(m) & a(k)
DoEvents
Next p
End If
Next o
Next n
End If
Next m
Next l
End If
Next k
Next j
Range("g1").Resize(cnt) = vR
End Sub
Data image
Result Image
If each cell has more than 2 characters, you can do as follows.
Sub test2()
Dim a(1 To 18)
Dim vR(1 To 1000000, 1 To 1)
Dim cnt As Long
Dim i As Integer, k As Integer, l As Integer
Dim m As Integer, n As Integer, o As Integer
For i = 1 To 18
a(i) = Range("a" & i)
Next i
For j = 1 To 18
For k = 1 To 18
If a(j) = Reverse(a(k)) Then
For l = 1 To 18
For m = 1 To 18
If a(l) = Reverse(a(m)) Then
For n = 1 To 18
For o = 1 To 18
If a(n) = Reverse(a(o)) Then
For p = 1 To 18
If a(p) = Reverse(a(p)) Then
cnt = cnt + 1
vR(cnt, 1) = a(j) & a(l) & a(n) & a(p) & a(o) & a(m) & a(k)
DoEvents
End If
Next p
End If
Next o
Next n
End If
Next m
Next l
End If
Next k
Next j
Range("g1").CurrentRegion.Clear
If cnt Then
Range("g1").Resize(cnt) = vR
End If
End Sub
Function Reverse(s)
Dim i As Integer
Dim myS As String
For i = Len(s) To 1 Step -1
myS = myS & Mid(s, i, 1)
Next i
Reverse = myS
End Function
Case 2 Data
Case 2 Result

Generate all permutations, ommitting those not in least to greatest order

I have a set of numbers, 1-33. I need a fast way to generate every permutation of three of these numbers that results in an ascending order.
Examples:
7 19 25
1 2 3
10 20 30
But not:
7 5 9
11 23 22
Is there a way to do this in Excel?
Thanks
This will generate all 5456 combinations of the integers from 1 to 33
Sub ListUm()
Dim i As Long, j As Long, k As Long, Z As Long
Z = 1
For i = 1 To 31
For j = i + 1 To 32
For k = j + 1 To 33
Cells(Z, 1) = i & "," & j & "," & k
Z = Z + 1
Next k
Next j
Next i
End Sub
Since you have a specified order you can use combinations rather than permutations.
Something like this perhaps?
Sub Testing123()
Dim seedMax As Integer
Dim a As Integer
Dim b As Integer
Dim c As Integer
seedMax = 33
For a = 1 To seedMax
For b = a + 1 To seedMax
For c = b + 1 To seedMax
Debug.Print a, b, c
Next c
Next b
Next a
End Sub
Writing it to a worksheet:
Sub Testing123withSheetWrite()
Dim a As Integer
Dim b As Integer
Dim c As Integer
Dim seedMax As Integer: seedMax = 33
Dim x As Long: x = 1
Dim y As Long: y = 1
For a = 1 To seedMax
For b = a + 1 To seedMax
For c = b + 1 To seedMax
Debug.Print a, b, c
Cells(x, y + 0) = a
Cells(x, y + 1) = b
Cells(x, y + 2) = c
x = x + 1
Next c
Next b
Next a
End Sub
Initialize A1,B1,C1 with 1,2,3,
then enter
=IF((B1=32)*(C1=33),A1+1,A1)
in A2,
=IF(A2=A1,IF(C1<33,B1,B1+1),A2+1)
in B2 and
=MAX(B2+1,MOD(C1,33)+1)
in C2
Then copy/drag cells A2:C2 down until the last line, 31,32, 33:

excel vba permutations exceed 1048576 rows

I'm trying to create a permutation list in excel for 6 numbers, each in their own columnA-F and each number is from 1-38. when i run the VBA i find that the permutations far exceed rows 1048576 available in excel, so therefore the VBA ends at that point. i want a VBA that when the rows reach 1048576 on whatever sheet and the permutation isnt finished it will just create a new sheet and continue where it stopped on the previous sheet and automatically create sheets until the permutation ends. i've searched passed questions but none found to help. Any expert help would be greatly appreciated.
Code:
Sub Perm()
Dim a As Integer
Dim b As Integer
Dim c As Integer
Dim d As Integer
Dim e As Integer
Dim f As Integer
n = 1
For a = 1 To 38
For b = 1 To 38
For c = 1 To 38
For d = 1 To 38
For e = 1 To 38
For f = 1 To 38
Cells(n, 1).Value = a
Cells(n, 2).Value = b
Cells(n, 3).Value = c
Cells(n, 4).Value = d
Cells(n, 5).Value = e
Cells(n, 6).Value = f
n = n + 1
Next f
Next e
Next d
Next c
Next b
Next a
End Sub
This should do it for you:
Sub Perm()
Dim a As Integer
Dim b As Integer
Dim c As Integer
Dim d As Integer
Dim e As Integer
Dim f As Integer
Dim n As Long
Dim maxRows As Long
Dim sheetNumber As Integer
Dim loopCounter As Integer
maxRows = 1048576
loopCounter = 38
sheetNumber = 1
n = 1
ActiveWorkbook.Sheets.Add After:=Worksheets(Worksheets.Count)
ActiveSheet.Name = "Sheet-" & sheetNumber
Application.ScreenUpdating = False
For a = 1 To loopCounter
For b = 1 To loopCounter
For c = 1 To loopCounter
For d = 1 To loopCounter
For e = 1 To loopCounter
For f = 1 To loopCounter
Cells(n, 1).Value = a
Cells(n, 2).Value = b
Cells(n, 3).Value = c
Cells(n, 4).Value = d
Cells(n, 5).Value = e
Cells(n, 6).Value = f
If n = maxRows Then
sheetNumber = sheetNumber + 1
ActiveWorkbook.Sheets.Add After:=Worksheets(Worksheets.Count)
ActiveSheet.Name = "Sheet-" & sheetNumber
n = 0
End If
n = n + 1
Next f
Next e
Next d
Next c
Next b
Next a
Application.ScreenUpdating = False
End Sub
This will create a sheet called "Sheet-1" and fill it up down to row 1048576.
Then it will create a new sheet called "Sheet-2" and repeat until that is full, etc.
It is best to disable ScreenUpdating for intensive cell writing as it will make a big reduction to the running time.
Good luck, as it will probably take awhile. As the other poster says, it will need nearly 3000 worksheets. Hopefully you have enough memory on your machine.

Resources