我正在使用几个按钮在Excel-VBA中编写管理器。
其中一个是使用另一个Excel文件(我称之为T
)生成一个标签作为输入。
T
的一些属性:
~90MB
尺寸~350K
行14
个月(无序)的销售数据。year/month
主要关系:
我正在生成一个新的Excel标签,其中包含卖家分组的最后T
的{{1}}数据。
重要说明:
year/month
是唯一可用的输入/来源。T
应计入所有卖家。这已经足够了,现在您知道我的已编码。
我的代码有效,但,大约需要4分钟的运行时间。
我已经使用较小的源(不大于total-money
)编码了一些其他按钮,这些按钮在5秒内运行。
考虑2MB
大小,可以接受4分钟的运行时。
但我并不为此感到骄傲,至少现在还没有。
我的代码主要基于T
来映射来自Scripting.Dictionary
的数据,然后我使用T
将分组数据设置为新创建的标签。
我不确定,但这是我的想法:
for each key in obj ... next key
是N
中的总密钥,我需要在汇总Scripting.Dictionary
之前检查obj.Exists(str)
。它将运行total-money
字符串比较以返回N
。false
时,它会运行maximun N
字符串。Tries
编写我自己的类对象。
我明天才会开始编码,我想要的只是一些确认,如果我是以正确的方式,或者是一些建议,如果我做错了。
你有什么建议吗?提前谢谢。
答案 0 :(得分:1)
超出内存限制
简而言之:
~ 13 seconds
。我们学到的东西很难。但我很高兴我找到了答案。
Scripting.Dictionary
的DP方法实际上更快达到了100%。cls_trie
实现的DP方法也达到了100%,但是比第一次提高了。~4-5 min
与上述尝试的~2-3 min
总运行时间进行比较。2%
。解决方案很简单,我必须平衡CPU和内存使用。
for-loops
将一些DP方法更改为简单if-conditions
。 ~15%
。~65%
。15 seconds
。我创建了一个GitHub repository with my cls_trie implementation并添加了一个带有示例用法的excel文件。
我是excel-vba世界的新手(目前正在使用它4个月)。可能有一些方法可以改进我的cls_trie实现,我向建议开放:
Option Explicit
Public Keys As Collection
Public Children As Variant
Public IsLeaf As Boolean
Public tObject As Variant
Public tValue As Variant
Public Sub Init()
Set Keys = New Collection
ReDim Children(0 To 255) As cls_trie
IsLeaf = False
Set tObject = Nothing
tValue = 0
End Sub
Public Function GetNodeAt(index As Integer) As cls_trie
Set GetNodeAt = Children(index)
End Function
Public Sub CreateNodeAt(index As Integer)
Set Children(index) = New cls_trie
Children(index).Init
End Sub
'''
'Following function will retrieve node for a given key,
'creating a entire new branch if necessary
'''
Public Function GetNode(ByRef key As Variant) As cls_trie
Dim node As cls_trie
Dim b() As Byte
Dim i As Integer
Dim pos As Integer
b = CStr(key)
Set node = Me
For i = 0 To UBound(b) Step 2
pos = b(i) Mod 256
If (node.GetNodeAt(pos) Is Nothing) Then
node.CreateNodeAt pos
End If
Set node = node.GetNodeAt(pos)
Next
If (node.IsLeaf) Then
'already existed
Else
node.IsLeaf = True
Keys.Add key
End If
Set GetNode = node
End Function
'''
'Following function will get the value for a given key
'Creating the key if necessary
'''
Public Function GetValue(ByRef key As Variant) As Variant
Dim node As cls_trie
Set node = GetNode(key)
GetValue = node.tValue
End Function
'''
'Following sub will set a value to a given key
'Creating the key if necessary
'''
Public Sub SetValue(ByRef key As Variant, value As Variant)
Dim node As cls_trie
Set node = GetNode(key)
node.tValue = value
End Sub
'''
'Following sub will sum up a value for a given key
'Creating the key if necessary
'''
Public Sub SumValue(ByRef key As Variant, value As Variant)
Dim node As cls_trie
Set node = GetNode(key)
node.tValue = node.tValue + value
End Sub
'''
'Following function will validate if given key exists
'''
Public Function Exists(ByRef key As Variant) As Boolean
Dim node As cls_trie
Dim b() As Byte
Dim i As Integer
b = CStr(key)
Set node = Me
For i = 0 To UBound(b) Step 2
Set node = node.GetNodeAt(b(i) Mod 256)
If (node Is Nothing) Then
Exists = False
Exit Function
End If
Next
Exists = node.IsLeaf
End Function
'''
'Following function will get another Trie from given key
'Creating both key and trie if necessary
'''
Public Function GetTrie(ByRef key As Variant) As cls_trie
Dim node As cls_trie
Set node = GetNode(key)
If (node.tObject Is Nothing) Then
Set node.tObject = New cls_trie
node.tObject.Init
End If
Set GetTrie = node.tObject
End Function
您可以在上面的代码中看到:
[a-z]
字母和数字,以及两个文本被映射到同一个分支节点的概率趋于零。作为一个伟大的程序员说,每个人都喜欢他自己的代码,即使其他代码太漂亮了,不值得讨厌[1]
我的结论
Scripting.Dictionary
,即使已经证明它可能比我的cls_trie
实施更好。谢谢大家的帮助。
答案 1 :(得分:1)
我坚信您已经找到了正确的解决方案,因为最近两年没有任何更新。
无论如何,我想提到(也许会帮助其他人)您的瓶颈不是字典或二叉树。如果有足够的RAM,即使有数百万行的内存处理速度也非常快。
瓶颈通常是从工作表中读取数据并将其写回到工作表中。这里的数组非常用人。
只需将数据从工作表中读取到变量数组中即可。 您不必立即使用该阵列。如果您更喜欢使用字典,只需将数组中的所有数据传输到字典中并使用它即可。由于此过程完全在内存中完成,因此不必担心性能下降。
完成字典中的数据处理后,将字典中的所有数据放回到数组中,然后一次将数组写入新工作表中。
Worksheets(“ New Sheet”)。Range(“ A1”)。Value = MyArray
我很确定只需要几秒钟