如何迭代计算笛卡尔积?

发布于 2024-08-25 00:48:22 字数 944 浏览 7 评论 0原文

这个问题询问如何计算给定数量向量的笛卡尔积。由于向量的数量是预先已知的并且相当小,因此可以通过嵌套的 for 循环轻松获得解决方案。

现在假设您以您选择的语言给出了一个向量向量(或列表列表,或集合集合等):

l = [ [1,2,3], [4,5], [6,7], [8,9,10], [11,12], [13] ]

如果要求我计算其笛卡尔积,那就是

[ [1,4,6,8,11,13], [1,4,6,8,12,13], [1,4,6,9,11,13], [1,4,6,9,12,13], ... ]

我将继续进行递归。例如,在 fast&dirty python 中,

def cartesianProduct(aListOfLists):
    if not aListOfLists:
        yield []
    else:
        for item in aListOfLists[0]:
            for product in cartesianProduct(aListOfLists[1:]):
                yield [item] + product

是否有一种简单的方法来迭代计算

(注意:答案不需要在 python 中,无论如何我知道在 python 中 itertools 可以更好地完成工作,如 这个问题。)

This question asks how to compute the Cartesian product of a given number of vectors. Since the number of vectors is known in advance and rather small, the solution is easily obtained with nested for loops.

Now suppose that you are given, in your language of choice, a vector of vectors (or list of lists, or set of sets, etc.):

l = [ [1,2,3], [4,5], [6,7], [8,9,10], [11,12], [13] ]

If I was asked to compute its Cartesian product, that is

[ [1,4,6,8,11,13], [1,4,6,8,12,13], [1,4,6,9,11,13], [1,4,6,9,12,13], ... ]

I would proceed with recursion. For example, in quick&dirty python,

def cartesianProduct(aListOfLists):
    if not aListOfLists:
        yield []
    else:
        for item in aListOfLists[0]:
            for product in cartesianProduct(aListOfLists[1:]):
                yield [item] + product

Is there an easy way to compute it iteratively?

(Note: The answer doesn't need to be in python, and anyway I'm aware that in python itertools does the job better, as in this question.)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

临风闻羌笛 2024-09-01 00:48:22

1) 在各个列表中创建索引列表,初始化为 0,即:

indexes = [0,0,0,0,0,0]

2) 从每个列表中生成适当的元素(在本例中为第一个)。

3) 将最后一个索引加一。

4) 如果最后一个索引等于最后一个列表的长度,则将其重置为零并进位1。重复此操作,直到没有进位。

5) 返回步骤 2,直到索引返回到 [0,0,0,0,0,0]

这与计数的工作原理类似,只是每个数字的基数可以不同。


下面是上述算法在 Python 中的实现:

def cartesian_product(aListOfList):
    indexes = [0] * len(aListOfList)
    while True:
        yield [l[i] for l,i in zip(aListOfList, indexes)]
        j = len(indexes) - 1
        while True:
            indexes[j] += 1
            if indexes[j] < len(aListOfList[j]): break
            indexes[j] = 0
            j -= 1
            if j < 0: return

这是使用模技巧实现它的另一种方法:

def cartesian_product(aListOfList):
    i = 0
    while True:
        result = []
        j = i
        for l in aListOfList:
             result.append(l[j % len(l)])
             j /= len(l)
        if j > 0: return
        yield result
        i += 1

请注意,这输出结果的顺序与示例中的顺序略有不同。这可以通过以相反顺序迭代列表来解决。

1) Create a list of indexes into the respective lists, initialized to 0, i.e:

indexes = [0,0,0,0,0,0]

2) Yield the appropriate element from each list (in this case the first).

3) Increase the last index by one.

4) If the last index equals the length of the last list, reset it to zero and carry one. Repeat this until there is no carry.

5) Go back to step 2 until the indexes wrap back to [0,0,0,0,0,0]

It's similar to how counting works, except the base for each digit can be different.


Here's an implementation of the above algorithm in Python:

def cartesian_product(aListOfList):
    indexes = [0] * len(aListOfList)
    while True:
        yield [l[i] for l,i in zip(aListOfList, indexes)]
        j = len(indexes) - 1
        while True:
            indexes[j] += 1
            if indexes[j] < len(aListOfList[j]): break
            indexes[j] = 0
            j -= 1
            if j < 0: return

Here is another way to implement it using modulo tricks:

def cartesian_product(aListOfList):
    i = 0
    while True:
        result = []
        j = i
        for l in aListOfList:
             result.append(l[j % len(l)])
             j /= len(l)
        if j > 0: return
        yield result
        i += 1

Note that this outputs the results in a slightly different order than in your example. This can be fixed by iterating over the lists in reverse order.

残月升风 2024-09-01 00:48:22

既然你要求一种与语言无关的解决方案,这里有一个 bash 的解决方案,但我们可以称之为迭代、递归吗?它是什么?这只是符号:

echo {1,2,3},{4,5},{6,7},{8,9,10},{11,12},13

也许足够有趣。

1,4,6,8,11,13 1,4,6,8,12,13 1,4,6,9,11,13 1,4,6,9,12,13 1,4,6,10,11,13 ...

Since you asked for a language-agnostic solution, here is one in bash, but can we call it iterative, recursive, what is it? It's just notation:

echo {1,2,3},{4,5},{6,7},{8,9,10},{11,12},13

maybe interesting enough.

1,4,6,8,11,13 1,4,6,8,12,13 1,4,6,9,11,13 1,4,6,9,12,13 1,4,6,10,11,13 ...
只是一片海 2024-09-01 00:48:22

对于所有 i,从 0 迭代到 \Pi a_i_length

for ( int i = 0; i < product; i++ ) {
    // N is the number of lists
    int now = i;
    for ( int j = 0; j < N; j++ ) {
        // This is actually the index, you can get the value easily.
        current_list[j] = now % master_list[j].length;

        // shifts digit (integer division)
        now /= master_list[j].length;  
    }
}

还有一些简单的方法可以编写此代码,这样您就不必重复执行相同的工作。

Iterate from 0 to \Pi a_i_length for all i.

for ( int i = 0; i < product; i++ ) {
    // N is the number of lists
    int now = i;
    for ( int j = 0; j < N; j++ ) {
        // This is actually the index, you can get the value easily.
        current_list[j] = now % master_list[j].length;

        // shifts digit (integer division)
        now /= master_list[j].length;  
    }
}

There are also some trivial ways to write this so you don't have to do the same work twice.

苦行僧 2024-09-01 00:48:22

您只需手动管理堆栈即可。基本上,你可以自己做递归所做的事情。由于递归将有关每个递归调用的数据放在堆栈上,因此您只需执行相同的操作:

Let L[i] = elements in vector i
k = 0;
st[] = a pseudo-stack initialized with 0
N = number of vectors 
while ( k > -1 )
{
  if ( k == N ) // solution, print st and --k

  if ( st[k] < L[k].count )
  {
    ++st[k]
    ++k
  }
  else
  {
    st[k] = 0;
    --k;
  }
} 

未经测试,但该想法可行。希望我没有错过任何事情。

编辑:好吧,我想太晚了。这与计数基本相同,只是另一种看待它的方式。

You just have to manage your stack manually. Basically, do what recursion does on your own. Since recursion puts data about each recursive call on a stack, you just do the same:

Let L[i] = elements in vector i
k = 0;
st[] = a pseudo-stack initialized with 0
N = number of vectors 
while ( k > -1 )
{
  if ( k == N ) // solution, print st and --k

  if ( st[k] < L[k].count )
  {
    ++st[k]
    ++k
  }
  else
  {
    st[k] = 0;
    --k;
  }
} 

Not tested, but the idea will work. Hopefully I didn't miss anything.

Edit: well, too late I guess. This is basically the same as counting, just another way of looking at it.

如梦 2024-09-01 00:48:22

这个解决方案更长,但我发现它更容易理解,
首先,计算组成产品的组合数量(内部列表长度的乘法)
其次,循环组合数,组合数是可以解码为产品元素的唯一编码,

例如: [["Apple", "Orange"],[1,2,3], ..] -> 0 = [“苹果”, 1], 1 = [“苹果”, 2], 2 = [“苹果”, 3], 4 = [“橙色”, 1] ...
另一个例如: ["Apple", 3] 等于 [0, 2]

您可以将主列表中的每个元素视为确定下一个数字的编码基数的数字,因为第一个元素是 [" Apple", "Orange"] 其大小为 2,第二位数字为 2
如果我们有:

[["Apple", "Orange"],[1,2,3], ["Shrimp", "Creep", "Sleep"]]
那么数字编码将是一个列表:[1, 2, 2*3]

from functools import reduce

def get_multiplication(digits: list):
    return reduce(lambda x, y: x*y, map(len, digits)) if digits else 1
    
def get_accumalated_multiplication(x: list[list]):
    return [get_multiplication(x[:i]) for i in range(len(x))]
    
def decode_to_product(encoding: int, encoding_digits: list, x: list):
    product = [0] * len(encoding_digits)
    current_product_encoding = encoding
    for i, digit in enumerate(reversed(encoding_digits)):
        bit = 0
        while ((bit + 1) * digit)  <= current_product_encoding:
            bit += 1 
        current_product_encoding -= digit * bit
        product[i] = x[i][bit]
    return tuple(product)
    

def product(x: list[list]):
    encoding_digits = get_accumalated_multiplication(x)
    num_products = reduce(lambda x, y: x*y, encoding_digits)

    for i in range(num_products):
        yield decode_to_product(i, encoding_digits, x)
    

This solution is longer but i find it easier to understand,
First, compute the number of combinations composing the product (multiplication of inner lists lengths)
Second, loop over the number of combinations, the combination number is a unique encoding you can decode into a products element

E.G: [["Apple", "Orange"],[1,2,3], ..] -> 0 = ["Apple", 1], 1 = ["Apple", 2], 2 = ["Apple", 3], 4 = ["Orange", 1] ...
And another e.g: ["Apple", 3] is equal to [0, 2]

you can look at each element in the master list as a digit determining the base of the encoding of the next digit, since the first element is ["Apple", "Orange"] and it's size is 2, the second digit is 2
if we had:

[["Apple", "Orange"],[1,2,3], ["Shrimp", "Creep", "Sleep"]]
then the digits encoding would have been a list: [1, 2, 2*3]

from functools import reduce

def get_multiplication(digits: list):
    return reduce(lambda x, y: x*y, map(len, digits)) if digits else 1
    
def get_accumalated_multiplication(x: list[list]):
    return [get_multiplication(x[:i]) for i in range(len(x))]
    
def decode_to_product(encoding: int, encoding_digits: list, x: list):
    product = [0] * len(encoding_digits)
    current_product_encoding = encoding
    for i, digit in enumerate(reversed(encoding_digits)):
        bit = 0
        while ((bit + 1) * digit)  <= current_product_encoding:
            bit += 1 
        current_product_encoding -= digit * bit
        product[i] = x[i][bit]
    return tuple(product)
    

def product(x: list[list]):
    encoding_digits = get_accumalated_multiplication(x)
    num_products = reduce(lambda x, y: x*y, encoding_digits)

    for i in range(num_products):
        yield decode_to_product(i, encoding_digits, x)
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文