考虑所有长度n
的列表,其中值是range 0 to n-1
的整数。我想尽可能快地迭代每个可能只有一个副本的列表。如果是n = 2
,那么可能的列表是:
00
11
如果他们是n = 3
:
001
002
110
112
220
221
100
200
011
211
022
122
010
020
101
121
202
212
此类列表的总数为n! * (n choose 2)
,因此我希望尽可能避免将它们全部存储在内存中。
对于每个列表,我想调用一个计算列表功能的函数 有什么好办法呢?
基准在我的计算机上,我得到以下基准测试结果
timeit all(one_duplicate(6))
100 loops, best of 3: 6.87 ms per loops
timeit all(one_duplicate(7))
10 loops, best of 3: 59 ms per loop
timeit all(one_duplicate(8))
1 loops, best of 3: 690 ms per loop
timeit all(one_duplicate(9))
1 loops, best of 3: 7.58 s per loop
和
timeit all(duplicates(range(6)))
10 loops, best of 3: 22.8 ms per loop
timeit all(duplicates(range(7)))
1 loops, best of 3: 416 ms per loop
timeit all(duplicates(range(8)))
1 loops, best of 3: 9.78 s per loop
和
timeit all(all_doublet_tuples(6))
100 loops, best of 3: 6.36 ms per loop
timeit all(all_doublet_tuples(7))
10 loops, best of 3: 60 ms per loop
timeit all(all_doublet_tuples(8))
1 loops, best of 3: 542 ms per loop
timeit all(all_doublet_tuples(9))
1 loops, best of 3: 7.27 s per loop
我现在声明all_doublet_tuples
和one_duplicate
首先相等(允许测试中出现噪音)。
答案 0 :(得分:5)
以这种方式考虑任何所需的输出元组:它是通过复制第一个(比如说左起)双重元素并将其插入第二个双合透镜的位置而得到的。 / p>
因此,我的解决方案基本上是两个步骤:
n-1
range(n)
个排列
import itertools as it
# add_doublet accomplishes step 2
def add_doublet(t):
for i in range(len(t)):
for j in range(i+1, len(t)+1):
yield t[:j] + (t[i],) + t[j:]
def all_doublet_tuples(n):
for unique_tuple in it.permutations(range(n), n-1):
for doublet_tuple in add_doublet(unique_tuple):
yield doublet_tuple
from pprint import pprint
n = 3
pprint(list(all_doublet_tuples(n)))
我不打印这里的输出因为它的长度......并且n = 3的情况很无聊。
答案 1 :(得分:3)
import itertools
def one_duplicate(n):
nrange = list(range(n))
for x in nrange:
without_x = nrange[:x] + nrange[x+1:]
for i, j in itertools.combinations(nrange, 2):
for others in map(list, itertools.permutations(without_x, n-2)):
others.insert(i, x)
others.insert(j, x)
yield others
>>> list(one_duplicate(2))
[[0, 0], [1, 1]]
>>> list(one_duplicate(3))
[[0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 2, 0], [1, 0, 0], [2, 0, 0], [1, 1, 0], [1, 1, 2], [1, 0, 1], [1, 2, 1], [0, 1, 1], [2, 1, 1], [2, 2, 0], [2, 2, 1], [2, 0, 2], [2, 1, 2], [0, 2, 2], [1, 2, 2]]
>>> list(one_duplicate(4))
[[0, 0, 1, 2], [0, 0, 1, 3], [0, 0, 2, 1], [0, 0, 2, 3], [0, 0, 3, 1], [0, 0, 3, 2], [0, 1, 0, 2], [0, 1, 0, 3], [0, 2, 0, 1], [0, 2, 0, 3], [0, 3, 0, 1], [0, 3, 0, 2], [0, 1, 2, 0], [0, 1, 3, 0], [0, 2, 1, 0], [0, 2, 3, 0], [0, 3, 1, 0], [0, 3, 2, 0], [1, 0, 0, 2], [1, 0, 0, 3], [2, 0, 0, 1], [2, 0, 0, 3], [3, 0, 0, 1], [3, 0, 0, 2], [1, 0, 2, 0], [1, 0, 3, 0], [2, 0, 1, 0], [2, 0, 3, 0], [3, 0, 1, 0], [3, 0, 2, 0], [1, 2, 0, 0], [1, 3, 0, 0], [2, 1, 0, 0], [2, 3, 0, 0], [3, 1, 0, 0], [3, 2, 0, 0], [1, 1, 0, 2], [1, 1, 0, 3], [1, 1, 2, 0], [1, 1, 2, 3], [1, 1, 3, 0], [1, 1, 3, 2], [1, 0, 1, 2], [1, 0, 1, 3], [1, 2, 1, 0], [1, 2, 1, 3], [1, 3, 1, 0], [1, 3, 1, 2], [1, 0, 2, 1], [1, 0, 3, 1], [1, 2, 0, 1], [1, 2, 3, 1], [1, 3, 0, 1], [1, 3, 2, 1], [0, 1, 1, 2], [0, 1, 1, 3], [2, 1, 1, 0], [2, 1, 1, 3], [3, 1, 1, 0], [3, 1, 1, 2], [0, 1, 2, 1], [0, 1, 3, 1], [2, 1, 0, 1], [2, 1, 3, 1], [3, 1, 0, 1], [3, 1, 2, 1], [0, 2, 1, 1], [0, 3, 1, 1], [2, 0, 1, 1], [2, 3, 1, 1], [3, 0, 1, 1], [3, 2, 1, 1], [2, 2, 0, 1], [2, 2, 0, 3], [2, 2, 1, 0], [2, 2, 1, 3], [2, 2, 3, 0], [2, 2, 3, 1], [2, 0, 2, 1], [2, 0, 2, 3], [2, 1, 2, 0], [2, 1, 2, 3], [2, 3, 2, 0], [2, 3, 2, 1], [2, 0, 1, 2], [2, 0, 3, 2], [2, 1, 0, 2], [2, 1, 3, 2], [2, 3, 0, 2], [2, 3, 1, 2], [0, 2, 2, 1], [0, 2, 2, 3], [1, 2, 2, 0], [1, 2, 2, 3], [3, 2, 2, 0], [3, 2, 2, 1], [0, 2, 1, 2], [0, 2, 3, 2], [1, 2, 0, 2], [1, 2, 3, 2], [3, 2, 0, 2], [3, 2, 1, 2], [0, 1, 2, 2], [0, 3, 2, 2], [1, 0, 2, 2], [1, 3, 2, 2], [3, 0, 2, 2], [3, 1, 2, 2], [3, 3, 0, 1], [3, 3, 0, 2], [3, 3, 1, 0], [3, 3, 1, 2], [3, 3, 2, 0], [3, 3, 2, 1], [3, 0, 3, 1], [3, 0, 3, 2], [3, 1, 3, 0], [3, 1, 3, 2], [3, 2, 3, 0], [3, 2, 3, 1], [3, 0, 1, 3], [3, 0, 2, 3], [3, 1, 0, 3], [3, 1, 2, 3], [3, 2, 0, 3], [3, 2, 1, 3], [0, 3, 3, 1], [0, 3, 3, 2], [1, 3, 3, 0], [1, 3, 3, 2], [2, 3, 3, 0], [2, 3, 3, 1], [0, 3, 1, 3], [0, 3, 2, 3], [1, 3, 0, 3], [1, 3, 2, 3], [2, 3, 0, 3], [2, 3, 1, 3], [0, 1, 3, 3], [0, 2, 3, 3], [1, 0, 3, 3], [1, 2, 3, 3], [2, 0, 3, 3], [2, 1, 3, 3]]
以下是算法的说明:
itertools.combinations(nrange, 2)
x
,获取除n-2
x
长度的所有排列
x
并生成结果。 我的答案和Peter Stahl's之间的时间比较:
# Just showing they both get the same result
In [23]: set(peter(range(6))) == set(tuple(x) for x in fj(6))
Out[23]: True
In [24]: %timeit list(peter(range(6)))
10 loops, best of 3: 27.3 ms per loop
In [25]: %timeit list(fj(6))
100 loops, best of 3: 8.32 ms per loop
In [26]: %timeit list(peter(range(7)))
1 loops, best of 3: 506 ms per loop
In [27]: %timeit list(fj(7))
10 loops, best of 3: 81 ms per loop
答案 2 :(得分:2)
import itertools
n=3
for i in range(n-1):
for j in range(n-i-1):
for perm in map(list, itertools.permutations(range(n), n-1)):
perm.insert(i, perm[i+j])
print perm
这里的关键概念是从N长度集合生成所有N-1个长度排列,然后在所有重复组合(i和i + j)上运行它
在N = 3的情况下,排列是:
(0,1)(0,2)(1,0)(1,2)(2,0)(2,1)
并且重复的元素可以位于由X标记的位置:
XX_,X_X,_XX
您的结果是这些集合的“倍增”:
XX_ X_X _XX
(0, 1) 001 010 011
(0, 2) 002 020 022
(1, 0) 110 101 100
(1, 2) 112 121 122
(2, 0) 220 202 200
(2, 1) 221 212 211
一切都是即时计算的。我猜内存消耗是O(N)。
答案 3 :(得分:1)
您的问题是如何在不在内存中创建实际list
以及如何在每个内容上运行函数的情况下执行此操作,因此我将首先回答该部分,然后针对您的具体问题。< / p>
可能列表的总数是n *(n-1)*(n选择2)所以我希望尽可能避免将它们全部存储在内存中。
您只需要创建一个生成器函数,yield
逐个回答,而不是将每个答案附加到list
的常规函数,然后返回list
。
对于每个列表,我想调用一个计算列表功能的函数。 有什么好办法呢?
您可以调用生成器函数并将结果用作迭代器。例如:
for element in my_generator_function(n):
my_function(element)
如果你想在每个元素上调用my_function
的结果,你可以构建另一个生成器函数:
def apply_to(my_function, my_iterator):
for element in my_iterator):
yield my_function(element)
但是,已经有一个内置函数可以完成,在Python 3中称为map
,在Python 2中称为itertools.imap
。
或者,更简单地说,您可以使用生成器表达式:
results = (my_function(element) for element in my_generator_function(n))
有关详情,请参阅官方Python教程中的Iterators,Generators和Generator Expressions部分。
现在,您可以像这样解决问题:
对于n = 2,这意味着(0,0)的所有排列以及来自(1)的0个数字,以及(1,1)的所有排列以及来自(0)的0个数字。对于n = 3,你得到了(0,0)的所有排列和1个数字(1,2),以及(1,1)的所有排列和1个数字(0,2),和(2,2)的所有排列以及1个数字(0,1)。等等。
因此,使用itertools
:
def one_duplicate_element(n):
for i in range(n):
rest = (j for j in range(n) if j != i)
for combination in itertools.combinations(rest, n-2):
yield from itertools.permutations(combination + (i, i))
这使用Python 3.3中的yield from
语句,这意味着“从这个迭代器中获取每个元素”。如果您使用的是3.2或更早版本,则需要使用循环显式执行此操作:
def one_duplicate_element(n):
for i in range(n):
rest = (j for j in range(n) if j != i)
for combination in itertools.combinations(rest, n-2):
for permutation in itertools.permutations(combination + (i, i)):
yield permutation
但是你会注意到,虽然这完全符合你的要求,但它不会生成与样本输出相同的列表。即使忽略排序(我认为这与你无关),也有重复。为什么呢?
那么(0, 0)
集的排列是什么?这是一个棘手的问题; (0, 0)
不能是一个集合,因为它中有重复项。那么,无论(0, 0)
是什么,它的排列是什么?好吧,按照有序元组排列的通常定义,((0, 0), (0, 0))
是什么。而这正是itertools.permutations
给你的。
您可以查看permutations
的工作原理并自行编写permutations_without_duplicates
函数,或者您可以使用文档的Recipes部分中的unique_everseen
函数,或者您可以对结果进行排序并使用unique_justseen
,或者......好吧,我们选择一个:
def one_duplicate_element_no_duplicates(n):
yield from unique_everseen(one_duplicate_element(n))
答案 4 :(得分:1)
from itertools import product
def duplicates(r):
r_len = len(r)
dup_len = r_len - 1
for tup in product(r, repeat=r_len):
if len(set(tup)) == dup_len:
yield tup
>>> list(duplicates(range(2)))
[(0, 0), (1, 1)]
>>> list(duplicates(range(3)))
[(0, 0, 1),
(0, 0, 2),
(0, 1, 0),
(0, 1, 1),
(0, 2, 0),
(0, 2, 2),
(1, 0, 0),
(1, 0, 1),
(1, 1, 0),
(1, 1, 2),
(1, 2, 1),
(1, 2, 2),
(2, 0, 0),
(2, 0, 2),
(2, 1, 1),
(2, 1, 2),
(2, 2, 0),
(2, 2, 1)]
我机器上的一些基准测试:
%timeit all(duplicates(range(5)))
100 loops, best of 3: 2.5 ms per loop
%timeit all(duplicates(range(6)))
10 loops, best of 3: 38.6 ms per loop
%timeit all(duplicates(range(7)))
1 loops, best of 3: 766 ms per loop
%timeit all(duplicates(range(8)))
1 loops, best of 3: 18.8 s per loop