簡體   English   中英

每個元素的計數反轉

[英]Count Inversion per Element

我想計算某些列表中每個元素的反轉次數 如果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

您可以使用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})

您可以瀏覽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])

所有循環都隱藏在外部方法中。

根據@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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM