Python - itertools.product不使用元素多次

时间:2018-03-02 02:43:34

标签: python python-3.x

我有一个列表列表,我想在不使用任何元素的情况下复制itertools.product()的效果。

>>> list = [['A', 'B'], ['C', 'D'], ['A', 'B']]
>>> [''.join(e) for e in itertools.product(*list)]
['ACA', 'ACB', 'ADA', 'ADB', 'BCA', 'BCB', 'BDA', 'BDB']
>>> # Desired output: ['ACB', 'ADB', 'BCA', 'BDA']

我需要使用它的列表太大而无法计算itertools.product并删除不需要的元素。 (来自itertools.product的250亿个排列,而我想要的输出只有~500,000)。优选地,答案是可迭代的。

编辑:我知道"产品"对于我需要的是错误的术语,但我很难找到我正在寻找的词。

Edit2:这是我希望执行此操作的列表:

[['A', 'H', 'L'], ['X', 'B', 'I'], ['Q', 'C', 'V'], ['D', 'N'], ['E', 'F'], ['E', 'F'], ['G'], ['A', 'H', 'L'], ['X', 'B', 'I'], ['W', 'U', 'J', 'K', 'M'], ['W', 'U', 'J', 'K', 'M'], ['A', 'H', 'L'], ['W', 'U', 'J', 'K', 'M'], ['D', 'N'], ['P', 'O', 'T'], ['P', 'O', 'T'], ['Q', 'C', 'V'], ['R'], ['S'], ['P', 'O', 'T'], ['W', 'U', 'J', 'K', 'M'], ['Q', 'C', 'V'], ['W', 'U', 'J', 'K', 'M'], ['X', 'B', 'I']]

3 个答案:

答案 0 :(得分:2)

import React from 'react';
import './App.css';

var createReactClass = require('create-react-class');

var GameBoard = createReactClass({
    getInitialState() {
        return {
            gameState: 0,
            heroLoc: 0,
            enemyLoc: 0,
            enemyType : 0,
        }
    },

    gameStart() {
        this.setState({
            gameState: 1
        })
        this.createEnemy();
    },

    changeEnemyType(){
        var type = Math.floor(Math.random()*3);
        var loc = Math.round(Math.random());
        this.setState({enemyLoc : loc});
        this.setState({enemyType : type});
    },

    createEnemy(){
        setInterval(this.changeEnemyType, 1000);
    },

    render() {
        var state = this.state
        var enemyCls = state.gameState? ("enemy enemy"+ state.enemyType  + " loc" + state.enemyLoc):"enemy";
        return (
            <div className = "board" >
                <div className="road">
                    <div className={enemyCls} id="enemy"></div>
                </div>
                <span className = { state.gameState? "start hide":"start" } onClick = { this.gameStart }> Start </span>
            </div>
        )
    }
});

较长的输入需要大约五秒钟才能在我的机器上运行。我使用test1 = [['A', 'B'], ['C', 'D'], ['A', 'B']] test2 = [['A', 'H', 'L'], ['X', 'B', 'I'], ['Q', 'C', 'V'], ['D', 'N'], ['E', 'F'], ['E', 'F'], ['G'], ['A', 'H', 'L'], ['X', 'B', 'I'], ['W', 'U', 'J', 'K', 'M'], ['W', 'U', 'J', 'K', 'M'], ['A', 'H', 'L'], ['W', 'U', 'J', 'K', 'M'], ['D', 'N'], ['P', 'O', 'T'], ['P', 'O', 'T'], ['Q', 'C', 'V'], ['R'], ['S'], ['P', 'O', 'T'], ['W', 'U', 'J', 'K', 'M'], ['Q', 'C', 'V'], ['W', 'U', 'J', 'K', 'M'], ['X', 'B', 'I']] def prod(curr, *others): if not others: for x in curr: yield {x} # (x,) for tuples else: for o in prod(*others): for c in curr: if c not in o: yield {c, *o} # (c, *o) for tuples print([''.join(x) for x in prod(*test1)]) # ['ABC', 'ABD', 'ABC', 'ABD'] print(sum(1 for x in prod(*test2))) # 622080 来传递值,因为它们在成员资格检查方面比元组或列表更有效。如果有必要,你可以使用元组,它会慢一些。

要考虑的一些问题:订单有关系吗?当我们无法使用当前列表中的项目时(因为它们都已被使用过),您希望发生什么?

答案 1 :(得分:2)

一个简单的基于堆栈的实现:

def product1(l): return product1_(l,0,[])
def product1_(l,i,buf):
  if i==len(l): yield buf
  else:
    for x in l[i]:
      if x not in buf:
        buf.append(x)
        yield from product1_(l,i+1,buf)
        buf.pop()

这比Patrick Haugh的回答慢一点(我的测试用例得到了18秒),但它以可预测的顺序给出了结果。

请注意,您必须在生成“他们”时处理这些值,因为它们都是相同的列表buf;您可以写yield tuple(buf)yield "".join(buf)来生成单独的“熟”值(成本不到一秒)。

如果值是字母,您可以使用“位掩码”列表来实现冲突测试,这会将时间缩短到大约13秒(但使用set的速度也一样快)。其他可能的优化包括首先处理具有较少合格元素的列表,以减少回溯;这可以将这种情况降低到11秒。

答案 2 :(得分:1)

您的具体案件有一个有趣的属性。如果我们将它安排在一个计数器中,我们会看到每个列表的出现次数与其条目相同:

Counter({('A', 'H', 'L'): 3,
         ('D', 'N'): 2,
         ('E', 'F'): 2,
         ('G',): 1,
         ('P', 'O', 'T'): 3,
         ('Q', 'C', 'V'): 3,
         ('R',): 1,
         ('S',): 1,
         ('W', 'U', 'J', 'K', 'M'): 5,
         ('X', 'B', 'I'): 3})

换句话说,忽略顺序,您想要的序列是列表排列的笛卡尔积。假设您的列表名为l。然后我们可以汇总一份列表,列出子列表的所有排列并获取它们的产品:

c = set(tuple(i) for i in l)
permutations = [list(itertools.permutations(i)) for i in c]
permutation_products = itertools.products(*permutations)

permutation_products的元素看起来像:

(('W', 'U', 'J', 'K', 'M'),
 ('E', 'F'),
 ('X', 'B', 'I'),
 ('Q', 'C', 'V'),
 ('P', 'O', 'T'),
 ('D', 'N'),
 ('G',),
 ('S',),
 ('R',),
 ('A', 'L', 'H'))

我们必须让它恢复正确的顺序。假设我们的排列被称为perm。对于列表的每个子列表,我们必须找到perm的正确元素,然后在排列中取下一个字母。我们可以通过制作字典来实现这一目标:

perm_dict = {frozenset(p): list(p) for p in perm}

然后,为了构建单个排列,我们有:

s = "".join([perm_dict[frozenset(i)].pop() for i in l])

我们可以将所有这些组合成一个生成器:

def permute_list(l):
    c = set(tuple(i) for i in l)
    permutations = [list(itertools.permutations(i)) for i in c]
    permutation_products = itertools.product(*permutations)
    for perm in permutation_products:
        perm_dict = {frozenset(p): list(p) for p in perm}
        yield "".join([perm_dict[frozenset(i)].pop() for i in l])