简体   繁体   English

numpy 二维数组的所有组合,其中填充了 0 和 1

[英]All combinations of a numpy 2d array filled with 0s and 1s

Given K, I need to have all the possibile combinations of K x 2 numpy matrices so that in each matrix there are all 0s except for two 1s in different rows and columns.给定 K,我需要拥有 K x 2 numpy 矩阵的所有可能组合,以便在每个矩阵中,除了不同行和列中的两个 1 之外,所有的都是 0。 Something like this for K = 5:对于 K = 5 是这样的:

  1. [[1,0],[0,1],[0,0],[0,0][0,0]] [[1,0],[0,1],[0,0],[0,0][0,0]]
  2. [[1,0],[0,0],[0,1],[0,0][0,0]] [[1,0],[0,0],[0,1],[0,0][0,0]]
  3. [[1,0],[0,0],[0,0],[0,1][0,0]] [[1,0],[0,0],[0,0],[0,1][0,0]]
  4. [[1,0],[0,0],[0,0],[0,0][0,1]] [[1,0],[0,0],[0,0],[0,0][0,1]]
  5. [[0,0],[1,0],[0,1],[0,0][0,0]] [[0,0],[1,0],[0,1],[0,0][0,0]]
  6. [[0,0],[1,0],[0,0],[0,1][0,0]] [[0,0],[1,0],[0,0],[0,1][0,0]]
  7. ... and so on ... 等等

So the resulting array should be a K x 2 x (K*(K-1)/2).所以结果数组应该是 K x 2 x (K*(K-1)/2)。 I want to avoid loops since it's not an efficient way when K is big enough (in my specific case K = 300)我想避免循环,因为当 K 足够大时(在我的特定情况下 K = 300),这不是一种有效的方法

I can't think of an elegant solution but here's a not-so-elegant pure numpy one:我想不出一个优雅的解决方案,但这是一个不太优雅的纯 numpy 一个:

import numpy as np

def combination_matrices(K):
    # get combination indices
    i, j = np.indices((K, K))
    comb_indices = np.transpose((i < j).nonzero())  # (num_combs, 2) array where ones are
    num_combs = comb_indices.shape[0]  # K*(K-1)/2

    # create a matrix of the desired shape, first axis enumerates combinations
    matrices = np.zeros((num_combs, K, 2), dtype=int)
    # broadcasting assignment of ones
    comb_range, col_index = np.ogrid[:num_combs, :2]
    matrices[comb_range, comb_indices, col_index] = 1
    return matrices

This first uses the indices of a (K, K) -shaped array to find the index pairs for every combination (these are indices that encode the upper triangle of the array, excluding the diagonal).这首先使用(K, K)形数组的索引来查找每个组合的索引对(这些索引对数组的上三角形进行编码,不包括对角线)。 Then we use a bit tricky broadcasting assignment (heavy fancy indexing ) to set each corresponding element of the pre-allocated output array to 1.然后我们使用一个有点棘手的广播分配(heavy fancy indexing )将预分配的 output 数组的每个对应元素设置为 1。

Note that I put the K*(K-1)/2 -sized axis first, because this makes the most sense in numpy with C-contiguous memory layout.请注意,我首先放置了K*(K-1)/2大小的轴,因为这在具有 C 连续 memory 布局的 numpy 中最有意义。 This way when you take the matrix for combination index 3 , arr[3, ...] will be a contiguous chunk of memory of shape (K, 2) that's fast to work with in vectorised operations.这样,当您采用组合索引3的矩阵时, arr[3, ...]将是形状为(K, 2)的 memory 的连续块,在矢量化操作中可以快速使用。

The output for K = 4 : K = 4的 output :

[[[1 0]
  [0 1]
  [0 0]
  [0 0]]

 [[1 0]
  [0 0]
  [0 1]
  [0 0]]

 [[1 0]
  [0 0]
  [0 0]
  [0 1]]

 [[0 0]
  [1 0]
  [0 1]
  [0 0]]

 [[0 0]
  [1 0]
  [0 0]
  [0 1]]

 [[0 0]
  [0 0]
  [1 0]
  [0 1]]]

itertools Libary has what you are looking for here is an example itertools 库有你要找的东西 这是一个例子


from itertools import product
 
def cartesian_product(arr1, arr2):
 
    # return the list of all the computed tuple
    # using the product() method
    return list(product(arr1, arr2))
   
# Driver Function
if __name__ == "__main__":
    arr1 = [1, 2, 3]
    arr2 = [5, 6, 7]
    print(cartesian_product(arr1, arr2))

Output: Output:

[(1, 5), (1, 6), (1, 7), (2, 5), (2, 6), (2, 7), (3, 5), (3, 6), (3, 7)] 

pass your arrays as argument to the cartesian_product functions将您的cartesian_product作为参数传递给 Cartesian_product 函数

This is an oddly specific question, but and interesting problem, I'd love to know what the context is?这是一个奇怪的具体问题,但有趣的问题,我很想知道上下文是什么?

You are looking for all permutations of a multiset您正在寻找多重集合的所有排列在此处输入图像描述 , python's itertools doesn't currently support this. ,python 的itertools目前不支持这个。 So simplest solution is to use the multiset tools of the sympy library.所以最简单的解决方案是使用sympy库的 multiset 工具。

The following code took about ~2.5 minutes to run on my machine, so is fairly fast for a single thread.以下代码在我的机器上运行大约需要 2.5 分钟,因此对于单线程来说相当快。 You're looking at 179700 unique permutations for K=300.您正在查看 K=300 的179700唯一排列。

(I took inspiration from https://stackoverflow.com/a/40289807/10739860 ) (我从https://stackoverflow.com/a/40289807/10739860获得灵感)

from collections import Counter
from math import factorial, prod

import numpy as np
from sympy.utilities.iterables import multiset_permutations
from tqdm import tqdm


def No_multiset_permutations(multiset: list) -> int:
    """Calculates the No. possible permutations given a multiset.
    See: https://en.wikipedia.org/wiki/Permutation#Permutations_of_multisets

    :param multiset: List representing a multiset.
    """
    value_counts = Counter(multiset).values()
    denominator = prod([factorial(val) for val in value_counts])
    return int(factorial(len(multiset)) / denominator)


def multiset_Kx2_permutations(K: int) -> np.ndarray:
    """This will generate all possible unique Kx2 permutations of an array
    withsize K where two values are 1 and the rest are 0.

    :param K: The size of the array.
    """
    # Construct number multiset, e.g. K=5 gives [1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
    numbers = [1, 1] + [0] * (K - 1) * 2

    # Use sympy's multiset_permutations to get a multiset permutation generator
    generator = multiset_permutations(numbers)

    # Calculate the No. possible permutations
    number_of_perms = No_multiset_permutations(numbers)

    # Get all permutations, bonus progress bar is included :)
    unique_perms = [next(generator) for _ in tqdm(range(number_of_perms))]

    # Reshape each permutation to Kx2
    unique_perms = np.array(unique_perms, dtype=np.int8)
    return unique_perms.reshape(-1, K, 2)


if __name__ == "__main__":
    solution = multiset_Kx2_permutations(300)

Another possibility (with rearranged axes for clearer output):另一种可能性(重新排列轴以获得更清晰的输出):

import numpy as np
from itertools import combinations

k = 4
n = k*(k-1)//2
x = list(zip(*combinations(range(k), 2)))
out = np.zeros((n, k, 2), dtype=int)
out[np.r_[0:n], x, [[0]*n, [1]*n]] = 1
print(out)

It gives:它给:

[[[1 0]
  [0 1]
  [0 0]
  [0 0]]

 [[1 0]
  [0 0]
  [0 1]
  [0 0]]

 [[1 0]
  [0 0]
  [0 0]
  [0 1]]

 [[0 0]
  [1 0]
  [0 1]
  [0 0]]

 [[0 0]
  [1 0]
  [0 0]
  [0 1]]

 [[0 0]
  [0 0]
  [1 0]
  [0 1]]]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM