生成可能的组合

时间:2011-09-26 19:39:18

标签: scala

我有几个哈希映射,我需要生成以下组合:

A: [x->1, y->2,...]
B: [x->1, a->4,...]
C: [x->1, b->5,...]
...

一些可能的组合:

A+B; A; A+C; A+B+C...

对于每个组合,我需要生成联合散列映射,并在两个散列映射中使用相同的键执行键值对的操作。

我所能想到的就是使用二进制计数器并将数字映射到相应的哈希映射:

001 -> A
101 -> A,C
...

虽然这个解决方案有效但是当我有超过100个哈希映射时,模运算很耗时。我是Scala的新手,但我相信必须有更好的方法来实现这个目标吗?

3 个答案:

答案 0 :(得分:14)

Scala序列具有combinations函数。这为您提供了从总数中选择特定数字的组合。从您的问题来看,您似乎想要选择所有不同的数字,因此理论上的代码可能类似于:

val elements = List('a, 'b, 'c, 'd)
(1 to elements.size).flatMap(elements.combinations).toList

/* List[List[Symbol]] = List(List('a), List('b), List('c), List('d), List('a, 'b), 
   List('a, 'c), List('a, 'd), List('b, 'c), List('b, 'd), List('c, 'd), 
   List('a, 'b, 'c), List('a, 'b, 'd), List('a, 'c, 'd), List('b, 'c, 'd), 
   List('a, 'b, 'c, 'd)) */

但正如所指出的,所有组合都会太多。使用100个元素,从100中选择2将给你4950个组合,3个给你161700,4个给你3921225,5个可能会给你一个溢出错误。因此,如果您只是将combinations的参数保持为2或3,那么您应该没问题。

答案 1 :(得分:12)

好吧,想想你的地图有多少组合:假设你有N个地图。

(the maps individually) + (pairs of maps) + (triples of maps) + ... + (all the maps)

当然是

(N choose 1) + (N choose 2) + ... + (N choose N-1)

N choose M定义为:

N! / (M! * (N-M)!)

对于N=100M=50N choose M超过100,000,000,000,000,000,000,000,000,000,因此“耗费时间”确实无法解决问题!

哦,这假设排序无关紧要 - 即A + B等于B + A。如果这个假设是错误的,那么你面临的排列明显多于可见宇宙中的粒子

为什么scala可能会帮助解决这个问题:它的并行集合框架!

答案 2 :(得分:0)

跟进你的想法,使用整数来表示位集。您使用的是实际的模运算符吗?您还可以使用位掩码来检查某个数字是否在bitset中。 (注意,在JVM上它们都是一个指令操作,所以谁知道那里发生了什么。)

另一个潜在的重大改进是,由于您对地图范围的操作是关联的,因此您可以通过重复使用以前的计算来节省计算。例如,如果您将A,B,C合并,但已将A,C合并到AC中,则可以将BAC合并。< / p>

以下代码实现了两个想法:

type MapT = Map[String,Int] // for conciseness later

@scala.annotation.tailrec
def pow2(i : Int, acc : Int = 1) : Int = {
  // for reasonably sized ints...
  if(i <= 0) acc else pow2(i - 1, 2 * acc)
}

// initial set of maps
val maps = List(
  Map("x" -> 1, "y" -> 2),
  Map("x" -> 1, "a" -> 4),
  Map("x" -> 1, "b" -> 5)
)
val num = maps.size

// any 'op' that's commutative will do
def combine(m1 : MapT, m2 : MapT)(op : (Int,Int)=>Int) : MapT = 
  ((m1.keySet intersect m2.keySet).map(k => (k -> op(m1(k), m2(k))))).toMap

val numCombs = pow2(num)

// precomputes all required powers of two
val masks : Array[Int] = (0 until num).map(pow2(_)).toArray

// this array will be filled, à la Dynamic Algorithm
val results : Array[MapT] = Array.fill(numCombs)(Map.empty)
// fill in the results for "combinations" of one map
for((m,i) <- maps.zipWithIndex) { results(masks(i)) = m }

val zeroUntilNum = (0 until num).toList
for(n <- 2 to num; (x :: xs) <- zeroUntilNum.combinations(n)) {
  // The trick here is that we already know the result of combining the maps
  // indexed by xs, we just need to compute the corresponding bitmask and get
  // the result from the array later.
  val known = xs.foldLeft(0)((a,i) => a | masks(i))
  val xm = masks(x)
  results(known | xm) = combine(results(known), results(xm))(_ + _)
}

如果打印结果数组,则得到:

0  ->  Map()
1  ->  Map(x -> 1, y -> 2)
2  ->  Map(x -> 1, a -> 4)
3  ->  Map(x -> 2)
4  ->  Map(x -> 1, b -> 5)
5  ->  Map(x -> 2)
6  ->  Map(x -> 2)
7  ->  Map(x -> 3)

当然,和其他人一样,当输入图的数量增加时,它最终会爆炸。