从列表中查找两次尝试中一个元素的最大出现次数

时间:2018-05-21 23:10:13

标签: python algorithm list permutation

最好用例子解释。如果python列表是 -

[[0,1,2,0,4],
 [0,1,2,0,2],
 [1,0,0,0,1],
 [1,0,0,1,0]]

我想选择两个子列表,这将产生零的最大出现次数 - 其中总和将按如下方式计算

SUM = No. of zeros present in the first selected sub-list + No. of zeros present in the second selected sub-list which were not present in the first selected sub-list.

在这种情况下,答案是5.(第一个或第二个子列表和最后一个子列表)。 (注意,不要选择第三个子列表,因为它在第三个索引中存在零,这与我们必须选择的第一个/第二个子列表中的相同,并且它将总计为4,如果不是最大值则我们考虑最后一个子列表)

如果我们将它应用于大输入,那么哪种算法最适合?有没有比N 2 时间更好的方法来做到这一点?

3 个答案:

答案 0 :(得分:2)

二进制操作对此任务非常有用:

  1. 将每个子列表转换为二进制数,其中0变为1位,其他数字变为0位。

    例如,[0,1,2,0,4]将变为10010,即18。

  2. 消除重复的数字。

  3. 将剩余数字成对组合,并将它们与二进制OR组合。
  4. 找到最多1位的数字。
  5. 代码:

    lists = [[0,1,2,0,4],
             [0,1,2,0,2],
             [1,0,0,0,1],
             [1,0,0,1,0]]
    
    import itertools
    
    def to_binary(lst):
        num = ''.join('1' if n == 0 else '0' for n in lst)
        return int(num, 2)
    
    def count_ones(num):
        return bin(num).count('1')
    
    # Step 1 & 2: Convert to binary and remove duplicates
    binary_numbers = {to_binary(lst) for lst in lists}
    
    # Step 3: Create pairs
    combinations = itertools.combinations(binary_numbers, 2)
    
    # Step 4 & 5: Compute binary OR and count 1 digits
    zeros = (count_ones(a | b) for a, b in combinations)
    
    print(max(zeros))  # output: 5
    

答案 1 :(得分:1)

朴素算法的效率是O(n(n-1)* m)~O(n 2 m)其中n是列表数,m是每个列表的长度。当n和m的大小相当时,这相当于O(n 3 )。

观察到幼稚矩阵乘法也是O(n 3 )可能会有所帮助。这可能会导致我们采用以下算法:

  1. 只用1和0写每个列表,其中1表示非零条目。
  2. 将这些列表排列在矩阵A中。
  3. 计算产品M = AA T
  4. 找出M中的最小元素;行和列对应于产生最大数量的非重叠零的列表。
  5. 这里,(3)是算法的限制步骤。渐近地,根据您的矩阵乘法算法,您可以将复杂度降低到大致为O(n 2.4 )。

    示例Python实现如下:

    import numpy as np
    
    lists = [[0,1,2,0,4],
             [0,1,2,0,2],
             [1,0,0,0,1],
             [1,0,0,1,0]]
    
    filtered = list(set(tuple(1 if e else 0 for e in sub) for sub in lists))
    A = np.mat(filtered)
    D = np.einsum('ik,jk->ij', A, A)
    
    indices= np.unravel_index(np.argmin(D), D.shape)
    
    print(f'{indices}: {len(lists[0]) - D[indices]}') # (0, 3): 0
    

    请注意,这个算法本身就具有计算点积矩阵的下三角和上三角两半的基本低效率。然而,numpy加速可能会从组合方法中抵消这一点。请参阅下面的时间结果:

    def numpy_approach(lists):
        filtered = list(set(tuple(1 if e else 0 for e in sub) for sub in lists))
        A = np.mat(filtered, dtype=bool).astype(int)
        D = np.einsum('ik,jk->ij', A, A)
        return len(lists[0]) - D.min()
    
    def itertools_approach(lists):
        binary_numbers = {int(''.join('1' if n == 0 else '0' for n in lst), 2) 
            for lst in lists}
        combinations = itertools.combinations(binary_numbers, 2)
        zeros = (bin(a | b).count('1') for a, b in combinations)
        return max(zeros)
    
    
    from time import time
    
    N = 1000
    lists = [[random.randint(0, 5) for _ in range(10)] for _ in range(100)]
    
    for name, function in {
            'numpy approach': numpy_approach, 
            'itertools approach': itertools_approach
            }.items():
        start = time()
        for _ in range(N):
            function(lists)
        print(f'{name}: {time() - start}')
    
    # numpy approach: 0.2698099613189697
    # itertools approach: 0.9693171977996826
    

答案 2 :(得分:0)

该算法应该看起来像(以Haskell代码为例,以免在Python中使这个过程变得微不足道:

  1. 将每个子列表转换为"为零"或者"不是零"

    map (map (\x -> if x==0 then 1 else 0)) bigList 
    
  2. 枚举列表,以便保留索引

    enumList = zip [0..] bigList
    
  3. 将每个子列表与其连续的子列表进行比较

    myCompare = concat . go
      where
      go []             = []
      go ((ix, xs):xss) = [((ix, iy), zipWith (.|.) xs ys) | (iy, ys) <- xss] : go xss
    
  4. 计算你的最大值

    best = maximumBy (compare `on` (sum . snd)) $ myCompare enumList
    
  5. 拉出指数

    result = fst best