Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 获取可缩放项的前n个元素的最简单方法_Algorithm_Scala - Fatal编程技术网

Algorithm 获取可缩放项的前n个元素的最简单方法

Algorithm 获取可缩放项的前n个元素的最简单方法,algorithm,scala,Algorithm,Scala,有没有一个简单有效的解决方案来确定可伸缩表的前n个元素?我是说 iter.toList.sortBy(_.myAttr).take(2) iter.top(2, _.myAttr) 但不必对所有元素进行排序,因为只有前2个元素感兴趣。理想情况下,我正在寻找类似 iter.toList.sortBy(_.myAttr).take(2) iter.top(2, _.myAttr) 另请参见:使用排序的顶部元素的解决方案: 更新: 谢谢大家的解决方案。最后,我采用了user unknown的原

有没有一个简单有效的解决方案来确定可伸缩表的前n个元素?我是说

iter.toList.sortBy(_.myAttr).take(2)
iter.top(2, _.myAttr)
但不必对所有元素进行排序,因为只有前2个元素感兴趣。理想情况下,我正在寻找类似

iter.toList.sortBy(_.myAttr).take(2)
iter.top(2, _.myAttr)
另请参见:使用排序的顶部元素的解决方案:

更新: 谢谢大家的解决方案。最后,我采用了user unknown的原始解决方案,并采用它来使用
Iterable
和pimp my library模式:


为了确定前N个元素,不需要对整个集合进行排序。但是,我不相信这个功能是由raw库提供的,所以您必须自己使用,可能使用pimp my library模式

例如,可以按如下方式获取集合的第n个元素:

  class Pimp[A, Repr <% TraversableLike[A, Repr]](self : Repr) {

    def nth(n : Int)(implicit ord : Ordering[A]) : A = {
      val trav : TraversableLike[A, Repr] = self
      var ltp : List[A] = Nil
      var etp : List[A] = Nil
      var mtp : List[A] = Nil
      trav.headOption match {
        case None      => error("Cannot get " + n + " element of empty collection")
        case Some(piv) =>
          trav.foreach { a =>
            val cf = ord.compare(piv, a)
            if (cf == 0) etp ::= a
            else if (cf > 0) ltp ::= a
            else mtp ::= a
          }
          if (n < ltp.length)
            new Pimp[A, List[A]](ltp.reverse).nth(n)(ord)
          else if (n < (ltp.length + etp.length))
            piv
          else
            new Pimp[A, List[A]](mtp.reverse).nth(n - ltp.length - etp.length)(ord)
      }
    }
  }
不幸的是,我很难通过以下方式发现这个皮条客:

implicit def t2n[A, Repr <% TraversableLike[A, Repr]](t : Repr) : Pimp[A, Repr] 
  = new Pimp[A, Repr](t)


如果目标不是对整个列表进行排序,那么您可以这样做(当然可以稍微优化一下,这样我们就不会在数字明显不应该出现时更改列表):


我最近在ApacheJackrabbit类中实现了这样一个排名算法(尽管是用Java实现的)。有关其要点,请参见
take
方法。基本思想是快速排序,但一旦找到顶部的
n
元素,就过早终止排序

对于较小的
n
值和较大的列表,可以通过选择最大元素
n
次数来获取顶部
n
元素:

def top[T](n:Int, iter:Iterable[T])(implicit ord: Ordering[T]): Iterable[T] = {
  def partitionMax(acc: Iterable[T], it: Iterable[T]): Iterable[T]  = {
    val max = it.max(ord)
    val (nextElems, rest) = it.partition(ord.gteq(_, max))
    val maxElems = acc ++ nextElems
    if (maxElems.size >= n || rest.isEmpty) maxElems.take(n)
    else partitionMax(maxElems, rest)
  }
  if (iter.isEmpty) iter.take(0)
  else partitionMax(iter.take(0), iter)
}
这不会对整个列表进行排序,而是对其进行排序。我相信我在
partitionMax
中调用的每个方法都是O(列表大小),我最多只希望调用
n
次,因此小
n
的总体效率将与迭代器的大小成正比

scala> top(5, List.range(1,1000000))
res13: Iterable[Int] = List(999999, 999998, 999997, 999996, 999995)

scala> top(5, List.range(1,1000000))(Ordering[Int].on(- _))
res14: Iterable[Int] = List(1, 2, 3, 4, 5)
您还可以在
n
接近iterable的大小时添加分支,并切换到
iter.toList.sortBy(uu.myAttr.take(n)

它不返回所提供的收集类型,但您可以查看这是否是一项要求。

我的解决方案(绑定到Int,但应轻松更改为Ordered(请几分钟):

  • 对于以上列表,将前2(4,3)作为前十名(前二名)的起点
  • 对它们进行排序,使第一个元素是较大的元素(如果有)
  • 重复遍历列表的其余部分(li.drop(n)),并将当前元素与最小值列表中的最大值进行比较;必要时进行替换,然后再次使用
  • 改进:
    • 扔掉Int,使用ordered
    • 扔掉(>\)并使用用户排序来允许底部10。(更难:选择中间的10:)
    • 扔掉列表,改用Iterable
更新(抽象): 顶部/底部方法和用法如上所述。对于顶部/底部元素的小组,排序很少被调用,在开始时调用几次,然后随着时间的推移调用的次数越来越少。例如,top(10)为10000时为70次,top(10)为100000时为90次

另一个版本:

val big = (1 to 100000)

def maxes[A](n:Int)(l:Traversable[A])(implicit o:Ordering[A]) =
    l.foldLeft(collection.immutable.SortedSet.empty[A]) { (xs,y) =>
      if (xs.size < n) xs + y
      else {
        import o._
        val first = xs.firstKey
        if (first < y) xs - first + y
        else xs
      }
    }

println(maxes(4)(big))
println(maxes(2)(List("a","ab","c","z")))
这里是渐近O(n)解

def top[T](data: List[T], n: Int)(implicit ord: Ordering[T]): List[T] = {
    require( n < data.size)

    def partition_inner(shuffledData: List[T], pivot: T): List[T] = 
      shuffledData.partition( e => ord.compare(e, pivot) > 0 ) match {
          case (left, right) if left.size == n => left
          case (left, x :: rest) if left.size < n => 
            partition_inner(util.Random.shuffle(data), x)
          case (left @ y :: rest, right) if left.size > n => 
            partition_inner(util.Random.shuffle(data), y)
      }

     val shuffled = util.Random.shuffle(data)
     partition_inner(shuffled, shuffled.head)
}

scala> top(List.range(1,10000000), 5)
def pickTopN[A, B](n: Int, iterable: Iterable[A], f: A => B)(implicit ord: Ordering[B]): Seq[A] = {
  val seq = iterable.toSeq
  val q = collection.mutable.PriorityQueue[A](seq.take(n):_*)(ord.on(f).reverse) // initialize with first n

  // invariant: keep the top k scanned so far
  seq.drop(n).foreach(v => {
    q += v
    q.dequeue()
  })

  q.dequeueAll.reverse
}

这是另一个简单且性能相当好的解决方案

def pickTopN[T](k: Int, iterable: Iterable[T])(implicit ord: Ordering[T]): Seq[T] {
  val q = collection.mutable.PriorityQueue[T](iterable.toSeq:_*)
  val end = Math.min(k, q.size)
  (1 to end).map(_ => q.dequeue())
}

大O是
O(n+k log n)
,其中
k是使用
PriorityQueue
的优化解决方案,时间复杂度为
O(nlogk)
。在更新中给出的方法中,每次对不需要的
sofar
列表进行排序,下面使用
PriorityQueue
对其进行优化

import scala.language.implicitConversions
import scala.language.reflectiveCalls
import collection.mutable.PriorityQueue
implicit def iterExt[A](iter: Iterable[A]) = new {
    def top[B](n: Int, f: A => B)(implicit ord: Ordering[B]) : List[A] = {
        def updateSofar (sofar: PriorityQueue[A], el: A): PriorityQueue[A] = {
            if (ord.compare(f(el), f(sofar.head)) < 0){
                sofar.dequeue
                sofar.enqueue(el)
            }
            sofar
        }

        val (sofar, rest) = iter.splitAt(n)
        (PriorityQueue(sofar.toSeq:_*)( Ordering.by( (x :A) => f(x) ) ) /: rest) (updateSofar (_, _)).dequeueAll.toList.reverse
    }
}

case class A(s: String, i: Int)
val li = List (4, 3, 6, 7, 1, 2, 9, 5).map(i => A(i.toString(), i))
println(li.top(3, -_.i))
导入scala.language.implicitConversions
导入scala.language.ReflectVeCalls
导入collection.mutable.PriorityQueue
隐式定义iterExt[A](iter:Iterable[A])=新{
def top[B](n:Int,f:A=>B)(隐式命令:排序[B]):列表[A]={
def updateSofar(sofar:PriorityQueue[A],el:A):PriorityQueue[A]={
如果(命令比较(f(el),f(sofar.head))<0){
出列
索法尔排队(el)
}
水底测音装置
}
val(索法尔,其余)=iter.splitAt(n)
(PriorityQueue(sofar.toSeq:*)(Ordering.by((x:A)=>f(x))/:rest)(updateSofar(,)).dequeueAll.toList.reverse
}
}
案例类别A(s:String,i:Int)
valli=List(4,3,6,7,1,2,9,5).map(i=>A(i.toString(),i))
println(li.top(3,-u.i))

已排序。反向。取(2)
获得前2名。这很简单,但我不确定排序的效率,因为排序是建立在
java.util.Arrays.sort
之上的,因此这可能会创建很多临时数组。如果目标是避免对完整列表排序,那么它也会失败。为了确保我们在同一页上,您的解决方案返回底部2,这就是为什么我提到了
reverse
,以使您的解决方案工作。我认为
iter.toList.sortBy(u.myAttr).take(n)
在迭代器的大小接近
n
时足够简单有效。您是否认为
n
总是很小,并且可能处理较大的迭代器?我也使用这种语法处理较小的列表,但现在我遇到了一种情况,即任意长度的集合(可能会变得相当大)必须有效地处理,我通常需要这个集合的前5个或前10个元素。我认为使用列表作为累加器最适合这个练习。我建议使用优先级队列(不需要每次需要插入累加器时都使用)。想法?谢谢你的解决方案。它简短易懂。我采用它来处理任意类型的Iterables。不幸的是,stackoverflow不允许我将我的代码放在这个注释中。是采用了原始的解决方案,还是使用extremN的第二个解决方案?你可以在你的问题后面加上(但标记为“#update:”,以使讨论更容易理解)或自己打开一个答案。我使用了你的原文
val li = List (4, 3, 6, 7, 1, 2, 9, 5)    
top (2, li)
def extremeN [T](n: Int, li: List [T])
  (comp1: ((T, T) => Boolean), comp2: ((T, T) => Boolean)):
     List[T] = {

  def updateSofar (sofar: List [T], el: T) : List [T] =
    if (comp1 (el, sofar.head)) 
      (el :: sofar.tail).sortWith (comp2 (_, _)) 
    else sofar

  (li.take (n) .sortWith (comp2 (_, _)) /: li.drop (n)) (updateSofar (_, _)) 
}

/*  still bound to Int:  
def top (n: Int, li: List [Int]) : List[Int] = {
  extremeN (n, li) ((_ < _), (_ > _))
}
def bottom (n: Int, li: List [Int]) : List[Int] = {
  extremeN (n, li) ((_ > _), (_ < _))
}
*/

def top [T] (n: Int, li: List [T]) 
  (implicit ord: Ordering[T]): Iterable[T] = {
  extremeN (n, li) (ord.lt (_, _), ord.gt (_, _))
}
def bottom [T] (n: Int, li: List [T])
  (implicit ord: Ordering[T]): Iterable[T] = {
  extremeN (n, li) (ord.gt (_, _), ord.lt (_, _))
}

top (3, li)
bottom (3, li)
val sl = List ("Haus", "Garten", "Boot", "Sumpf", "X", "y", "xkcd", "x11")
bottom (2, sl)
def extremeN [T](n: Int, li: List [T])
  (comp1: ((T, T) => Boolean), comp2: ((T, T) => Boolean)):
     List[T] = {

  def sortedIns (el: T, list: List[T]): List[T] = 
    if (list.isEmpty) List (el) else 
    if (comp2 (el, list.head)) el :: list else 
      list.head :: sortedIns (el, list.tail)

  def updateSofar (sofar: List [T], el: T) : List [T] =
    if (comp1 (el, sofar.head)) 
      sortedIns (el, sofar.tail)
    else sofar

  (li.take (n) .sortWith (comp2 (_, _)) /: li.drop (n)) (updateSofar (_, _)) 
}
val big = (1 to 100000)

def maxes[A](n:Int)(l:Traversable[A])(implicit o:Ordering[A]) =
    l.foldLeft(collection.immutable.SortedSet.empty[A]) { (xs,y) =>
      if (xs.size < n) xs + y
      else {
        import o._
        val first = xs.firstKey
        if (first < y) xs - first + y
        else xs
      }
    }

println(maxes(4)(big))
println(maxes(2)(List("a","ab","c","z")))
def maxes2[A](n:Int)(l:Traversable[A])(implicit o:Ordering[A]) =
    l.foldLeft(List.empty[A]) { (xs,y) =>
      import o._
      if (xs.size < n) (y::xs).sort(lt _)
      else {
        val first = xs.head
        if (first < y) (y::(xs - first)).sort(lt _)
        else xs
      }
    }
def top[T](data: List[T], n: Int)(implicit ord: Ordering[T]): List[T] = {
    require( n < data.size)

    def partition_inner(shuffledData: List[T], pivot: T): List[T] = 
      shuffledData.partition( e => ord.compare(e, pivot) > 0 ) match {
          case (left, right) if left.size == n => left
          case (left, x :: rest) if left.size < n => 
            partition_inner(util.Random.shuffle(data), x)
          case (left @ y :: rest, right) if left.size > n => 
            partition_inner(util.Random.shuffle(data), y)
      }

     val shuffled = util.Random.shuffle(data)
     partition_inner(shuffled, shuffled.head)
}

scala> top(List.range(1,10000000), 5)
def top[T](data: List[T], n: Int)(implicit ord: Ordering[T]): List[T] = {
    require( n < data.size)

    @tailrec
    def partition_inner(shuffledData: List[T], pivot: T): List[T] = 
      shuffledData.par.partition( e => ord.compare(e, pivot) > 0 ) match {
          case (left, right) if left.size == n => left.toList
          case (left, right) if left.size < n => 
            partition_inner(util.Random.shuffle(data), right.head)
          case (left, right) if left.size > n => 
            partition_inner(util.Random.shuffle(data), left.head)
      }

     val shuffled = util.Random.shuffle(data)
     partition_inner(shuffled, shuffled.head)
}
def pickTopN[T](k: Int, iterable: Iterable[T])(implicit ord: Ordering[T]): Seq[T] {
  val q = collection.mutable.PriorityQueue[T](iterable.toSeq:_*)
  val end = Math.min(k, q.size)
  (1 to end).map(_ => q.dequeue())
}
def pickTopN[A, B](n: Int, iterable: Iterable[A], f: A => B)(implicit ord: Ordering[B]): Seq[A] = {
  val seq = iterable.toSeq
  val q = collection.mutable.PriorityQueue[A](seq.take(n):_*)(ord.on(f).reverse) // initialize with first n

  // invariant: keep the top k scanned so far
  seq.drop(n).foreach(v => {
    q += v
    q.dequeue()
  })

  q.dequeueAll.reverse
}
import scala.language.implicitConversions
import scala.language.reflectiveCalls
import collection.mutable.PriorityQueue
implicit def iterExt[A](iter: Iterable[A]) = new {
    def top[B](n: Int, f: A => B)(implicit ord: Ordering[B]) : List[A] = {
        def updateSofar (sofar: PriorityQueue[A], el: A): PriorityQueue[A] = {
            if (ord.compare(f(el), f(sofar.head)) < 0){
                sofar.dequeue
                sofar.enqueue(el)
            }
            sofar
        }

        val (sofar, rest) = iter.splitAt(n)
        (PriorityQueue(sofar.toSeq:_*)( Ordering.by( (x :A) => f(x) ) ) /: rest) (updateSofar (_, _)).dequeueAll.toList.reverse
    }
}

case class A(s: String, i: Int)
val li = List (4, 3, 6, 7, 1, 2, 9, 5).map(i => A(i.toString(), i))
println(li.top(3, -_.i))