Algorithm 将集合划分为;k";接近相等的片段(Scala,但语言不可知)
在此代码块之前定义:Algorithm 将集合划分为;k";接近相等的片段(Scala,但语言不可知),algorithm,scala,slice,Algorithm,Scala,Slice,在此代码块之前定义: 数据集可以是向量或列表 numberOfSlices是一个Int表示对数据集进行切片的次数 我想将数据集分割成numberOfSlicesslices,尽可能均匀地分布。我想我所说的“分割”是指使用集合论术语的“分割”(所有的交集应该是空的,所有的并集应该是原始的),尽管这不一定是一个集合,只是一个任意的集合 e、 g 有没有比下面的更好的方法?(我甚至不确定这是否是最佳的…) 或者这可能不是一个算法上可行的尝试,在这种情况下,任何已知的好的启发式 val slices
可以是数据集
或向量
列表
是一个numberOfSlices
表示对数据集进行切片的次数Int
numberOfSlices
slices,尽可能均匀地分布。我想我所说的“分割”是指使用集合论术语的“分割”(所有的交集应该是空的,所有的并集应该是原始的),尽管这不一定是一个集合,只是一个任意的集合
e、 g
有没有比下面的更好的方法?(我甚至不确定这是否是最佳的…)
或者这可能不是一个算法上可行的尝试,在这种情况下,任何已知的好的启发式
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
}
正如Kaito所提到的,分组的正是您想要的。但是,如果您只想知道如何实现这种方法,有很多方法;-)。例如,您可以这样做:
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)
}
如果
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)
}
我会这样处理:给定
n
元素和m
分区(n>m),要么n mod m==0,在这种情况下,每个分区都有n/m个元素,要么n mod m=y,在这种情况下,每个分区都有n/m
元素,并且必须将y
分布在一些m
上
您将拥有y
插槽和n/m+1
元素,以及(m-y)插槽和n/m元素。如何分配它们是您的选择。典型的“最佳”分区在切割后计算精确的分数长度,然后四舍五入以找到实际要采用的数量:
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),5)
res6: Vector[Seq[Int]] =
Vector(List(1), List(), List(2), List(), List(3))
这里有一个为我做这项工作的单行程序,它使用了我们熟悉的Scala技巧,即返回
流的递归函数。请注意使用(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 index=scala.util.Random.shuffle(1到39)
scala>for(ff for)(ff for)(ff for)(ff以下是我对这个问题的看法:
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
}
它比其他一些解决方案更详细,但希望也更清晰。只有在您希望保留顺序时才需要反向。我不确定如何解释“尽可能均匀地分布”。按照您的代码,Seq:grouped(Int)
已经做了你想做的事情,只是它从来没有超过切片大小。似乎分组
会将它分成“x”组,而我想把一个集合分成“x”组。我在回复中尝试了它,列表(1,2,3,4,5)。分组(2)。toList
给出了列表(列表(1,2),列表(3,4),列表(5))
而我想要的是类似于列表(列表(1,2),列表(3,4,5))
的东西。这很好,但不幸地延伸了“尽可能均匀分布”的规定要求,因为所有的“大”段都排在最后-例如,剪切(1到15,10)。toList.map(\uu.size)
产生5个单元素分段,后面是5个双元素分段。分组后的子列表可能会比其他子列表小得多。@Vasily802我不知道为什么#::
可能不起作用,但我已经替换了它,并且对代码进行了一些改进,并修复了演示。谢谢。。.
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
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
}