【问题标题】:How to add up sets of consecutive integers with good performance如何将性能良好的连续整数集相加
【发布时间】:2014-01-19 04:22:06
【问题描述】:

我有一个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 秒.

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

【问题讨论】:

    标签: vb.net performance linq


    【解决方案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)
    

    是的,我使用了 indexer-access 语法,但是因为它在 helper List(Of T) 上,所以它实际上是一个 indexer 访问,而不是隐藏的 IEnumerable.ElementAtOrDefault(index) 调用。

    【讨论】:

    • 我想我会继续使用邮政编码(嘻嘻),它比您的聚合版本更容易理解。感谢您解释运行时差异的原因。同时,我知道 LINQ 的内部实现,因为我看到了一些来自 LINQ 类似扩展的源代码。现在我明白了背后的代码发生了什么以及为什么运行时差异如此之大。
    猜你喜欢
    • 2019-01-31
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多