将Scala数组转换为唯一排序列表的有效方法

时间:2011-11-16 23:08:02

标签: arrays list scala optimization sorting

任何人都可以在Scala中优化以下语句:

// maybe large
val someArray = Array(9, 1, 6, 2, 1, 9, 4, 5, 1, 6, 5, 0, 6) 

// output a sorted list which contains unique element from the array without 0
val newList=(someArray filter (_>0)).toList.distinct.sort((e1, e2) => (e1 > e2))

由于性能至关重要,有更好的方法吗?

谢谢。

7 个答案:

答案 0 :(得分:20)

这条简单的行是迄今为止最快的代码之一:

someArray.toList.filter (_ > 0).sortWith (_ > _).distinct

但到目前为止,明显的赢家是 - 由于我的测量 - 杰德韦斯利 - 史密斯。也许如果Rex的代码是固定的,它看起来会有所不同。

bench diagram

典型免责声明1 + 2:

  1. 我修改了代码以接受一个数组并返回一个List。
  2. 典型的基准考虑因素:
    • 这是随机数据,均匀分布。对于100万个元素,我在0到1百万之间创建了一个100万英寸的数组。因此,或多或少的零,以及或多或少的重复,它可能会有所不同。
    • 可能取决于机器等。我使用单核CPU,Intel-Linux-32bit,jdk-1.6,scala 2.9.0.1
  3. 这是生成图形的基础benchcoat-code and the concrete code (gnuplot)。 Y轴:以秒为单位的时间。 X轴:阵列中的100 000到1 000 000个元素。

    更新

    在发现Rex代码的问题之后,他的代码和Jed的代码一样快,但最后一个操作是将他的Array转换为List(以填满我的基准界面)。使用var result = List [Int]result = someArray (i) :: result加速他的代码,使其速度大约是Jed-Code的两倍。

    另一个,也许有趣的发现是:如果我按照filter / sort / distinct(fsd)=>的顺序重新排列我的代码。 (dsf,dfs,fsd,...),所有6种可能性都没有显着差异。

答案 1 :(得分:7)

我没有测量过,但是我和Duncan在一起,排序到位,然后使用类似的东西:

util.Sorting.quickSort(array)
array.foldRight(List.empty[Int]){ 
  case (a, b) => 
    if (!b.isEmpty && b(0) == a) 
      b 
    else 
      a :: b 
}

从理论上讲,这应该非常有效。

答案 2 :(得分:4)

如果没有基准测试,我无法确定,但我认为以下内容非常有效:

val list = collection.SortedSet(someArray.filter(_>0) :_*).toList

另请尝试在您的版本中的someArray之后添加.par。它不能保证更快,有点可能。你应该运行一个基准测试和实验。

sort已弃用。请改用.sortWith(_ > _)

答案 3 :(得分:3)

拳击原语将给你10-30倍的性能惩罚。因此,如果确实性能有限,那么您将需要处理原始基本数组:

def arrayDistinctInts(someArray: Array[Int]) = {    
  java.util.Arrays.sort(someArray)
  var overzero = 0
  var ndiff = 0
  var last = 0
  var i = 0
  while (i < someArray.length) {
    if (someArray(i)<=0) overzero = i+1
    else if (someArray(i)>last) {
      last = someArray(i)
      ndiff += 1
    }
    i += 1
  }
  val result = new Array[Int](ndiff)
  var j = 0
  i = overzero
  last = 0
  while (i < someArray.length) {
    if (someArray(i) > last) {
      result(j) = someArray(i)
      last = someArray(i)
      j += 1
    }
    i += 1
  }
  result
}

如果你小心的话,你可以稍微好一些(并且要警告,我把它打成了我的头顶;我可能已经打错了东西,但这是使用的样式),但是如果你发现了现有版本太慢,这应该至少快5倍,可能还要多。


编辑(除了修复以前的代码以使其实际有效):

如果您坚持以列表结尾,那么您可以随时构建列表。你可以递归地执行此操作,但我不认为在这种情况下它比迭代版本更清晰,所以:

def listDistinctInts(someArray: Array[Int]): List[Int] = {
  if (someArray.length == 0 || someArray(someArray.length-1) <= 0) List[Int]()
  else {
    java.util.Arrays.sort(someArray)
    var last = someArray(someArray.length-1)
    var list = last :: Nil
    var i = someArray.length-2
    while (i >= 0) {
      if (someArray(i) < last) {
        last = someArray(i)
        if (last <= 0) return list;
        list = last :: list
      }
      i -= 1
    }
    list
  }
}

另外,如果您不能通过排序来销毁原始数组,那么如果您复制数组并销毁副本(基元的数组副本非常快),那么您最好是最好的。

请记住,根据数据的性质,有一些特殊情况的解决方案要快得多。例如,如果您知道您有一个长数组,但数字将在一个较小的范围内(例如-100到100),那么您可以使用bitset来跟踪您遇到的数据。

答案 4 :(得分:2)

为了提高效率,取决于你的大值:

val a = someArray.toSet.filter(_>0).toArray
java.util.Arrays.sort(a) // quicksort, mutable data structures bad :-)
res15: Array[Int] = Array(1, 2, 4, 5, 6, 9)

请注意,这是使用未装箱数组上的qsort进行排序。

答案 5 :(得分:1)

我无法衡量,但还有一些建议......

在转换为列表之前对数组进行排序可能会更有效,并且您可能会考虑从排序列表中手动删除重复项,因为它们将组合在一起。在排序之前或之后移除0的成本也取决于它们与其他条目的比率。

答案 6 :(得分:1)

如何将所有内容添加到有序集合中?

val a = scala.collection.immutable.SortedSet(someArray filter (0 !=): _*)

当然,您应该基准代码来检查更快的内容,更重要的是,这确实是一个热点。