我已经为这个答案搜索了一下,但没有什么能解决我的问题。这就是我正在做的事情......
我有一个包含[edit:16rows x 9cols]矩阵的工作表。矩阵的单元格是正整数和负整数。以下是部分数据的示例:
我需要找到所有可能的Summations,条件是每行只能选择一个值。换句话说,我会在每一行中取第一个值并将它们相加。然后,我将获取第一行的第二个值并将其添加到所有其他行'第一个值... on和on,直到我找到每行的最后一个值的总和。
首先,我想存储(-144,-16,0,-96,-74,0,589,-61,-55,-18,-66,0,-279的和) ,-24,-43,-406)。存储的下一个总和将是(-5,-16,0,-96,-74,0,589,-61,-55,-18,-66,0,-279,-24,-43, - 406)。
我试图想出一种巧妙的方法来使用For
循环来使用GoTo
语句,但我对任何想法持开放态度。也许是一个递归函数?
答案 0 :(得分:2)
有可能通过递归来解决这类问题,但我发现没有。
考虑速度表。每个循环向最右边的数字添加一个。如果该数字从9溢出到零,则将一个数字添加到左侧的下一个数字。如果该数字溢出,则向左侧的数字添加一个数字。这一直持续到数字不溢出或最左边的数字溢出。因此,您在车速表上看到的值是:
0 0 0 0
0 0 0 1
0 0 0 2
: : : :
0 0 0 9
0 0 1 0
0 0 1 1
: : : :
0 0 1 9
0 0 2 0
: : : :
0 0 9 9
0 1 0 0
: : : :
9 9 9 9
如果速度表的数字是整数数组中的条目,则一个简单的循环可以循环显示这些值。
对于你的问题:
通过此更改,您的车速表将从以下位置循环:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
到
55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55
如果您将列编号从0到55,则每个数字都会告诉您哪一列用于相应的行。
你的车速表循环的一个值是:
1 45 5 30 8 22 1 0 38 51 14 42 29 31 46 7
它告诉你总结:
Column 1 of first row
Column 45 of second row
Column 5 of third row
Column 30 of fourth row
And so on
另一个循环将提取并求和这些值。
因此外环将使速度表从{0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0}循环至{55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55}。对于车速表上的每个值,内循环计算并存储总和。
第2节
更新新要求的原始代码几乎不需要更改。
在原版中,我有一个“车速表”,从(0,0,0,......)循环到(55,55,55,...),每个“车轮”的值从0到55.我现在有了添加了一个数组,它给出了每个“轮”的最大值。例如,第一个“轮”可以取值0到5,它们对应于从矩阵中提取的六个值:-144 -5 0 12 16 20。
5 2 4 8 2 1 1 3 1 5 7 1 8 3 4 5 New maximum values
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Minimum values
55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 Old maximum values
我将矩阵图像中的数据输入到新工作簿的Sheet1中:
我的宏将该矩阵导入变量数组。每次访问变量类型变量都有开销,因此我将数据复制到Long数组。为了演示宏正确创建数组,我输出:
Row Lst ---0 ---1 ---2 ---3 ---4 ---5 ---6 ---7 ---8
0 5 -144 -5 0 12 16 20
1 2 -16 0 50
2 4 0 5 8 11 70
3 8 -96 -57 -47 -45 -29 -13 -2 0 3
4 2 -74 -18 0
5 1 0 8
6 1 589 0
7 3 -61 -44 -26 0
8 1 -55 0
9 5 -18 0 9 18 50 58
10 7 -66 -36 0 2 16 46 62 82
11 1 0 8
12 8 -279 -272 -253 -229 -165 -121 -74 -38 0
13 3 -24 -19 -17 0
14 4 -43 -27 -21 -9 0
15 5 -406 -91 -64 -29 -3 0
列“Lst”给出每行的最后一个条目。
对于宏的第一个版本,我将前200个和的诊断信息输出到Sheet2:
这足以让我相信宏正确地使车速表循环,正确地从矩阵中提取值并正确地对这些值求和。
对于宏的第二个版本,我删除了所有诊断代码并将总和输出到文件。我在1,000,000总和之后切换文件以保持文件大小可管理。创建文件50后,宏退出。创建这50个文件需要13分15秒。第一个文件的顶部是:
然后我切换到Visual Basic 2010.我创建了一个简单形式的Windows应用程序:
我有六个控件,其中四个命名如图所示,另一个lblMessage
直到最后才可见。 lblFileNumMax
的值8000在运行时被计算出的要创建的文件数替换。每次创建新文件时,lblFileNumCrnt
的值都会更新。每分钟创建大约100个,这提供了充分的进展指示。
我本可以从Excel加载矩阵,但我认为硬编码更容易。除此之外,VBA版本的代码差别不大。我保留了在创建50个文件后停止生成的陷阱,并使用批处理文件检查VBA文件是否与VB文件相同:
Del compare.txt
comp "Sums 0001.txt" "Sums 00001VBA.txt" <N.txt >>Compare.txt
comp "Sums 0002.txt" "Sums 00002VBA.txt" <N.txt >>Compare.txt
comp "Sums 0003.txt" "Sums 00003VBA.txt" <N.txt >>Compare.txt
comp "Sums 0004.txt" "Sums 00004VBA.txt" <N.txt >>Compare.txt
comp "Sums 0005.txt" "Sums 00005VBA.txt" <N.txt >>Compare.txt
然后我删除了陷阱,让程序创建所有8063文件,在我的2.1 GHz笔记本电脑上花了51分45秒。
我无法发布VBA代码,因为我不小心将它与8063文件一起删除了40 Gb,这足以导致我的回收站溢出。
VB.net代码如下。
Option Strict On
Imports System.IO
Public Class Form1
Dim fileOut As StreamWriter
Private Sub cmdStart_Click(sender As System.Object, e As System.EventArgs) Handles cmdStart.Click
Dim matrix(,) As Integer = {{-144, -5, 0, 12, 16, 20, 0, 0, 0}, _
{-16, 0, 50, 0, 0, 0, 0, 0, 0}, _
{0, 5, 8, 11, 70, 0, 0, 0, 0}, _
{-96, -57, -47, -45, -29, -13, -2, 0, 3}, _
{-74, -18, 0, 0, 0, 0, 0, 0, 0}, _
{0, 8, 0, 0, 0, 0, 0, 0, 0}, _
{589, 0, 0, 0, 0, 0, 0, 0, 0}, _
{-61, -44, -26, 0, 0, 0, 0, 0, 0}, _
{-55, 0, 0, 0, 0, 0, 0, 0, 0}, _
{-18, 0, 9, 18, 50, 58, 0, 0, 0}, _
{-66, -36, 0, 2, 16, 46, 62, 82, 0}, _
{0, 8, 0, 0, 0, 0, 0, 0, 0}, _
{-279, -272, -253, -229, -165, -121, -74, -38, 0}, _
{-24, -19, -17, 0, 0, 0, 0, 0, 0}, _
{-43, -27, -21, -9, 0, 0, 0, 0, 0}, _
{-406, -91, -64, -29, -3, 0, 0, 0, 0}}
Dim lastEntryPerRow() As Integer = {5, 2, 4, 8, 2, 1, 1, 3, 1, 5, 7, 1, 8, 3, 4, 5}
Const sumsPerFile As Long = 1000000
Dim fileOutNum As Integer
Dim fileOutNumMax As Long
Dim finished As Boolean
Dim numSums As Integer
Dim pathProg As String
Dim posChar As Int32
Dim speedo() As Integer
Dim sumCrnt As Integer
Dim rowCrnt As Integer
Dim rowMax As Integer = matrix.GetUpperBound(0)
Dim timeStart As Long
cmdStart.Visible = False
cmdExit.Visible = False
' Extract folder containing program
pathProg = Application.ExecutablePath
posChar = InStrRev(pathProg, "\")
If posChar <> 0 Then
' Discard the name of the program
pathProg = Mid(pathProg, 1, posChar)
End If
' Initialise Speedo to all zeros
ReDim speedo(rowMax)
For rowCrnt = 0 To rowMax
speedo(rowCrnt) = 0
Next
' Calculate number of files to be created
fileOutNumMax = 1
For rowCrnt = 0 To rowMax
fileOutNumMax *= CLng(lastEntryPerRow(rowCrnt) + 1)
Next
fileOutNumMax = CInt(fileOutNumMax / sumsPerFile)
lblFileNumMax.Text = CStr(fileOutNumMax)
' Initialise control variables
numSums = 0
fileOutNum = 1
finished = False
lblFileNumCrnt.Text = CStr(fileOutNum)
Application.DoEvents()
timeStart = (Hour(DateTime.Now) * 24 + Minute(DateTime.Now)) * 60 + Second(DateTime.Now)
Do While True
' False means overwrite if file already exists
fileOut = New StreamWriter(pathProg & "\Sums " & Format(fileOutNum, "0000") & ".txt", False)
Do While True
' Output sum identified by current value of Speedo
sumCrnt = 0
numSums = numSums + 1
For rowCrnt = 0 To rowMax
sumCrnt += matrix(rowCrnt, speedo(rowCrnt))
Next
fileOut.WriteLine(sumCrnt)
' Generate next value for Speedo
' Process entries from left to right
For rowCrnt = 0 To rowMax
If speedo(rowCrnt) = lastEntryPerRow(rowCrnt) Then
' This column is about to overflow
speedo(rowCrnt) = 0
If rowCrnt = rowMax Then
' rightmost entry has overflowed. All done
finished = True
Exit Do
End If
' Continue with For-Loop to step next column to right
Else
' This column is not about to overflow
speedo(rowCrnt) = speedo(rowCrnt) + 1
' Have finished generation
Exit For
End If
Next
If numSums >= sumsPerFile Then
Exit Do
End If
Loop
fileOut.Close()
fileOut = Nothing
numSums = 0
fileOutNum = fileOutNum + 1
'If fileOutNum >= 51 Then
' Exit Do
'End If
If finished Then
Exit Do
End If
lblFileNumCrnt.Text = CStr(fileOutNum)
Application.DoEvents()
Loop
Debug.Print(CStr((Hour(DateTime.Now) * 24 + Minute(DateTime.Now)) * 60 + _
Second(DateTime.Now) - timeStart) & " seconds")
cmdExit.Visible = True
End Sub
Private Sub cmdExit_Click(sender As System.Object, e As System.EventArgs) Handles cmdExit.Click
If fileOut IsNot Nothing Then
fileOut.Close()
fileOut = Nothing
End If
Me.Close()
End Sub
End Class