我希望能够获取一系列数字并返回包含三元组的列表而不重复。 x的每个元素应该在三元组的每个位置出现一次。目标是获得如下内容:
get_combinations_without_duplicates(3) = [(0, 1, 2), (1, 2, 0), (2, 0, 1)]
对于范围(3),这只是一个列表旋转,但对于更高的范围,有更多可能的组合。我希望能够随机生成满足这些约束条件的三元组列表。
假设我们首先为n = 4的情况指定每个三元组的第一个元素:
[(0,),(1,),(2,),(3,)]
第一个三元组的第二个元素可以是除0之外的任何元素。一旦选择了其中一个元素,那么这将限制下一个三元组的选项,依此类推。目标是使用一个函数来获取数字并以这种方式创建三元组,但并不总是创建相同的三元组。也就是说,最终结果可能是轮换:
[(0, 1, 2), (1, 2, 3), (2, 3, 0), (3, 0, 1),]
或
[(0, 2, 3), (1, 3, 0), (2, 0, 1), (3, 1, 2)]
以下是此功能的实现:
def get_combinations_without_duplicates(n):
output = []
second = range(n)
third = range(n)
for i in range(n):
triple = [i]
#Get the second value of the triple, but make sure that it isn't a
#duplicate of the first value
#in the triple or any value that has appeared in the second position of any triple
choices_for_second = [number for number in second if number not in triple]
#Randomly select a number from the allowed possibilities
n_second = random.choice(choices_for_second)
#Append it to the triple
triple.append(n_second)
#Remove that value from second so that it won't be chosen for other triples
second = [number for number in second if number != n_second]
#Do the same for the third value
choices_for_third = [number for number in third if number not in triple]
n_third = random.choice(choices_for_third)
triple.append(n_third)
third = [number for number in third if number != n_third]
output.append(tuple(triple))
return output
如下所述,此过程有时会随机选择不起作用的组合。如果您执行以下操作,则可以处理:
def keep_trying(n):
try:
return get_combinations_without_duplicates(n)
except IndexError:
return keep_trying(n)
但是,我想知道是否有更好的方法来做到这一点。
答案 0 :(得分:5)
让我们再试一次。
一些观察结果。
根据这些规范,我们可以提出一种程序方法;
[0, 1, 2, 3]
,随机追加并删除元素中尚未包含的数字。 [01, 13, 20, 32]
[012, 130, 203, 321]
但是,这不起作用。对于某些迭代,它将自己回到角落而不能生成数字。例如,[01, 13, 20, 32].. appending [3, 0, 1... crap, I'm stuck.
解决这个问题的唯一方法是在整个行上进行 true 改组,然后重新洗牌,直到一个适合。这可能需要相当长的时间,并且随着设置变长而变得更加痛苦。
所以,从程序上讲:
解决方案1:随机生成
通过此过程,虽然计算机可以非常快速验证解决方案,但它无法非常快速地生成解决方案。但是,它只是产生真正随机答案的两种方法之一。
因此,最快保证的方法是使用验证程序,而不是生成程序。首先,产生所有可能的排列。
from itertools import permutations
n = 4
candidates = [i for i in permutations(xrange(n),3)]
然后
解决方案2:随机验证
下一个三联体的解决方案在数学上保证在解决方案集中,因此如果你让它耗尽自己,应该出现一个随机解决方案。这种方法的问题在于,并不能保证每个可能的结果都具有相等的概率。
解决方案3:迭代验证
对于等概率结果,去除随机化,并生成每个可能的3元组合,n列表长 - 并验证每个解决方案候选者。
在候选解决方案列表上编写一个验证的函数,以生成每个解决方案,然后从该列表中随机弹出解决方案。
from itertools import combinations
results = [verify(i) for i in combinations(candidates, n)]
# this is 10626 calls to verify for n=4, 5 million for n=5
# this is not an acceptable solution.
解决方案1或3都不是非常快,O(n ** 2),但是根据您的标准,如果您想要一个真正随机的解决方案,这可能会达到最快速度。解决方案2将保证是这三者中最快的,通常大大超过1或3,解决方案3具有最稳定的结果。您选择的这些方法中的哪一种取决于您想要对输出执行的操作。
之后:
最终,代码的速度将取决于您希望代码的随机。吐出满足您要求的元组系列的第一个(也是唯一的第一个)实例的算法可以快速运行,因为它只是按顺序攻击排列,一次,它将在O(n)时间内运行。但是,它不会随意做任何事......
另外,这里有一些验证(i)的快速代码。它基于观察到两个元组在同一索引中可能没有相同的数字。
def verify(t):
""" Verifies that a set of tuples satisfies the combinations without duplicates condition. """
zipt = zip(*t)
return all([len(i) == len(set(i)) for i in zipt])
n = 4完整解决方案集
((0, 1, 2), (1, 0, 3), (2, 3, 0), (3, 2, 1))
((0, 1, 2), (1, 0, 3), (2, 3, 1), (3, 2, 0))
((0, 1, 2), (1, 2, 3), (2, 3, 0), (3, 0, 1))
((0, 1, 2), (1, 3, 0), (2, 0, 3), (3, 2, 1))
((0, 1, 3), (1, 0, 2), (2, 3, 0), (3, 2, 1))
((0, 1, 3), (1, 0, 2), (2, 3, 1), (3, 2, 0))
((0, 1, 3), (1, 2, 0), (2, 3, 1), (3, 0, 2))
((0, 1, 3), (1, 3, 2), (2, 0, 1), (3, 2, 0))
((0, 2, 1), (1, 0, 3), (2, 3, 0), (3, 1, 2))
((0, 2, 1), (1, 3, 0), (2, 0, 3), (3, 1, 2))
((0, 2, 1), (1, 3, 0), (2, 1, 3), (3, 0, 2))
((0, 2, 1), (1, 3, 2), (2, 0, 3), (3, 1, 0))
((0, 2, 3), (1, 0, 2), (2, 3, 1), (3, 1, 0))
((0, 2, 3), (1, 3, 0), (2, 0, 1), (3, 1, 2))
((0, 2, 3), (1, 3, 2), (2, 0, 1), (3, 1, 0))
((0, 2, 3), (1, 3, 2), (2, 1, 0), (3, 0, 1))
((0, 3, 1), (1, 0, 2), (2, 1, 3), (3, 2, 0))
((0, 3, 1), (1, 2, 0), (2, 0, 3), (3, 1, 2))
((0, 3, 1), (1, 2, 0), (2, 1, 3), (3, 0, 2))
((0, 3, 1), (1, 2, 3), (2, 1, 0), (3, 0, 2))
((0, 3, 2), (1, 0, 3), (2, 1, 0), (3, 2, 1))
((0, 3, 2), (1, 2, 0), (2, 1, 3), (3, 0, 1))
((0, 3, 2), (1, 2, 3), (2, 0, 1), (3, 1, 0))
((0, 3, 2), (1, 2, 3), (2, 1, 0), (3, 0, 1))
n = 5有552个独特的解决方案。这是第20个。
((0, 1, 2), (1, 0, 3), (2, 3, 4), (3, 4, 0), (4, 2, 1))
((0, 1, 2), (1, 0, 3), (2, 3, 4), (3, 4, 1), (4, 2, 0))
((0, 1, 2), (1, 0, 3), (2, 4, 0), (3, 2, 4), (4, 3, 1))
((0, 1, 2), (1, 0, 3), (2, 4, 1), (3, 2, 4), (4, 3, 0))
((0, 1, 2), (1, 0, 4), (2, 3, 0), (3, 4, 1), (4, 2, 3))
((0, 1, 2), (1, 0, 4), (2, 3, 1), (3, 4, 0), (4, 2, 3))
((0, 1, 2), (1, 0, 4), (2, 4, 3), (3, 2, 0), (4, 3, 1))
((0, 1, 2), (1, 0, 4), (2, 4, 3), (3, 2, 1), (4, 3, 0))
((0, 1, 2), (1, 2, 0), (2, 3, 4), (3, 4, 1), (4, 0, 3))
((0, 1, 2), (1, 2, 0), (2, 4, 3), (3, 0, 4), (4, 3, 1))
((0, 1, 2), (1, 2, 3), (2, 0, 4), (3, 4, 0), (4, 3, 1))
((0, 1, 2), (1, 2, 3), (2, 0, 4), (3, 4, 1), (4, 3, 0))
((0, 1, 2), (1, 2, 3), (2, 3, 4), (3, 4, 0), (4, 0, 1))
((0, 1, 2), (1, 2, 3), (2, 4, 0), (3, 0, 4), (4, 3, 1))
((0, 1, 2), (1, 2, 3), (2, 4, 1), (3, 0, 4), (4, 3, 0))
((0, 1, 2), (1, 2, 4), (2, 0, 3), (3, 4, 0), (4, 3, 1))
((0, 1, 2), (1, 2, 4), (2, 0, 3), (3, 4, 1), (4, 3, 0))
((0, 1, 2), (1, 2, 4), (2, 3, 0), (3, 4, 1), (4, 0, 3))
((0, 1, 2), (1, 2, 4), (2, 3, 1), (3, 4, 0), (4, 0, 3))
((0, 1, 2), (1, 2, 4), (2, 4, 3), (3, 0, 1), (4, 3, 0))
因此,您可以生成这样的解决方案,但这需要时间。如果你打算利用这个,我会缓存按原样生成的解决方案,然后当你需要它们时随机拉出它们。顺便说一句,n = 5花了不到一分钟才完成,蛮力。由于解是O(n ** 2),我预计n = 6需要一个多小时,n = 7一天。回归真正的随机解决方案的唯一方法就是这样做。
已编辑:无均衡分布的随机解决方案:
以下是我在尝试解决此问题时编写的代码,解决方案2 的实现。我想我会发布它,因为它是一个部分的,不相等的分发解决方案,并且生成所有可能的解决方案,保证,给予足够的时间。
def seeder(n):
""" Randomly generates the first element in a solution. """
seed = [0]
a = range(1, n)
for i in range(1, 3):
seed.append(a.pop(random.randint(0,len(a)-1)))
return [seed]
def extend_seed(seed, n):
""" Semi-Randomly generates the next element in a solution. """
next_seed = [seed[-1][0] + 1]
candidates = range(0, n)
for i in range(1, 3):
c = candidates[:]
for s in next_seed:
c.remove(s)
for s in seed:
try:
c.remove(s[i])
except ValueError:
pass
next_seed.append(c.pop(random.randint(0,len(c)-1)))
seed.append(next_seed)
return seed
def combo(n):
""" Extends seed until exhausted.
Some random generations return results shorter than n. """
seed = seeder(n)
while True:
try:
extend_seed(seed, n)
except (ValueError, IndexError):
return seed
def combos(n):
""" Ensures that the final return is of length n. """
while True:
result = combo(n)
if len(result) == n:
return result
答案 1 :(得分:2)
你基本上想要一个Latin square焦虑的数字网格,其中每一行和每一列只包含一个数字,除了你只关心每一行中的前三个数字(一个拉丁矩形)。
<强>更新强>
我已经删除了我无效的代码示例,因为生成具有相同概率的随机拉丁方是非平凡的,如in a question on math.stackexchange.com所述。
SAGE project实现了该问题中提到的算法,因此您可以查看代码以获取灵感。
或者,如果您真的想了解详细信息,请查看this paper了解生成随机拉丁矩形的具体情况。
答案 2 :(得分:1)
实际上,itertools已经为你解决了这个问题。
import itertools
allp = [x for x in itertools.permutations(range(3))]
print allp
mylist = ['A','B','C']
allp2 = [x for x in itertools.permutations(mylist)]
print allp2
输出
[(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]
[('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]
答案 3 :(得分:0)
对您的问题只是一个不同的观点。看看这是否适合你
>>> from itertools import chain,combinations
>>> def get_combinations_without_duplicates(iterable):
return (tuple(chain(*(set(iterable) - set(e) , e))) for e in combinations(iterable,2))
>>> list(get_combinations_without_duplicates(range(3)))
[(2, 0, 1), (1, 0, 2), (0, 1, 2)]
答案 4 :(得分:0)
简单的列表轮换为所有n&gt; = 3:
提供了正确的解决方案考虑n = 5的旋转解:
[
(0, 1, 2),
(1, 2, 3),
(2, 3, 4),
(3, 4, 0),
(4, 0, 1)
]
每个位置只出现一次,每个位置都存在所有数字。
通常,len(get_combinations_without_duplicates(n)) == n
代表n> = 3
答案 5 :(得分:0)
这是一种利用deque.rotate
的方法>>> datax = []
>>> from collections import deque
>>> data_length = 10
>>> subset_length = 3
>>> for i in xrange(0, subset_length):
... datax.append(deque(xrange(data_length)))
...
>>> for i in xrange(0, subset_length):
... datax[i].rotate(i)
...
>>> print zip(*datax)
[(0, 9, 8), (1, 0, 9), (2, 1, 0), (3, 2, 1), (4, 3, 2), (5, 4, 3), (6, 5, 4), (7, 6, 5), (8, 7, 6), (9, 8, 7)]