我的问题
我正在尝试从非常大的笛卡尔积中生成一小部分可能的组合。我的输入是一个数组数组,但每个数组的大小是动态的。目前,我正在使用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个可能的设置时,这将花费很长时间才能找到一个非常小的子集。我感谢所有的帮助以及我可以寻求的任何其他资源,以获得有关该主题的更多知识。
答案 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