这个代码块前的定义:
-
dataset
可以是一个Vector
或List
-
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
}
Answer 1:
如果行为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)
}
Answer 2:
典型的“最佳”的分区在切割之后,计算一个精确的分数的长度,然后舍入到找到的实际数量取:
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))
Answer 3:
这里有一个一行,没有工作对我来说,使用返回一个递归函数的熟悉斯卡拉招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
并不试图拉平所有子列表的大小。
Answer 4:
这是我对这个问题:
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
}
这比一些其他解决方案更详细,但希望更清楚为好。 如果你想为了保存反向才是必需的。
Answer 5:
由于怪盗提到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)
}
Answer 6:
我接近这样说:给定n
元素和m
分区(N> M),是n模m == 0在此情况下,每一分区将具有N / m个元素,或正模m = Y,在此情况下你必须与每个分区n/m
的元素,你必须分发y
对一些m
。
你必须y
与插槽n/m+1
的元素和(我的)插槽N / M。 你如何分配这些是你的选择。
文章来源: Partition a collection into “k” close-to-equal pieces (Scala, but language agnostic)