Defined before this block of code:
dataset
can be aVector
orList
numberOfSlices
is anInt
denoting how many "times" to slice dataset
I want to split the dataset into numberOfSlices
slices, distributed as evenly as possible. By "split" I guess I mean "partition" (intersection of all should be empty, union of all should be the original) to use the set theory term, though this is not necessarily a set, just an arbitrary collection.
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))
Is there a better way to do it than what I have below? (which I'm not even sure is optimal...) Or perhaps this is not an algorithmically feasible endeavor, in which case any known good heuristics?
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
}
As Kaito mentions
grouped
is exactly what you are looking for. But if you just want to know how to implement such a method, there are many ways ;-). You could for example do it like this:If the behavior of
xs.grouped(xs.size / n)
doesn't work for you, it's pretty easy to define exactly what you want. The quotient is the size of the smaller pieces, and the remainder is the number of the bigger pieces:Here's a one-liner that does the job for me, using the familiar Scala trick of a recursive function that returns a
Stream
. Notice the use of(x+k/2)/k
to round the chunk sizes, intercalating the smaller and larger chunks in the final list, all with sizes with at most one element of difference. If you round up instead, with(x+k-1)/k
, you move the smaller blocks to the end, andx/k
moves them to the beginning.Demo:
Notice how
grouped
does not try to even out the size of all the sub-lists.I'd approach it this way: Given
n
elements andm
partitions (n>m), either n mod m == 0 in which case, each partition will have n/m elements, or n mod m = y, in which case you'll have each partition withn/m
elements and you have to distributey
over somem
.You'll have
y
slots withn/m+1
elements and (m-y) slots with n/m. How you distribute them is your choice.The typical "optimal" partition calculates an exact fractional length after cutting and then rounds to find the actual number to take:
This way your longer and shorter blocks will be interspersed, which is often more desirable for evenness:
You can even cut more times than you have elements: