我有一系列的Int号码:
val numbers = Seq(5, 3, 4, 1)
我需要根据他们的不同对他们进行分组。差异必须小于或等于某个阈值,对于此示例,它应为2。所以可能的群体是:
我真的不关心我会得到哪些群体。允许每个元素使用一次。我还需要保留索引,所以在分组之前我需要一个zipWithIndex。
有没有聪明的方法来进行这样的分组?
答案 0 :(得分:2)
好的。算法的想法:
获取numbers
中的下一个元素。检查它是否属于先前创建的组。如果是,请将其添加到该组。如果没有,请添加包含该元素的新组。
我使用IndexedSeq
因为我希望索引为O(1)
。
有点长,但我现在想不出更好的东西。我希望我能够理解你对#34;差异"
的想法val numbers = Seq(5, 3, 4, 1)
def group(seq: Seq[Int], treshold: Int) = seq.zipWithIndex.foldLeft(IndexedSeq.empty[IndexedSeq[(Int,Int)]])((result, elem) => {
(0 until result.size).find(
i => result(i).forall(num => (num._1 - elem._1).abs <= treshold)).map(
i => result.updated(i, result(i) :+ elem))
.getOrElse(result :+ IndexedSeq(elem))
})
println(group(numbers, 2)) //result Vector(Vector((5,0), (3,1), (4,2)), Vector((1,3)))
修改忘记了您想zipWithIndex
答案 1 :(得分:2)
由于您无论如何都在使用元素索引,您可能也不关心使用组的索引,在这种情况下,Kigyo的答案可能是正确答案。
函数式编程的一个好处是,它通常可以让你免于使用索引,所以为了完整起见,这是一个使用span
的实现。需要跟踪组的索引(首先是没有元素索引的简单形式):
val numbers = Seq(5, 3, 4, 1)
numbers.foldLeft(List.empty[List[Int]]) {
case (acc, x) => acc.span(_.exists(y => math.abs(x - y) > 2)) match {
case (bad, picked :: rest) => (x :: picked) :: rest ::: bad
case (bad, _) => List(x) :: bad
}
}
如果您还没有zipWithIndex
- 编辑numbers
,您还可以在折叠过程中照顾它,而不会有太多额外的麻烦:
val numbers = Seq(5, 3, 4, 1)
numbers.foldLeft(List.empty[List[(Int, Int)]], 0) {
case ((acc, i), x) => acc.span(_.exists(y => math.abs(x - y._1) > 2)) match {
case (bad, picked :: rest) => (((x, i) :: picked) :: rest ::: bad, i + 1)
case (bad, _) => (List((x, i)) :: bad, i + 1)
}
}._1
这会按预期返回List(List((1, 3)), List((4, 2), (3, 1), (5, 0)))
,并在序列中保存一次迭代,只需要很少的额外详细程度。