在此代码块之前定义:
dataset
可以是Vector
或List
numberOfSlices
是一个Int
,表示切片数据集的“次数”我想将数据集拆分为numberOfSlices
个切片,尽可能均匀分布。通过“拆分”我想我的意思是“分区”(所有的交集应该是空的,所有的联合应该是原始的)来使用集合理论术语,尽管这不一定是集合,只是一个任意集合。
e.g。
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
}
答案 0 :(得分: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)
}
答案 1 :(得分: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))
答案 2 :(得分:3)
这是一个为我工作的单线程,使用熟悉的递归函数的Scala技巧返回Slider
。请注意使用label2.SetBinding(Label.ContentProperty, new Binding("Value") { Source = sl });
来舍入块大小,在最终列表中插入较小和较大的块,所有块的大小最多只有一个差异元素。如果您使用Slider sl;
Label label1;
Label label2;
private void AllFour_Unchecked(object sender, RoutedEventArgs e)
{
label1 = new Label();
label1.HorizontalAlignment = HorizontalAlignment.Center;
sl = new Slider();
sl.Minimum = 1;
sl.Maximum = 4;
sl.TickFrequency = 1;
sl.IsSnapToTickEnabled = true;
sl.SetResourceReference(Control.StyleProperty, "SliderStyle");
sl.SetResourceReference(Slider.ForegroundProperty, "SliderSelectionRangeBackgroundBrush");
label2 = new Label();
label2.HorizontalAlignment = HorizontalAlignment.Center;
label2.BorderBrush = new SolidColorBrush(Colors.Gray);
label2.SetBinding(Label.ContentProperty, new Binding("Value") { Source = sl });
Dispatcher.BeginInvoke(new Action(() =>
{
Upgrades.Children.Add(label1);
Upgrades.Children.Add(sl);
Upgrades.Children.Add(label2);
}), System.Windows.Threading.DispatcherPriority.Background);
}
private void AllFour_Checked(object sender, RoutedEventArgs e)
{
Upgrades.Children.Remove(label1);
Upgrades.Children.Remove(sl);
Upgrades.Children.Remove(label2);
}
进行向上舍入,则将较小的块移动到末尾,Stream
将它们移动到开头。
(x+k/2)/k
演示:
(x+k-1)/k
请注意x/k
如何不尝试均衡所有子列表的大小。
答案 3 :(得分: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)
}
答案 4 :(得分: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的(m-y)广告位。你如何分发它们是你的选择。
答案 5 :(得分:0)
这是我要解决的问题:
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
}
它比其他一些解决方案更为冗长,但也希望更加清晰。 撤消仅在您希望保留订单时才需要。