我有以下问题,可以总结如下:
假设您有两个大于0的整数N(定义了 数组
n=np.array(range(N)
)和M。我们想生成所有 可能的元素组合 ofn
M ,条件是没有相等的元素是连续的。
例如,对于N = 3(n=[0,1,2]
)和M = 3,我们应获得:
(0,1,0), (0,1,2) (0,2,0), (0,2,1), (1,0,1), (1,0,2), (1,2,0), (1,2,1), (2,0,1), (2,0,2), (2,1,0), (2,1,2)
也就是说,不必出现(0,0,1), (1,1,1), (2,1,1)
...等组合。
注意,所有有效组合的数量仅由N*(N-1)**(M-1)
给出。
到目前为止,对于这样的示例,我正在使用这个简单的脚本(该脚本还计算长度从m = 1到m = M的所有组合):
import numpy as np
N = 3
M = 3
p = np.array(range(N))
ic = [0]*M
c2 = np.zeros((int(N*(N-1)**(M-1)),M))
c1 = np.zeros((int(N*(N-1)**(M-2)),M-1))
c0 = np.zeros((int(N*(N-1)**(M-3)),M-2))
for i in p:
c0[ic[0],:] = [i]
ic[0] += 1
for j in p[p!=i]:
c1[ic[1],:] = [i,j]
ic[1] += 1
for k in p[p!=j]:
c2[ic[2],:] = [i,j,k]
ic[2] += 1
问题在于,这仅适用于M = 3的特定情况,并且M可以是大于0的任何整数。因此,对于某些M,先前的代码应具有M个嵌套循环,必须手动引入。
我尝试定义一个可变数目的循环的 recursive 函数,像这样一个计算组合的 number 的函数(上面方程式给出的数字):
def rec_f(c,N,M):
if n>=1:
for x in range(N):
c=rec_f(c,N,M-1)
else:
c += 1
return c
我什至不知道为什么它可以解决这个简单的问题。现在,我需要了解先前循环的索引,以便能够复制生成所有可能组合的脚本,而我不知道该如何做。
我还尝试过制作一个唯一的for
循环(将迭代N *(N-1)^(M-1)次),并牢记组合可以用数字表示以N为底,但是玩了一段时间后,我没有任何用处。
我将不胜感激,在此先感谢您(感谢您的长帖子)!
答案 0 :(得分:3)
只需将最后一个元素(如果有)添加为递归函数的可选参数。另外,不需要N
参数,只需传递要从中选择的元素即可(这也使其更通用)。另外,我建议将其作为生成器函数,因为组合的数量可能会变得相当大,因此您可以在组合出现时逐个使用它们。
def combinations(elements, m, last=-1):
if m:
for x in elements:
if x != last:
for rest in combinations(elements, m-1, x):
yield (x,) + rest
else:
yield ()
或者更紧凑一点,使用yield from
生成器表达式:
def combinations(elements, m, last=-1):
if m:
yield from ((x,) + rest for x in elements if x != last
for rest in combinations(elements, m-1, x))
else:
yield ()
两个版本的示例结果:
print(*combinations(range(3), 3))
# (0, 1, 0), (0, 1, 2), (0, 2, 0), (0, 2, 1), (1, 0, 1), (1, 0, 2), (1, 2, 0), (1, 2, 1), (2, 0, 1), (2, 0, 2), (2, 1, 0), (2, 1, 2)
答案 1 :(得分:2)
您所描述的内容可以通过使用Python的(强大的)itertools
库来实现,然后根据您的情况进行过滤。但是,您想要的是数组的 product 而不是 combination 。
这里是执行此操作的一种方法,假设参数N和M。
import numpy as np
import itertools
p = np.arange(N)
获得长度为3的乘积:
product_iterator = itertools.product(p, repeat=M)
这为您提供了一个迭代器对象。您可以从中获取具体列表(在本示例中,尽管我将其称为列表,但立即将其转换为数组):
product_list = np.array(list(product_iterator))
这时,您得到了所有27种组合的数组:[[0 0 0],[0 0 1],[0 0 2],...,[2 2 1],[2 2 2]] 。现在,您可以按照所需的条件过滤它们。
在您的情况下,“没有连续的重复元素”等于检查两个连续的元素之间的差是否永远不为零。因此我们得到了不同之处:
diffs = np.diff(product_list,axis=1)
这将产生:
[[ 0 0]
[ 0 1]
[ 0 2]
[ 1 -1]
[ 1 0]
[ 1 1]
[ 2 -2]
[ 2 -1]
[ 2 0]
[-1 0]
[-1 1]
[-1 2]
[ 0 -1]
[ 0 0]
[ 0 1]
[ 1 -2]
[ 1 -1]
[ 1 0]
[-2 0]
[-2 1]
[-2 2]
[-1 -1]
[-1 0]
[-1 1]
[ 0 -2]
[ 0 -1]
[ 0 0]]
现在我们逐行检查是否有零:
no_consec_indexes = np.apply_along_axis(lambda x: np.all(x), 1, diffs)
这产生了一组布尔no_consec_indexes
:
array([False, False, False, True, False, True, True, True, False,
False, True, True, False, False, False, True, True, False,
False, True, True, True, False, True, False, False, False])
您可以使用它来过滤出原始产品数组:
product_list[no_consec_indexes]
哪个会产生您想要的答案:
array([[0, 1, 0],
[0, 1, 2],
[0, 2, 0],
[0, 2, 1],
[1, 0, 1],
[1, 0, 2],
[1, 2, 0],
[1, 2, 1],
[2, 0, 1],
[2, 0, 2],
[2, 1, 0],
[2, 1, 2]])