最好用例子解释。如果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 时间更好的方法来做到这一点?
答案 0 :(得分:2)
二进制操作对此任务非常有用:
将每个子列表转换为二进制数,其中0变为1位,其他数字变为0位。
例如,[0,1,2,0,4]
将变为10010
,即18。
消除重复的数字。
代码:
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 )可能会有所帮助。这可能会导致我们采用以下算法:
这里,(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中使这个过程变得微不足道:
将每个子列表转换为"为零"或者"不是零"
map (map (\x -> if x==0 then 1 else 0)) bigList
枚举列表,以便保留索引
enumList = zip [0..] bigList
将每个子列表与其连续的子列表进行比较
myCompare = concat . go
where
go [] = []
go ((ix, xs):xss) = [((ix, iy), zipWith (.|.) xs ys) | (iy, ys) <- xss] : go xss
计算你的最大值
best = maximumBy (compare `on` (sum . snd)) $ myCompare enumList
拉出指数
result = fst best