如何添加具有良好性能的连续整数集

时间:2014-01-19 04:22:07

标签: vb.net performance linq

我有一个IEnumerable(Of Integer),想要将每组三个连续的整数相加,从第一个开始,到最后一个减去两个。

示例:我的整数是{0, 6, 2, 8, 2, 0, 3, 7, 1}。我想将其转换为{0+60+2, 600+20+8, 200+80+2, ...} = {62, 628, 282, 820, 203, 37, 371},然后将其存储在有序集中。请注意,我选择了基数10来简化问题,实际上基数是变化的(设置的最大值加1,因此我的方法会为集合中三个数字的每个排列生成一个唯一的ID)。

我测试了三段不同的代码:

1)

sums = New SortedSet(Of Integer)()
For i = 0 To integers.Count - 3
  sums.Add(integers(i) * 100 + integers(i + 1) * 10 + integers(i + 2))
Next

2)

sums = New SortedSet(Of Integer)(
  integers.Take(integers.Count - 2).Select(
    Function(val, index) val * 100 + integers(index + 1) * 10 + integers(index + 2)
  )
)

3)

Dim num = integers.Count - 2
Dim intsX = integers.Take(num)
Dim intsY = integers.Skip(1).Take(num)
Dim intsZ = integers.Skip(2)
sums = New SortedSet(Of Integer)(
  intsX.Zip(
  intsY, Function(x, y) x * 100 + y * 10).Zip(
  intsZ, Function(xy, z) xy + z)
)

所有三种方法都有效,但我的大约62k整数集的测试表明执行时间存在显着差异:方法1):1.8-2秒,方法2:1.4-1.5秒,方法3:0.75-0.77秒

由于性能对我的项目至关重要,我仍然不确定这是否是实现我想要的最佳代码。有没有人有另外一个我可以测试的想法?

1 个答案:

答案 0 :(得分:1)

第一次和第二次尝试的问题都与VB.NET在IEnumerable(Of T)上解决类似数组索引器访问的方式有关。这种类型的调用被转换为ElementAtOrDefault()扩展方法调用,因此您的第一个方法看起来像这样:

For i = 0 To integers.Count - 3
  sums.Add(integers.ElementAtOrDefault(i) * 100 + integers.ElementAtOrDefault(i + 1) * 10 + integers.ElementAtOrDefault(i + 2))
Next

是的,ElementAtOrDefault()每次调用时都需要从集合的开头迭代(取决于实际的IEnumerable(Of T)实现)。所以在更糟糕的情况下,你的解决方案一遍又一遍地迭代你的收藏,你甚至都不知道。

这也是Zip让它变得更好的原因。它只对你的源集合进行3次迭代,这比之前描述的情况要好得多。

另一种可能的解决方案

您可以尝试Aggregate

Dim sums = New SortedSet(Of Integer)()
source.Aggregate(
    New List(Of Integer)(3),
    Function(l As List(Of Integer), i As Integer)
        l.Add(i)
        If l.Count = 3 Then
            sums.Add(l(0) * 100 + l(1) * 10 + l(2))
            l.RemoveAt(0)
        End If
        Return l
    End Function,
    Function(l) l)

是的,我使用了索引器访问语法,但因为它位于帮助器List(Of T)上,所以它实际上是一个索引器访问,而不是隐藏的IEnumerable.ElementAtOrDefault(index)调用。