Python 求和为同一向量的两对不相交对

Python 求和为同一向量的两对不相交对,python,numpy,Python,Numpy,这是一项后续行动 我有随机二维数组,我使用 import numpy as np from itertools import combinations n = 50 A = np.random.randint(2, size=(m,n)) 我想确定矩阵是否有两个不相交的列对,它们的和是同一列向量。我正在寻找一个快速的方法来做到这一点。在前面的问题中((0,1),(0,2))可以作为一对列索引,但在这种情况下,它不是两对中的0 上一个问题的公认答案是如此巧妙地优化了,不幸的是,我看不出如何做出这

这是一项后续行动

我有随机二维数组,我使用

import numpy as np
from itertools import combinations
n = 50
A = np.random.randint(2, size=(m,n))
我想确定矩阵是否有两个不相交的列对,它们的和是同一列向量。我正在寻找一个快速的方法来做到这一点。在前面的问题中((0,1),(0,2))可以作为一对列索引,但在这种情况下,它不是两对中的0

上一个问题的公认答案是如此巧妙地优化了,不幸的是,我看不出如何做出这个看起来简单的改变。(在这个问题中,我对列而不是行感兴趣,但我总是可以做A.transpose()

下面是一些代码,演示如何测试所有4×4阵列

n = 4
nxn = np.arange(n*n).reshape(n, -1)
count = 0
for i in xrange(2**(n*n)):
   A = (i >> nxn) %2
   p = 1
   for firstpair in combinations(range(n), 2):
       for secondpair in combinations(range(n), 2):
           if firstpair < secondpair and not set(firstpair) & set(secondpair):
              if (np.array_equal(A[firstpair[0]] + A[firstpair[1]], A[secondpair[0]] + A[secondpair[1]] )):
                  if (p):
                      count +=1
                      p = 0
print count
n=4
nxn=np.arange(n*n).重塑(n,-1)
计数=0
对于x范围内的i(2**(n*n)):
A=(i>>nxn)%2
p=1
对于组合中的第一对(范围(n),2):
对于组合中的第二对(范围(n),2):
如果第一对<第二对且未设置(第一对)&设置(第二对):
如果(np.数组_等于(A[firstpair[0]]+A[firstpair[1]],A[secondpair[0]]+A[secondpair[1]]):
如果(p):
计数+=1
p=0
打印计数

这将输出3136。

这是我的解决方案,扩展到我认为您想要的。但这并不完全清楚;可以得到任意数量的行对,这些行对的总和相同;其中可能存在求和为相同值的唯一行子集。例如:

给定这组总和相同的行对

[[19 19 30 30]
 [11 16 11 16]]
存在这些行的唯一子集,这些行可能仍然被视为有效;但它应该这样做吗

[[19 30]
 [16 11]]
无论如何,鉴于下面的代码,我希望这些细节很容易处理

import numpy as np

n = 20
#also works for non-square A
A = np.random.randint(2, size=(n*6,n)).astype(np.int8)
##A = np.array( [[0, 0, 0], [1, 1, 1], [1, 1 ,1]], np.uint8)
##A = np.zeros((6,6))
#force the inclusion of some hits, to keep our algorithm on its toes
##A[0] = A[1]


def base_pack_lazy(a, base, dtype=np.uint64):
    """
    pack the last axis of an array as minimal base representation
    lazily yields packed columns of the original matrix
    """
    a = np.ascontiguousarray( np.rollaxis(a, -1))
    packing = int(np.dtype(dtype).itemsize * 8 / (float(base) / 2))
    for columns in np.array_split(a, (len(a)-1)//packing+1):
        R = np.zeros(a.shape[1:], dtype)
        for col in columns:
            R *= base
            R += col
        yield R

def unique_count(a):
    """returns counts of unique elements"""
    unique, inverse = np.unique(a, return_inverse=True)
    count = np.zeros(len(unique), np.int)
    np.add.at(count, inverse, 1)        #note; this scatter operation requires numpy 1.8; use a sparse matrix otherwise!
    return unique, count, inverse

def voidview(arr):
    """view the last axis of an array as a void object. can be used as a faster form of lexsort"""
    return np.ascontiguousarray(arr).view(np.dtype((np.void, arr.dtype.itemsize * arr.shape[-1]))).reshape(arr.shape[:-1])


def has_identical_row_sums_lazy(A, combinations_index):
    """
    compute the existence of combinations of rows summing to the same vector,
    given an nxm matrix A and an index matrix specifying all combinations

    naively, we need to compute the sum of each row combination at least once, giving n^3 computations
    however, this isnt strictly required; we can lazily consider the columns, giving an early exit opportunity
    all nicely vectorized of course
    """

    multiplicity, combinations = combinations_index.shape
    #list of indices into combinations_index, denoting possibly interacting combinations
    active_combinations = np.arange(combinations, dtype=np.uint32)
    #keep all packed columns; we might need them later
    columns = []

    for packed_column in base_pack_lazy(A, base=multiplicity+1):       #loop over packed cols
        columns.append(packed_column)
        #compute rowsums only for a fixed number of columns at a time.
        #this is O(n^2) rather than O(n^3), and after considering the first column,
        #we can typically already exclude almost all combinations
        partial_rowsums = sum(packed_column[I[active_combinations]] for I in combinations_index)
        #find duplicates in this column
        unique, count, inverse = unique_count(partial_rowsums)
        #prune those combinations which we can exclude as having different sums, based on columns inspected thus far
        active_combinations = active_combinations[count[inverse] > 1]
        #early exit; no pairs
        if len(active_combinations)==0:
            return False

    """
    we now have a small set of relevant combinations, but we have lost the details of their particulars
    to see which combinations of rows does sum to the same value, we do need to consider rows as a whole
    we can simply apply the same mechanism, but for all columns at the same time,
    but only for the selected subset of row combinations known to be relevant
    """
    #construct full packed matrix
    B = np.ascontiguousarray(np.vstack(columns).T)
    #perform all relevant sums, over all columns
    rowsums = sum(B[I[active_combinations]] for I in combinations_index)
    #find the unique rowsums, by viewing rows as a void object
    unique, count, inverse = unique_count(voidview(rowsums))
    #if not, we did something wrong in deciding on active combinations
    assert(np.all(count>1))

    #loop over all sets of rows that sum to an identical unique value
    for i in xrange(len(unique)):
        #set of indexes into combinations_index;
        #note that there may be more than two combinations that sum to the same value; we grab them all here
        combinations_group = active_combinations[inverse==i]
        #associated row-combinations
        #array of shape=(mulitplicity,group_size)
        row_combinations = combinations_index[:,combinations_group]

        #if no duplicate rows involved, we have a match
        if len(np.unique(row_combinations[:,[0,-1]])) == multiplicity*2:
            print row_combinations
            return True

    #none of identical rowsums met uniqueness criteria
    return False


def has_identical_triple_row_sums(A):
    n = len(A)
    idx = np.array( [(i,j,k)
        for i in xrange(n)
            for j in xrange(n)
                for k in xrange(n)
                    if i<j and j<k], dtype=np.uint16)
    idx = np.ascontiguousarray( idx.T)
    return has_identical_row_sums_lazy(A, idx)

def has_identical_double_row_sums(A):
    n = len(A)
    idx = np.array(np.tril_indices(n,-1), dtype=np.int32)
    return has_identical_row_sums_lazy(A, idx)


from time import clock
t = clock()
for i in xrange(1):
##    print has_identical_double_row_sums(A)
    print has_identical_triple_row_sums(A)
print clock()-t
将numpy导入为np
n=20
#也适用于非方形A
A=np.random.randint(2,size=(n*6,n)).astype(np.int8)
##A=np.array([[0,0,0],[1,1,1],[1,1,1]],np.uint8)
##A=np.零((6,6))
#强制包含一些命中,使我们的算法保持稳定
##A[0]=A[1]
def base_pack_lazy(a,base,dtype=np.uint64):
"""
将数组的最后一个轴打包为最小基表示
惰性地生成原始矩阵的压缩列
"""
a=np.ASCONTIGOUUSARRAY(np.rollaxis(a,-1))
包装=int(np.dtype(dtype).itemsize*8/(浮点数(基数)/2))
对于np.array_split(a,(len(a)-1)//packing+1中的列:
R=np.zero(a.shape[1:],dtype)
对于列中的列:
R*=基数
R+=col
产量R
def唯一_计数(a):
“”“返回唯一元素的计数”“”
unique,inverse=np.unique(a,返回值_inverse=True)
count=np.zero(len(unique),np.int)
np.add.at(计数,倒数,1)#注;此分散操作需要numpy 1.8;否则使用稀疏矩阵!
返回唯一、计数、反转
def无效视图(arr):
“”“将数组的最后一个轴作为空对象查看。可以用作更快的lexsort形式”“”
返回np.ascontiguousarray(arr.view)(np.dtype((np.void,arr.dtype.itemsize*arr.shape[-1]))。重新整形(arr.shape[:-1])
def具有相同的行和惰性(A,组合索引):
"""
计算求和到同一向量的行组合的存在性,
给定一个nxm矩阵A和一个指定所有组合的索引矩阵
天真地说,我们需要至少计算一次每行组合的总和,给出n^3次计算
然而,这并不是严格要求的,我们可以懒惰地考虑栏目,提供早期的退出机会。
当然,所有这些都很好地矢量化了
"""
多重性,组合=组合_index.shape
#组合索引的索引列表,表示可能相互作用的组合
活动组合=np.arange(组合,数据类型=np.uint32)
#保留所有填料塔;我们以后可能需要它们
列=[]
对于base_pack_lazy(A,base=multiplity+1)中的packed_列:#在packed cols上循环
列。追加(压缩列)
#一次仅为固定数量的列计算行和。
#这是O(n^2)而不是O(n^3),在考虑第一列之后,
#我们通常已经排除了几乎所有的组合
部分行和=和(组合索引中I的压缩列[I[活动组合])
#在此列中查找重复项
unique,count,inverse=unique\u count(部分行和)
#根据到目前为止检查的列,删减那些我们可以排除为具有不同总和的组合
活动组合=活动组合[计数[逆]>1]
#提前退出;没有双
如果len(活动_组合)==0:
返回错误
"""
我们现在有一小套相关的组合,但我们已经失去了它们的细节
若要查看行的组合组合为相同值,则需要将行视为一个整体。
我们可以简单地应用相同的机制,但同时对所有列,
但仅适用于已知相关的行组合的选定子集
"""
#构造全填充矩阵
B=np.ASCONTIGOUUSARRAY(np.vstack(列).T)
#对所有列执行所有相关合计
行和=和(B[I[活动组合]]用于组合索引中的I)
#通过将行视为空对象来查找唯一的行和
unique,count,inverse=unique_count(无效视图(行和))
#如果不是,我们在决定主动组合时就做错了
断言(np.all(计数>1))
#循环所有求和为相同唯一值的行集合
对于xrange中的i(len(unique)):
#将索引集组合为索引;
#请注意,可能有两个以上的组合总和为相同的值;我们把他们都抓在这里
组合组=活动组合[逆==i]
#关联行组合
#形状数组=(多重性,组大小)
行组合=组合