每个元素的计数反转

时间:2019-04-30 18:28:26

标签: python algorithm sorting count mergesort

我想计算每个列表的每个元素的反转次数。 如果i < jlist[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

3 个答案:

答案 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