【问题标题】:Get unique elements from each list in list of lists从列表列表中的每个列表中获取唯一元素
【发布时间】:2021-03-03 13:54:59
【问题描述】:

我有一个用整数填充的列表列表。这些整数表示图中的节点,主列表中的列表表示图中的循环。我想按照列表列表的顺序提取一组唯一的节点 - 每个循环中的一个节点。

示例:

我知道,不可能有一个只有两个节点的循环,但这是我想出的最简单的非平凡示例,它应该清楚我在寻找什么。

cycles = [[11, 22, 55], [22, 44], [11, 33], [22, 33]]
result = [11, 44, 33, 22]

另一个可能的结果是[22, 44, 11, 33]

我的解决方案:

到目前为止,我尝试使用itertools.product 来遍历列表列表中的所有元素组合,直到找到与主列表中列表数量相同长度的集合。

def find_random_unique_combination_from_cycles(cycles):
    only_one = dict()
    for ci, cy in enumerate(cycles):
        for element in cy:
            if len([element for cyc in cycles if element in cyc]) == 1:
                only_one.update({element: ci})

    prod_cycles = [cycle for ci, cycle in enumerate(cycles) if ci not in only_one.values()]
    cycle_length = len(prod_cycles)
    result = []
    for combi in it.product(*prod_cycles, repeat=1):
        if len(set(combi)) == cycle_length:
            result = list(combi)
            break
    for element, index in only_one.items():
        result.insert(index, element)
    return result

问题:

此解决方案对于上述示例和类似情况非常有效。但是对于具有更多和更大周期的更大图,它在适当的运行时找不到解决方案(我不得不停止执行包含约 200 个周期的列表)。我还尝试通过删除只有一个唯一元素的所有循环来减小列表列表的大小,但这并没有太大帮助。 有没有更好更快的方法从每个列表列表中查找单个唯一元素?

非常感谢您的帮助!提前致谢!

【问题讨论】:

  • 你能定义Uniqueness吗?为什么11 而不是22?可以是 22 而不是 11?
  • 另一个可能的结果是:[22, 44, 11, 33]
  • 不知道解决方案是否有效
  • list(set([item for l in loops for item in l]))
  • 谢谢!我的例子不够精确!我对其进行了编辑...在这种情况下,您将拥有比循环更多的独特元素!

标签: python itertools


【解决方案1】:

我建议使用递归生成器方法将使用的数字集带到下一个递归级别:

def getPath(cycles,used=None):
    if not cycles: yield [];return  # end of path
    if used is None: used = set()   # track used numbers
    for n in cycles[0]:             # take from first sub list
        if n in used: continue      # skip used, recurse, tagging used n
        yield from ([n]+p for p in getPath(cycles[1:],used|{n}))

输出:

cycles = [[11, 22, 55], [22, 44], [11, 33], [22, 33]]

for path in getPath(cycles): print(path)

[11, 44, 33, 22]
[22, 44, 11, 33]
[55, 22, 11, 33]
[55, 44, 11, 22]
[55, 44, 11, 33]
[55, 44, 33, 22]

如果你想让路径随机出来,你可以在函数的开头添加这一行(并导入随机模块):

if used is None: cycles = [random.sample(c,len(c)) for c in cycles]

这将允许您使用 next() 函数来获得随机路径,而无需遍历所有组合:

randomPath = next(getPath(cycles))

请注意,生成器使用了一种简单的、蛮力的遍历。有优化空间。

例如,可以检查循环中不同的未使用数字是否足以覆盖子列表的数量。这将允许更早地短路遍历分支。

...
if len(set().union(*cycles)-used)<len(cycles): return
for n in cycles[0]:
    ...

或者,可以从每个递归级别的循环列表中删除使用的数字。

def getPath(cycles):
    if not cycles: yield [];return  # end of path
    if not all(cycles): return      # no path when there is an empty sublist
    for n in cycles[0]:             # take from first sub list
        nextCycles = [ [r for r in c if r!=n] for c in cycles[1:] ]
        yield from ( [n] + p for p in getPath(nextCycles) )

【讨论】:

  • 看起来很棒!太感谢了!我试试看
【解决方案2】:
cycles = [[11, 22], [22, 44], [11, 33], [22, 33]]
from itertools import product
a=list(product(*cycles))
[list(i) for i in a if len(i) == len(set(i))]

输出:

[[11, 44, 33, 22], [22, 44, 11, 33]]

如果您对效率感兴趣,可以查看networkx 模块。

import networkx as nx
import matplotlib.pyplot as plt
G = nx.Graph()
cycles = [(11, 22), (22, 44), (11, 33), (22, 33)]
G.add_edges_from(cycles)
nx.draw(G, with_labels=True, font_weight='bold')
G.nodes

G.nodes 返回NodeView((11, 22, 44, 33))

【讨论】:

  • 感谢您的回答!对于具有更大循环的大型图,这无济于事!我收到内存错误
  • 我使用 networkx 从图中提取循环。但我只需要每个周期中的一个唯一节点。
猜你喜欢
  • 2021-11-23
  • 1970-01-01
  • 2018-01-17
  • 1970-01-01
  • 2017-04-12
  • 1970-01-01
  • 2012-04-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多