如何在给定索引的情况下生成特定的笛卡尔积

时间:2018-03-20 00:23:08

标签: python set itertools cartesian-product

我的问题

我正在尝试从非常大的笛卡尔积中生成一小部分可能的组合。我的输入是一个数组数组,但每个数组的大小是动态的。目前,我正在使用Python,但我对任何需要使用的语言持开放态度。

我的目标

在看到这个问题:How to select specific item from cartesian product without calculating every other item后,我认为这是一个令人惊叹的算法,可以生成给定索引的集合。但是,这仅适用于3个阵列。我的最终目标是这样的,确定集合的函数是find_set

Input:
A = [ A_0, A_1, A_2, ..., A_n ]
B = [ B_0, B_1, B_2, ..., B_n ]
C = [ C_0, C_1, C_2, ..., C_n ]
D = [ D_0, D_1, D_2, ..., D_n ]
...
N = [ N_0, N_1, D_2, ..., N_n ]

List = [ A, B, C, D, ... N]

find_set(List, 0) -> [ A_0, B_0, C_0, ..., N_0 ]
find_set(List, 1) -> [ A_0, B_0, C_0, ..., N_1 ]
...

等等,对于任何给定的索引。

到目前为止我做了什么

我使用Python 2.7和itertools.product来生成所有组合,但这只是一个迭代器。在遇到内存消耗问题后,我尝试了这样的事情:

results = itertools.product(*List)

# generates 100,000 random indices between 0 and the size of the Cartesian Product
desired_indices = random.sample(xrange(0, calculated_size - 1), 100000) 

for item, index in results:
    if index in desired_indices:
          # do things

问题是,无论如何都会导致O(N)操作,当我有433,501,216个可能的设置时,这将花费很长时间才能找到一个非常小的子集。我感谢所有的帮助以及我可以寻求的任何其他资源,以获得有关该主题的更多知识。

2 个答案:

答案 0 :(得分:0)

我不会说Python,但我为Scala编写了一个迭代器,它不需要存储中间结果,只需要存储索引。

告诉我,如果您需要有关语法的更多信息。

基本思路如下:如果你有两个列表,三个中的一个和两个元素中的一个,(a,b,c)和(1,2),你可以生成(a1,a2,然后是b1, b2,最后是c1,c2)。它完全由每个列表的长度控制,因此必须能够提前计算笛卡尔积的大小(2 * 3)或一般情况下的长度乘积,并取每个长度的模数正确的顺序,为(0..size-1)中的每个数字返回一组不同的元素。

class CartesianIterator [T] (val ll: Seq[Seq[T]]) extends Iterator [Seq[T]] { // with IndexedSeq [Seq[T]] {
  var current = 0L
  override val size = ll.map (_.size).product

  def get (n: Long): List[T] = {

      def get (n: Long, lili: Seq[Seq[T]]): List[T] = lili.length match {
        case 0L => List ()
        case _ => {
          val inner = lili.head
          inner ((n % inner.size).toInt) :: get (n / inner.size, lili.tail)
        }
      }

      get (n, ll)
  }

  override def hasNext () : Boolean = current != size
  override def next (): Seq[T] = {
    current += 1
    get (current - 1)
  }

    // IndexedSeq: Selects an element by its index in the sequence.
    // val x = CartesianIterator (...)
    // val res = x(123) // = x.apply (123)
    def apply (idx: Long): Seq[T] = {
        current = idx-1L
        next ()
    }
}

def exhaustiveDemo () {
  val ci = new CartesianIterator (List(List ('a', 'b'), List (1, 2, 3, 4), List ("foo", "bar", "baz")))
  for (p <-ci) println (p)
}

def massiveDemo () {
    val r = util.Random
    // 8 bit per value, ...
    val li = (0 to 256).toList
    // 256^8 combinations, 0 to Long.MaxValue
    val ll = List (li, li, li, li, li, li, li, li)
    val ci = new CartesianIterator (ll)
    for (i <- 0 to 9;
        rnd = math.abs(r.nextLong ());
        tuple = ci.get (rnd)
    ) println (tuple.mkString (":") + " at idx: " + rnd)
}

exhaustiveDemo ()
massiveDemo ()

示例输出:

List(a, 1, foo)
List(b, 1, foo)
List(a, 2, foo)
//...
List(b, 3, baz)
List(a, 4, baz)
List(b, 4, baz)

92:167:65:79:242:74:167:67 at idx: 5009630557992325817
252:176:16:94:68:30:43:44 at idx: 3270674835001624787
113:63:109:2:2:184:2:82 at idx: 6072977656466246445
95:68:232:237:171:233:183:114 at idx: 8494823201479688501
181:241:90:213:40:128:134:57 at idx: 4259670111450561340
29:113:165:134:150:89:247:72 at idx: 5402953717167499239
87:30:93:211:245:210:1:83 at idx: 6146770892844404160
25:205:116:230:196:105:62:37 at idx: 2757875964952116944
194:68:71:160:63:57:204:41 at idx: 3094941632910767450
166:133:37:249:17:6:215:92 at idx: 6874662895836376467

答案 1 :(得分:0)

我实际上能够自己解决这个问题。如果其他人遇到这个问题,这里有一个Python实现:

import math

class LazyCartesianProduct:
    def __init__(self, sets):
        self.sets = sets
        self.divs = []
        self.mods = []
        self.maxSize = 1
        self.precompute()

    def precompute(self):
        for i in self.sets:
            self.maxSize = self.maxSize * len(i)
        length = len(self.sets)
        factor = 1
        for i in range((length - 1), -1, -1):
            items = len(self.sets[i])
            self.divs.insert(0, factor)
            self.mods.insert(0, items)
            factor = factor * items

    def entryAt(self, n):
        length = len(self.sets)
        if n < 0 or n >= self.maxSize:
            raise IndexError
        combination = []
        for i in range(0, length):
            combination.append(self.sets[i][ int(math.floor(n / self.divs[i])) % self.mods[i]])
        return combination

这实现了此网站的算法:http://phrogz.net/lazy-cartesian-product