按保证金分组

时间:2014-06-09 21:26:45

标签: scala

我有一系列的Int号码:

val numbers = Seq(5, 3, 4, 1)

我需要根据他们的不同对他们进行分组。差异必须小于或等于某个阈值,对于此示例,它应为2。所以可能的群体是:

  • (5,3,4)(1)
  • (1,3)(5,4)

我真的不关心我会得到哪些群体。允许每个元素使用一次。我还需要保留索引,所以在分组之前我需要一个zipWithIndex。

有没有聪明的方法来进行这样的分组?

2 个答案:

答案 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))),并在序列中保存一次迭代,只需要很少的额外详细程度。