我有一个很大的元素列表(100多个元素):elements = [a, b, c, d, e, f, g...]
。
我需要构建所有可能的定向循环列表,考虑到序列
[a,b,c,d,e], [b,c,d,e,a], [c,d,e,a,b], [d,e,a,b,c], [e,a,b,c,d]
被认为是相同的,因为它们是同一定向循环的不同表示。只有起点不同。
此外,由于方向很重要,[a,b,c,d,e]
和[e,d,c,b,a]
不同。
我正在寻找所有长度的所有定向循环,从2到len(elements)
。使用内置permutations
,combinations
等优化的最佳pythonic方法是什么?
答案 0 :(得分:6)
也许我错过了什么,但这对我来说似乎很简单:
def gen_oriented_cycles(xs):
from itertools import combinations, permutations
for length in range(2, len(xs) + 1):
for pieces in combinations(xs, length):
first = pieces[0], # 1-tuple
for rest in permutations(pieces[1:]):
yield first + rest
然后,例如,
for c in gen_oriented_cycles('abcd'):
print c
显示:
('a', 'b')
('a', 'c')
('a', 'd')
('b', 'c')
('b', 'd')
('c', 'd')
('a', 'b', 'c')
('a', 'c', 'b')
('a', 'b', 'd')
('a', 'd', 'b')
('a', 'c', 'd')
('a', 'd', 'c')
('b', 'c', 'd')
('b', 'd', 'c')
('a', 'b', 'c', 'd')
('a', 'b', 'd', 'c')
('a', 'c', 'b', 'd')
('a', 'c', 'd', 'b')
('a', 'd', 'b', 'c')
('a', 'd', 'c', 'b')
这是否缺少了您正在寻找的一些基本属性?
修改强>
我认为可能会遗漏这部分标准:
此外,由于方向很重要,[a,b,c,d,e]和[e,d,c,b,a]不同。
但第二个想法我觉得它符合这个要求,因为[e,d,c,b,a]
与[a,e,d,c,b]
相同。
答案 1 :(得分:0)
有没有什么好的理由在记忆中使用规范表示?这将是巨大的,并且可能有任何用于此的用例可能有更好的方法来处理它。
对于您的源材料,您会使用X元素的任意组合,甚至不一定是同类的元素? (即你有(a,e,g,x,f)
等)。然后,我会这样做一个嵌套循环。外部将按长度选择,并选择要使用的整个列表的子集。内部组件将构建子集的组合,然后丢弃匹配项。无论你怎么做,它都会很慢,但是我会使用一个带有冷冻集的字典作为关键字(项目,用于不变性和快速查找),并且这些项目是已经检测到的周期列表。无论你怎么做,它都会很慢/长时间运行,但这是一种方式。
首先,您需要一种方法来确定两个元组(或列表)是否代表相同的循环。你可以这样做:
def match_cycle(test_cycle, ref_cycle):
try:
refi = ref_cycle.index(test_cycle[0])
partlen = len(ref_cycle) - refi
return not (any(test_cycle[i] - ref_cycle[i+refi] for i in range(partlen)) or
any(test_cycle[i+partlen] - ref_cycle[i] for i in range(refi)))
except:
return False
然后,其余的。
def all_cycles(elements):
for tuple_length in range(2, len(elements)):
testdict = defaultdict(list)
for outer in combinations(elements, tuple_length):
for inner in permutations(outer):
testset = frozenset(inner)
if not any(match_cycle(inner, x) for x in testdict[testset]):
testdict[testset].append(inner)
yield inner
这为长度为5的elements
生成了60个项目,这似乎是正确的,从检查看起来还不错。请注意,这将是指数虽然....长度(5)花了1.34毫秒/循环。长度(6)花了22.1毫秒。长度(7)花费703毫秒,长度(8)花费33.5秒。长度(100)可能会在你退休之前完成,但我不会赌它。
可能有更好的方法,可能是,但一般来说,100个元素中的子集数量非常大,即使减少了一些周期。因此,这可能不是解决您要解决的任何问题的正确方法。
答案 2 :(得分:0)
这可能有效:
import itertools
import collections
class Cycle(object):
def __init__(self, cycle):
self.all_possible = self.get_all_possible(cycle)
self.canonical = self.get_canonical(self.all_possible)
def __eq__(self, other):
return self.canonical == other.canonical
def __hash__(self):
return hash(self.canonical)
def get_all_possible(self, cycle):
output = []
cycle = collections.deque(cycle)
for i in xrange(len(cycle)):
cycle.rotate(1)
output.append(list(cycle))
return output
def get_canonical(self, cycles):
return min(map(tuple, cycles), key=lambda item: hash(item))
def __repr__(self):
return 'Cycle({0})'.format(self.canonical)
def list_cycles(elements):
output = set()
for i in xrange(2, len(elements) + 1):
output.update(set(map(Cycle, itertools.permutations(elements, i))))
return list(output)
def display(seq):
for cycle in seq:
print cycle.canonical
print '\n'.join(' ' + str(item) for item in cycle.all_possible)
def main():
elements = 'abcdefghijkl'
final = list_cycles(elements)
display(final)
if __name__ == '__main__':
main()
它创建一个表示任何给定循环的类,它将进行散列并检查与循环的规范表示的相等性。这样可以将Cycle对象放在一个集合中,该集合将自动过滤掉任何重复项。不幸的是,它不会高效,因为它首先产生每一个可能的排列。
答案 3 :(得分:0)
对于长度为2到len(元素)的循环,这应该给你正确的答案。可能不是最快的方法。我使用了qarma的旋转提示,始终以最小的元素开始。
from itertools import permutations
def rotate_min(l):
'''Rotates the list so that the smallest element comes first '''
minIndex = l.index(min(l))
rotatedTuple = l[minIndex:] + l[:minIndex]
return rotatedTuple
def getCycles(elements):
elementIndicies = tuple(range(len(elements))) #tupple is hashable so it works with set
cyclesIndices = set()
cycles = []
for length in range(2, len(elements)+1):
allPermutation = permutations(elementIndicies, length)
for perm in allPermutation:
rotated_perm = rotate_min(perm)
if rotated_perm not in cyclesIndices:
#If the cycle of indices is not in the set, add it.
cyclesIndices.add(rotated_perm)
#convert indicies to the respective elements and append
cycles.append([elements[i] for i in rotated_perm])
return cycles