19

在这段代码之前定义:

  • dataset可以是VectorList
  • numberOfSlices表示Int对数据集进行切片的次数

我想将数据集分成多个numberOfSlices切片,尽可能均匀地分布。“分裂”我想我的意思是“分区”(所有的交集应该是空的,所有的联合应该是原始的)使用集合论术语,虽然这不一定是一个集合,只是一个任意集合。

例如

dataset = List(1, 2, 3, 4, 5, 6, 7)
numberOfSlices = 3
slices == ListBuffer(Vector(1, 2), Vector(3, 4), Vector(5, 6, 7))

有没有比我下面的更好的方法?(我什至不确定这是最优的......)或者这可能不是算法上可行的努力,在这种情况下,任何已知的好的启发式方法?

val slices = new ListBuffer[Vector[Int]]
val stepSize = dataset.length / numberOfSlices
var currentStep = 0
var looper = 0
while (looper != numberOfSlices) {
  if (looper != numberOfSlices - 1) {
    slices += dataset.slice(currentStep, currentStep + stepSize)
    currentStep += stepSize
  } else {
    slices += dataset.slice(currentStep, dataset.length)
  }
  looper += 1
}
4

6 回答 6

14

如果 的行为xs.grouped(xs.size / n)对您不起作用,那么很容易准确地定义您想要的内容。商是小块的大小,余数是大块的数量:

def cut[A](xs: Seq[A], n: Int) = {
  val (quot, rem) = (xs.size / n, xs.size % n)
  val (smaller, bigger) = xs.splitAt(xs.size - rem * (quot + 1))
  smaller.grouped(quot) ++ bigger.grouped(quot + 1)
}
于 2012-07-12T16:55:57.933 回答
7

典型的“最佳”分区在切割后计算精确的小数长度,然后四舍五入以找到要取的实际数字:

def cut[A](xs: Seq[A], n: Int):Vector[Seq[A]] = {
  val m = xs.length
  val targets = (0 to n).map{x => math.round((x.toDouble*m)/n).toInt}
  def snip(xs: Seq[A], ns: Seq[Int], got: Vector[Seq[A]]): Vector[Seq[A]] = {
    if (ns.length<2) got
    else {
      val (i,j) = (ns.head, ns.tail.head)
      snip(xs.drop(j-i), ns.tail, got :+ xs.take(j-i))
    }
  }
  snip(xs, targets, Vector.empty)
}

这样,您的更长和更短的块将被穿插,这通常更适合均匀性:

scala> cut(List(1,2,3,4,5,6,7,8,9,10),4)
res5: Vector[Seq[Int]] = 
  Vector(List(1, 2, 3), List(4, 5), List(6, 7, 8), List(9, 10))

您甚至可以剪切比您拥有的元素更多的次数:

scala> cut(List(1,2,3),5)
res6: Vector[Seq[Int]] = 
  Vector(List(1), List(), List(2), List(), List(3))
于 2012-07-12T18:27:43.187 回答
4

这是一个为我完成这项工作的单线器,它使用了熟悉的 Scala 技巧,即递归函数返回一个Stream. 注意使用(x+k/2)/k来舍入块大小,在最终列表中插入更小和更大的块,所有块的大小都最多有一个差异元素。如果您使用 向上取整,(x+k-1)/k则将较小的块移动到末尾,然后x/k将它们移动到开头。

def k_folds(k: Int, vv: Seq[Int]): Stream[Seq[Int]] =
    if (k > 1)
        vv.take((vv.size+k/2)/k) +: k_folds(k-1, vv.drop((vv.size+k/2)/k))
    else
        Stream(vv)

演示:

scala> val indices = scala.util.Random.shuffle(1 to 39)

scala> for (ff <- k_folds(7, indices)) println(ff)
Vector(29, 8, 24, 14, 22, 2)
Vector(28, 36, 27, 7, 25, 4)
Vector(6, 26, 17, 13, 23)
Vector(3, 35, 34, 9, 37, 32)
Vector(33, 20, 31, 11, 16)
Vector(19, 30, 21, 39, 5, 15)
Vector(1, 38, 18, 10, 12)

scala> for (ff <- k_folds(7, indices)) println(ff.size)
6
6
5
6
5
6
5

scala> for (ff <- indices.grouped((indices.size+7-1)/7)) println(ff)
Vector(29, 8, 24, 14, 22, 2)
Vector(28, 36, 27, 7, 25, 4)
Vector(6, 26, 17, 13, 23, 3)
Vector(35, 34, 9, 37, 32, 33)
Vector(20, 31, 11, 16, 19, 30)
Vector(21, 39, 5, 15, 1, 38)
Vector(18, 10, 12)

scala> for (ff <- indices.grouped((indices.size+7-1)/7)) println(ff.size)
6
6
6
6
6
6
3

请注意如何grouped不尝试平衡所有子列表的大小。

于 2017-02-14T21:53:00.827 回答
1

这是我对这个问题的看法:

  def partition[T](items: Seq[T], partitionsCount: Int): List[Seq[T]] = {
    val minPartitionSize = items.size / partitionsCount
    val extraItemsCount = items.size % partitionsCount

    def loop(unpartitioned: Seq[T], acc: List[Seq[T]], extra: Int): List[Seq[T]] =
      if (unpartitioned.nonEmpty) {
        val (splitIndex, newExtra) = if (extra > 0) (minPartitionSize + 1, extra - 1) else (minPartitionSize, extra)
        val (newPartition, remaining) = unpartitioned.splitAt(splitIndex)
        loop(remaining, newPartition :: acc, newExtra)
      } else acc

    loop(items, List.empty, extraItemsCount).reverse
  }

它比其他一些解决方案更冗长,但也希望更清晰。仅当您希望保留订单时才需要反向。

于 2019-05-04T13:58:02.130 回答
0

正如 Kaito 所提到grouped的,这正是您正在寻找的。但是如果你只是想知道如何实现这样的方法,有很多方法;-)。例如,您可以这样做:

def grouped[A](xs: List[A], size: Int) = {
  def grouped[A](xs: List[A], size: Int, result: List[List[A]]): List[List[A]] = {
    if(xs.isEmpty) {
      result
    } else {
      val (slice, rest) = xs.splitAt(size)
      grouped(rest, size, result :+ slice)
    }
  }
  grouped(xs, size, Nil)
}
于 2012-07-12T16:52:24.353 回答
0

我会这样处理:给定n元素和m分区 (n>m),n mod m == 0 在这种情况下,每个分区将有 n/m 个元素,或者 n mod m = y,在这种情况下你'每个分区都有n/m元素,你必须分布y在一些m.

您将拥有y带有n/m+1元素的插槽和带有 n/m 的(我的)插槽。你如何分配它们是你的选择。

于 2012-07-12T17:02:43.627 回答