我想计算每个列表的每个元素的反转次数。
如果i < j
和list[i] > list[j]
,则对(i, j)
是一个反转。许多算法都会计算这些对的总数,而我想对某些列表中的所有元素进行计数,这些元素多久是一次反转对的一部分。
考虑例如此列表:[4, 2, 3, 1]
有五个反转对:(4, 2), (4, 3), (4, 1), (2, 1), (3, 1)
。我知道您可以通过嵌套的for循环(或使用向量化)来获取元素计数:
import numpy as np
lst = np.array([4,2,3,1])
inv_count = np.zeros(lst.size)
for i in np.arange(lst.size - 1):
idx = np.where(lst[i] > lst[(i+1):])[0] + i + 1
inv_count[idx] += 1
inv_count[i] += idx.size
产生正确的结果
array([3., 2., 2., 3.])
但这正在O(n^2)
中运行(我认为),我想知道是否可以更有效地解决它。
例如,我知道mergesort以下所示的代码通常用于计算O(n log n)
中的总反转,但是我不确定如何采用它来对每个元素进行计数?
def mergeSortInversions(arr):
if len(arr) == 1:
return arr, 0
else:
a = arr[:len(arr)//2]
b = arr[len(arr)//2:]
a, ai = mergeSortInversions(a)
b, bi = mergeSortInversions(b)
c = []
i = 0
j = 0
inversions = 0 + ai + bi
while i < len(a) and j < len(b):
if a[i] <= b[j]:
c.append(a[i])
i += 1
else:
c.append(b[j])
j += 1
inversions += (len(a)-i)
c += a[i:]
c += b[j:]
return c, inversions
答案 0 :(得分:2)
您可以使用函数combinations()
和filter()
来构建具有反型的列表:
from itertools import combinations
l = [4, 2, 3, 1]
list(filter(lambda x: x[0] > x[1], combinations(l, 2)))
# [(4, 2), (4, 3), (4, 1), (2, 1), (3, 1)]
您可以使用defaultdict()
来计算倒置次数:
from itertools import combinations
from collections import defaultdict
l = [4, 2, 3, 1]
dd = defaultdict(int)
for i, j in combinations(l, 2):
if i > j:
dd[i] += 1
dd[j] += 1
print(dd)
# defaultdict(<type 'int'>, {1: 3, 2: 2, 3: 2, 4: 3})
答案 1 :(得分:1)
您可以浏览np.greater.outer()。 ufunc的外部方法。
import numpy as np
a = np.array([ 4, 2, 3, 1])
为所有i和j都使用a [i]> a [j]生成一个正方形数组
arr = np.greater.outer(a,a)*1
arr
Out[24]:
array([[0, 1, 1, 1],
[0, 0, 0, 1],
[0, 1, 0, 1],
[0, 0, 0, 0]])
我们只对右上角的三角形感兴趣。
arr = np.triu(arr)
arr
Out[25]:
array([[0, 1, 1, 1],
[0, 0, 0, 1],
[0, 0, 0, 1],
[0, 0, 0, 0]])
arr += arr.T # add the transposition. arr is symmetrical about the diagonal.
arr
Out[9]:
array([[0, 1, 1, 1],
[1, 0, 0, 1],
[1, 0, 0, 1],
[1, 1, 1, 0]])
求和以获取计数。
arr.sum(axis = 0)
Out[28]: array([3, 2, 2, 3])
所有循环都隐藏在外部方法中。
答案 2 :(得分:1)
根据@Andrew Scott的评论,这是所需的更改:
import numpy as np
def mergeSortInversions(arr, counts):
if len(arr) == 1:
return arr, counts
else:
a = arr[:len(arr)//2]
b = arr[len(arr)//2:]
a, ai = mergeSortInversions(a, counts)
b, bi = mergeSortInversions(b, counts)
c = []
i = 0
j = 0
inversions = ai + bi
while i < len(a) and j < len(b):
if a[i] <= b[j]:
c = np.concatenate([c, a[i, None]])
i += 1
else:
c = np.concatenate([c, b[j, None]])
inversions[a[i:].astype(np.int) - 1] += 1
inversions[b[j].astype(np.int) - 1] += (len(a)-i)
j += 1
c = np.concatenate([c, a[i:]])
c = np.concatenate([c, b[j:]])
return c, inversions