Подсчет инверсий в массиве

Я разрабатываю алгоритм, чтобы сделать следующее: заданный массив A[1... n], для каждого i < j найти все пары инверсии, такие, что A[i] > A[j]. Я использую сортировку слияния и копирование массива A в массив B, а затем сравниваю два массива, но мне трудно понять, как я могу использовать это, чтобы найти количество инверсий. Любые подсказки или помощь были бы оценены.

Ответ 1

Единственный совет, который я мог бы дать этому (что выглядит подозрительно, как вопрос домашней работы;)) заключается в том, чтобы сначала сделать это вручную с небольшим набором чисел (например, 5), а затем записать шаги, которые вы предприняли для решения проблемы проблема.

Это позволит вам определить общее решение, которое вы можете использовать для написания кода.

Ответ 2

Итак, вот решение O (n log n) в java.

long merge(int[] arr, int[] left, int[] right) {
    int i = 0, j = 0, count = 0;
    while (i < left.length || j < right.length) {
        if (i == left.length) {
            arr[i+j] = right[j];
            j++;
        } else if (j == right.length) {
            arr[i+j] = left[i];
            i++;
        } else if (left[i] <= right[j]) {
            arr[i+j] = left[i];
            i++;                
        } else {
            arr[i+j] = right[j];
            count += left.length-i;
            j++;
        }
    }
    return count;
}

long invCount(int[] arr) {
    if (arr.length < 2)
        return 0;

    int m = (arr.length + 1) / 2;
    int left[] = Arrays.copyOfRange(arr, 0, m);
    int right[] = Arrays.copyOfRange(arr, m, arr.length);

    return invCount(left) + invCount(right) + merge(arr, left, right);
}

Это почти нормальный тип слияния, вся магия скрыта в функции слияния. Обратите внимание, что при алгоритме сортировки удаляются инверсии. В то время как алгоритм слияния подсчитывает количество удаленных инверсий (отсортировано, можно сказать).

Единственный момент, когда удаляются инверсии, - это когда алгоритм берет элемент с правой стороны массива и объединяет его с основным массивом. Количество инверсий, удаленных этой операцией, - это количество элементов, оставшихся от левого массива для объединения.:)

Надеюсь, что это достаточно объяснительно.

Ответ 3

Я нашел его в O (n * log n) по следующему методу.

  • Объединить массив A и создать копию (массив B)
  • Возьмите A [1] и найдите его позицию в отсортированном массиве B через двоичный поиск. Количество инверсий для этого элемента будет на единицу меньше, чем номер индекса его позиции в B, поскольку каждое меньшее число, которое появляется после первого элемента A, будет инверсией.

    2а. скопируйте количество инверсий, чтобы встретить переменную num_inversions.

    2b. удалите A [1] из массива A, а также из его соответствующей позиции в массиве B

  • повторите попытку с шага 2, пока в не будет больше элементов.

Вот пример выполнения этого алгоритма. Исходный массив A = (6, 9, 1, 14, 8, 12, 3, 2)

1: Объединить сортировку и скопировать в массив B

B = (1, 2, 3, 6, 8, 9, 12, 14)

2: возьмите A [1] и бинарный поиск, чтобы найти его в массиве B

A [1] = 6

B = (1, 2, 3, 6, 8, 9, 12, 14)

6 находится в 4-й позиции массива B, поэтому есть 3 инверсии. Мы это знаем, потому что 6 находилось в первой позиции в массиве A, поэтому любой элемент с более низким значением, который впоследствии появляется в массиве A, будет иметь индекс j > я (так как я в этом случае равен 1).

2.b: Удалите A [1] из массива A, а также из его соответствующей позиции в массиве B (выделены жирные элементы).

A = ( 6, 9, 1, 14, 8, 12, 3, 2) = (9, 1, 14, 8, 12, 3, 2)

B = (1, 2, 3, 6, 8, 9, 12, 14) = (1, 2, 3, 8, 9, 12, 14)

3: Повторите шаг 2 на новых массивах A и B.

A [1] = 9

B = (1, 2, 3, 8, 9, 12, 14)

9 теперь находится в пятой позиции массива B, таким образом, существует 4 инверсии. Мы это знаем, потому что 9 находилось в первой позиции в массиве A, поэтому любой более низкий элемент значения, который впоследствии появляется, будет иметь индекс j > я (так как я в этом случае снова равен 1). Удалите A [1] из массива A, а также из его соответствующей позиции в массиве B (выделены жирные элементы)

A = ( 9, 1, 14, 8, 12, 3, 2) = (1, 14, 8, 12, 3, 2)

B = (1, 2, 3, 8, 9, 12, 14) = (1, 2, 3, 8, 12, 14)

Продолжая в этом ключе, мы получим общее количество инверсий для массива A после завершения цикла.

Шаг 1 (сортировка слияния) приведет к выполнению O (n * log n). Шаг 2 будет выполняться n раз, и при каждом выполнении будет выполняться двоичный поиск, в котором O (log n) будет выполняться для всего O (n * log n). Таким образом, общее время работы будет равно O (n * log n) + O (n * log n) = O (n * log n).

Спасибо за вашу помощь. Написание образцов массивов на листе бумаги действительно помогло визуализировать проблему.

Ответ 4

В Python

# O(n log n)

def count_inversion(lst):
    return merge_count_inversion(lst)[1]

def merge_count_inversion(lst):
    if len(lst) <= 1:
        return lst, 0
    middle = int( len(lst) / 2 )
    left, a = merge_count_inversion(lst[:middle])
    right, b = merge_count_inversion(lst[middle:])
    result, c = merge_count_split_inversion(left, right)
    return result, (a + b + c)

def merge_count_split_inversion(left, right):
    result = []
    count = 0
    i, j = 0, 0
    left_len = len(left)
    while i < left_len and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            count += left_len - i
            j += 1
    result += left[i:]
    result += right[j:]
    return result, count        


#test code
input_array_1 = []  #0
input_array_2 = [1] #0
input_array_3 = [1, 5]  #0
input_array_4 = [4, 1] #1
input_array_5 = [4, 1, 2, 3, 9] #3
input_array_6 = [4, 1, 3, 2, 9, 5]  #5
input_array_7 = [4, 1, 3, 2, 9, 1]  #8

print count_inversion(input_array_1)
print count_inversion(input_array_2)
print count_inversion(input_array_3)
print count_inversion(input_array_4)
print count_inversion(input_array_5)
print count_inversion(input_array_6)
print count_inversion(input_array_7)

Ответ 5

Интересно, почему никто не упоминал двоично-индексированные деревья. Вы можете использовать его для поддержания префиксных сумм по значениям ваших элементов перестановки. Затем вы можете просто перейти справа налево и подсчитать для каждого элемента число элементов, меньшее, чем оно справа:

def count_inversions(a):
  res = 0
  counts = [0]*(len(a)+1)
  rank = { v : i+1 for i, v in enumerate(sorted(a)) }
  for x in reversed(a):
    i = rank[x] - 1
    while i:
      res += counts[i]
      i -= i & -i
    i = rank[x]
    while i <= len(a):
      counts[i] += 1
      i += i & -i
  return res

Сложность - O (n log n), а постоянный коэффициент очень низкий.

Ответ 6

У меня был вопрос, похожий на этот вопрос для домашнего задания. Я был ограничен, что он должен иметь эффективность O (nlogn).

Я использовал идею, которую вы предлагали использовать Mergesort, поскольку она уже имеет правильную эффективность. Я просто ввел код в функцию слияния, которая была в основном: Всякий раз, когда число из массива справа добавляется в выходной массив, я добавляю к общему числу инверсий количество оставшихся в левом массиве чисел.

Мне сейчас очень нравится, что я достаточно об этом думал. Ваш подсчет, сколько раз больше числа, следующего перед любыми числами.

НТН.

Ответ 7

Обратите внимание, что ответ Джеффри Ирвинг неверен.

Количество инверсий в массиве составляет половину общих элементов расстояния, которые необходимо перемещать для сортировки массива. Поэтому его можно вычислить, сортируя массив, сохраняя полученную перестановку p [i], а затем вычисляя сумму abs (p [i] -i)/2. Это занимает время O (n log n), которое является оптимальным.

Альтернативный метод приведен в http://mathworld.wolfram.com/PermutationInversion.html. Этот метод эквивалентен сумме max (0, p [i] -i), которая равна сумме abs (p [i] -i])/2, так как общие элементы расстояния, перемещенные влево, равны общие элементы расстояния перемещаются вправо.

Возьмем последовательность {3, 2, 1} в качестве примера. Существует три инверсии: (3, 2), (3, 1), (2, 1), поэтому число инверсии равно 3. Однако, согласно цитируемому методу, ответ был бы равен 2.

Ответ 8

Число инверсий можно найти, проанализировав процесс слияния в сортировке слияния: merge process

При копировании элемента из второго массива в массив слияния (9 в этом примере) он сохраняет свое место относительно других элементов. При копировании элемента из первого массива в массив слияния (здесь здесь 5) он инвертируется со всеми элементами, находящимися во втором массиве (2 инверсии с 3 и 4). Поэтому небольшая модификация сортировки слияния может решить проблему в O (n ln n).
Например, просто раскомментируйте две строки # в приведенном ниже коде python mergesort, чтобы иметь счетчик.

def merge(l1,l2):
    l = []
    # global count
    while l1 and l2:
        if l1[-1] <= l2[-1]:
            l.append(l2.pop())
        else:
            l.append(l1.pop())
            # count += len(l2)
    l.reverse()
    return l1 + l2 +l

def sort(l): 
    t = len(l) // 2
    return merge(sort(l[:t]), sort(l[t:])) if t > 0 else l

count=0
print(sort([5,1,2,4,9,3]), count)
# [1, 2, 3, 4, 5, 9] 6

EDIT

Для производительности в python, numpy и numba версии:

Часть numba для эффективного подхода BIT:

@numba.njit
def BIT(ags,counts,n):
    res = 0        
    for x in ags :
        i = x
        while i:
            res += counts[i]
            i -= i & -i
        i = x+1
        while i < n:
            counts[i] += 1
            i += i & -i
    return n*(n-1)//2 - res 

И часть numpy, которая использует argsort вместо ранга.

def count_inversions(a):
    n = a.size
    counts = np.zeros(n , np.uint64)
    ags = a.argsort(kind='mergesort')    
    return  BIT(ags,counts,n)

Он примерно в 50 раз быстрее, чем предыдущая версия.

Ответ 10

Вот одно возможное решение с изменением бинарного дерева. Он добавляет поле под названием rightSubTreeSize для каждого дерева node. Продолжайте вставлять число в двоичное дерево в том порядке, в котором они появляются в массиве. Если число идет lhs из node, счетчик инверсии для этого элемента будет (1 + rightSubTreeSize). Поскольку все эти элементы больше текущего элемента, и они появились бы раньше в массиве. Если элемент переходит в rhs node, просто увеличьте его rightSubTreeSize. Ниже приведен код.

Node { 
    int data;
    Node* left, *right;
    int rightSubTreeSize;

    Node(int data) { 
        rightSubTreeSize = 0;
    }   
};

Node* root = null;
int totCnt = 0;
for(i = 0; i < n; ++i) { 
    Node* p = new Node(a[i]);
    if(root == null) { 
        root = p;
        continue;
    } 

    Node* q = root;
    int curCnt = 0;
    while(q) { 
        if(p->data <= q->data) { 
            curCnt += 1 + q->rightSubTreeSize;
            if(q->left) { 
                q = q->left;
            } else { 
                q->left = p;
                break;
            }
        } else { 
            q->rightSubTreeSize++;
            if(q->right) { 
                q = q->right;
            } else { 
                q->right = p;
                break;
            }
        }
    }

    totCnt += curCnt;
  }
  return totCnt;

Ответ 11

public static int mergeSort(int[] a, int p, int r)
{
    int countInversion = 0;
    if(p < r)
    {
        int q = (p + r)/2;
        countInversion = mergeSort(a, p, q);
        countInversion += mergeSort(a, q+1, r);
        countInversion += merge(a, p, q, r);
    }
    return countInversion;
}

public static int merge(int[] a, int p, int q, int r)
{
    //p=0, q=1, r=3
    int countingInversion = 0;
    int n1 = q-p+1;
    int n2 = r-q;
    int[] temp1 = new int[n1+1];
    int[] temp2 = new int[n2+1];
    for(int i=0; i<n1; i++) temp1[i] = a[p+i];
    for(int i=0; i<n2; i++) temp2[i] = a[q+1+i];

    temp1[n1] = Integer.MAX_VALUE;
    temp2[n2] = Integer.MAX_VALUE;
    int i = 0, j = 0;

    for(int k=p; k<=r; k++)
    {
        if(temp1[i] <= temp2[j])
        {
            a[k] = temp1[i];
            i++;
        }
        else
        {
            a[k] = temp2[j];
            j++;
            countingInversion=countingInversion+(n1-i); 
        }
    }
    return countingInversion;
}
public static void main(String[] args)
{
    int[] a = {1, 20, 6, 4, 5};
    int countInversion = mergeSort(a, 0, a.length-1);
    System.out.println(countInversion);
}

Ответ 12

Вот С++-решение

/**
*array sorting needed to verify if first arrays n'th element is greater than sencond arrays
*some element then all elements following n will do the same
*/
#include<stdio.h>
#include<iostream>
using namespace std;
int countInversions(int array[],int size);
int merge(int arr1[],int size1,int arr2[],int size2,int[]);
int main()
{
    int array[] = {2, 4, 1, 3, 5};
    int size = sizeof(array) / sizeof(array[0]);
    int x = countInversions(array,size);
    printf("number of inversions = %d",x);
}

int countInversions(int array[],int size)
{
    if(size > 1 )
    {
    int mid = size / 2;
    int count1 = countInversions(array,mid);
    int count2 = countInversions(array+mid,size-mid);
    int temp[size];
    int count3 = merge(array,mid,array+mid,size-mid,temp);
    for(int x =0;x<size ;x++)
    {
        array[x] = temp[x];
    }
    return count1 + count2 + count3;
    }else{
        return 0;
    }
}

int merge(int arr1[],int size1,int arr2[],int size2,int temp[])
{
    int count  = 0;
    int a = 0;
    int b = 0;
    int c = 0;
    while(a < size1 && b < size2)
    {
        if(arr1[a] < arr2[b])
        {
            temp[c] = arr1[a];
            c++;
            a++;
        }else{
            temp[c] = arr2[b];
            b++;
            c++;
            count = count + size1 -a;
        }
    }

    while(a < size1)
    {
        temp[c] = arr1[a];
        c++;a++;
    }

while(b < size2)
    {
        temp[c] = arr2[b];
        c++;b++;
    }

    return count;
}

Ответ 13

Вот код C для инверсий count

#include <stdio.h>
#include <stdlib.h>

int  _mergeSort(int arr[], int temp[], int left, int right);
int merge(int arr[], int temp[], int left, int mid, int right);

/* This function sorts the input array and returns the
   number of inversions in the array */
int mergeSort(int arr[], int array_size)
{
    int *temp = (int *)malloc(sizeof(int)*array_size);
    return _mergeSort(arr, temp, 0, array_size - 1);
}

/* An auxiliary recursive function that sorts the input array and
  returns the number of inversions in the array. */
int _mergeSort(int arr[], int temp[], int left, int right)
{
  int mid, inv_count = 0;
  if (right > left)
  {
    /* Divide the array into two parts and call _mergeSortAndCountInv()
       for each of the parts */
    mid = (right + left)/2;

    /* Inversion count will be sum of inversions in left-part, right-part
      and number of inversions in merging */
    inv_count  = _mergeSort(arr, temp, left, mid);
    inv_count += _mergeSort(arr, temp, mid+1, right);

    /*Merge the two parts*/
    inv_count += merge(arr, temp, left, mid+1, right);
  }
  return inv_count;
}

/* This funt merges two sorted arrays and returns inversion count in
   the arrays.*/
int merge(int arr[], int temp[], int left, int mid, int right)
{
  int i, j, k;
  int inv_count = 0;

  i = left; /* i is index for left subarray*/
  j = mid;  /* i is index for right subarray*/
  k = left; /* i is index for resultant merged subarray*/
  while ((i <= mid - 1) && (j <= right))
  {
    if (arr[i] <= arr[j])
    {
      temp[k++] = arr[i++];
    }
    else
    {
      temp[k++] = arr[j++];

     /*this is tricky -- see above explanation/diagram for merge()*/
      inv_count = inv_count + (mid - i);
    }
  }

  /* Copy the remaining elements of left subarray
   (if there are any) to temp*/
  while (i <= mid - 1)
    temp[k++] = arr[i++];

  /* Copy the remaining elements of right subarray
   (if there are any) to temp*/
  while (j <= right)
    temp[k++] = arr[j++];

  /*Copy back the merged elements to original array*/
  for (i=left; i <= right; i++)
    arr[i] = temp[i];

  return inv_count;
}

/* Driver progra to test above functions */
int main(int argv, char** args)
{
  int arr[] = {1, 20, 6, 4, 5};
  printf(" Number of inversions are %d \n", mergeSort(arr, 5));
  getchar();
  return 0;
}

Здесь было подробно объяснено: http://www.geeksforgeeks.org/counting-inversions/

Ответ 14

Здесь O (n * log (n)) реализация perl:

sub sort_and_count {
    my ($arr, $n) = @_;
    return ($arr, 0) unless $n > 1;

    my $mid = $n % 2 == 1 ? ($n-1)/2 : $n/2;
    my @left = @$arr[0..$mid-1];
    my @right = @$arr[$mid..$n-1];

    my ($sleft, $x) = sort_and_count( \@left, $mid );
    my ($sright, $y) = sort_and_count( \@right, $n-$mid);
    my ($merged, $z) = merge_and_countsplitinv( $sleft, $sright, $n );

    return ($merged, $x+$y+$z);
}

sub merge_and_countsplitinv {
    my ($left, $right, $n) = @_;

    my ($l_c, $r_c) = ($#$left+1, $#$right+1);
    my ($i, $j) = (0, 0);
    my @merged;
    my $inv = 0;

    for my $k (0..$n-1) {
        if ($i<$l_c && $j<$r_c) {
            if ( $left->[$i] < $right->[$j]) {
                push @merged, $left->[$i];
                $i+=1;
            } else {
                push @merged, $right->[$j];
                $j+=1;
                $inv += $l_c - $i;
            }
        } else {
            if ($i>=$l_c) {
                push @merged, @$right[ $j..$#$right ];
            } else {
                push @merged, @$left[ $i..$#$left ];
            }
            last;
        }
    }

    return (\@merged, $inv);
}

Ответ 15

Легкий ответ O (n ^ 2) заключается в использовании вложенных for-loops и приращения счетчика для каждой инверсии

int counter = 0;

for(int i = 0; i < n - 1; i++)
{
    for(int j = i+1; j < n; j++)
    {
        if( A[i] > A[j] )
        {
            counter++;
        }
    }
}

return counter;

Теперь я полагаю, что вы хотите более эффективное решение, я подумаю об этом.

Ответ 16

Поскольку это старый вопрос, я предоставлю свой ответ в C.

#include <stdio.h>

int count = 0;
int inversions(int a[], int len);
void mergesort(int a[], int left, int right);
void merge(int a[], int left, int mid, int right);

int main() {
  int a[] = { 1, 5, 2, 4, 0 };
  printf("%d\n", inversions(a, 5));
}

int inversions(int a[], int len) {
  mergesort(a, 0, len - 1);
  return count;
}

void mergesort(int a[], int left, int right) {
  if (left < right) {
     int mid = (left + right) / 2;
     mergesort(a, left, mid);
     mergesort(a, mid + 1, right);
     merge(a, left, mid, right);
  }
}

void merge(int a[], int left, int mid, int right) {
  int i = left;
  int j = mid + 1;
  int k = 0;
  int b[right - left + 1];
  while (i <= mid && j <= right) {
     if (a[i] <= a[j]) {
       b[k++] = a[i++];
     } else {
       printf("right element: %d\n", a[j]);
       count += (mid - i + 1);
       printf("new count: %d\n", count);
       b[k++] = a[j++];
     }
  }
  while (i <= mid)
    b[k++] = a[i++];
  while (j <= right)
    b[k++] = a[j++];
  for (i = left, k = 0; i <= right; i++, k++) {
    a[i] = b[k];
  }
}

Ответ 17

Одно возможное решение в С++, удовлетворяющее требованию сложности по времени O (N * log (N)), будет следующим:

#include <algorithm>

vector<int> merge(vector<int>left, vector<int>right, int &counter)
{

    vector<int> result;

    vector<int>::iterator it_l=left.begin();
    vector<int>::iterator it_r=right.begin();

    int index_left=0;

    while(it_l!=left.end() || it_r!=right.end())
    {

        // the following is true if we are finished with the left vector 
        // OR if the value in the right vector is the smaller one.

        if(it_l==left.end() || (it_r!=right.end() && *it_r<*it_l) )
        {
            result.push_back(*it_r);
            it_r++;

            // increase inversion counter
            counter+=left.size()-index_left;
        }
        else
        {
            result.push_back(*it_l);
            it_l++;
            index_left++;

        }
    }

    return result;
}

vector<int> merge_sort_and_count(vector<int> A, int &counter)
{

    int N=A.size();
    if(N==1)return A;

    vector<int> left(A.begin(),A.begin()+N/2);
    vector<int> right(A.begin()+N/2,A.end());

    left=merge_sort_and_count(left,counter);
    right=merge_sort_and_count(right,counter);


    return merge(left, right, counter);

}

Он отличается от обычной сортировки слиянием только счетчиком.

Ответ 18

Другое решение Python, короткое. Использует встроенный модуль bisect, который предоставляет функции для вставки элемента на свое место в отсортированном массиве и для поиска индекса элемента в отсортированном массиве.

Идея состоит в том, чтобы хранить элементы, оставшиеся от n-го в таком массиве, что позволило бы нам легко найти число из них больше n-го.

import bisect
def solution(A):
    sorted_left = []
    res = 0
    for i in xrange(1, len(A)):
        bisect.insort_left(sorted_left, A[i-1])
        # i is also the length of sorted_left
        res += (i - bisect.bisect(sorted_left, A[i]))
    return res

Ответ 19

O (n log n) время, O (n) пространственное решение в java.

Слияние, с настройкой для сохранения количества инверсий, выполняемых во время шага слияния. (для хорошо объясненного mergesort посмотрите http://www.vogella.com/tutorials/JavaAlgorithmsMergesort/article.html)

Поскольку слияние может быть выполнено на месте, сложность пространства может быть улучшена до O (1).

При использовании этого вида инверсии происходят только на шаге слияния и только тогда, когда мы должны поместить элемент второй части перед элементами из первой половины, например.

  • 0 5 10 15

сливается с

  • 1 6 22

имеем 3 + 2 + 0 = 5 инверсий:

  • 1 с {5, 10, 15}
  • 6 с {10, 15}
  • 22 с {}

После того, как мы сделали 5 инверсий, наш новый объединенный список 0, 1, 5, 6, 10, 15, 22

В Codility есть демо-задача, называемая ArrayInversionCount, где вы можете протестировать свое решение.

    public class FindInversions {

    public static int solution(int[] input) {
        if (input == null)
            return 0;
        int[] helper = new int[input.length];
        return mergeSort(0, input.length - 1, input, helper);
    }

    public static int mergeSort(int low, int high, int[] input, int[] helper) {
        int inversionCount = 0;
        if (low < high) {
            int medium = low + (high - low) / 2;
            inversionCount += mergeSort(low, medium, input, helper);
            inversionCount += mergeSort(medium + 1, high, input, helper);
            inversionCount += merge(low, medium, high, input, helper);
        }
        return inversionCount;
    }

    public static int merge(int low, int medium, int high, int[] input, int[] helper) {
        int inversionCount = 0;

        for (int i = low; i <= high; i++)
            helper[i] = input[i];

        int i = low;
        int j = medium + 1;
        int k = low;

        while (i <= medium && j <= high) {
            if (helper[i] <= helper[j]) {
                input[k] = helper[i];
                i++;
            } else {
                input[k] = helper[j];
                // the number of elements in the first half which the j element needs to jump over.
                // there is an inversion between each of those elements and j.
                inversionCount += (medium + 1 - i);
                j++;
            }
            k++;
        }

        // finish writing back in the input the elements from the first part
        while (i <= medium) {
            input[k] = helper[i];
            i++;
            k++;
        }
        return inversionCount;
    }

}

Ответ 20

Мой ответ в Python:

1- Сначала отсортируйте массив и сделайте его копию. В моей программе B представляет отсортированный массив. 2- Итерации по исходному массиву (несортированные) и найти индекс этого элемента в отсортированном списке. Также запишите индекс элемента. 3- Убедитесь, что у элемента нет дубликатов, если это необходимо, вам нужно изменить значение вашего индекса на -1. Условие while в моей программе точно делает это. 4- Продолжайте считать инверсию, которая будет иметь значение вашего индекса, и удалите элемент, как только вы рассчитали его инверсию.

def binarySearch(alist, item):
    first = 0
    last = len(alist) - 1
    found = False

    while first <= last and not found:
        midpoint = (first + last)//2
        if alist[midpoint] == item:
            return midpoint
        else:
            if item < alist[midpoint]:
                last = midpoint - 1
            else:
                first = midpoint + 1

def solution(A):

    B = list(A)
    B.sort()
    inversion_count = 0
    for i in range(len(A)):
        j = binarySearch(B, A[i])
        while B[j] == B[j - 1]:
            if j < 1:
                break
            j -= 1

        inversion_count += j
        B.pop(j)

    if inversion_count > 1000000000:
        return -1
    else:
        return inversion_count

print solution([4, 10, 11, 1, 3, 9, 10])

Ответ 21

Ну, у меня другое решение, но я боюсь, что это будет работать только для отдельных элементов массива.

//Code
#include <bits/stdc++.h>
using namespace std;

int main()
{
    int i,n;
    cin >> n;
    int arr[n],inv[n];
    for(i=0;i<n;i++){
        cin >> arr[i];
    }
    vector<int> v;
    v.push_back(arr[n-1]);
    inv[n-1]=0;
    for(i=n-2;i>=0;i--){
        auto it = lower_bound(v.begin(),v.end(),arr[i]); 
        //calculating least element in vector v which is greater than arr[i]
        inv[i]=it-v.begin();
        //calculating distance from starting of vector
        v.insert(it,arr[i]);
        //inserting that element into vector v
    }
    for(i=0;i<n;i++){
        cout << inv[i] << " ";
    }
    cout << endl;
    return 0;
}

Чтобы объяснить мой код, мы продолжаем добавлять элементы с конца массива. Для любого входящего элемента массива мы находим индекс первого элемента в векторе v, который больше нашего входящего элемента, и присваиваем ему значение к числу инверсий индекса входящего элемента. После этого мы вставляем этот элемент в вектор v в его правильное положение, так что вектор v остается в отсортированном порядке.

//INPUT     
4
2 1 4 3

//OUTPUT    
1 0 1 0

//To calculate total inversion count just add up all the elements in output array

Ответ 22

Основная цель этого ответа - сравнить скорости различных версий Python, найденных здесь, но у меня также есть несколько собственных вкладов. (FWIW, я только что обнаружил этот вопрос при повторном поиске).

Относительные скорости выполнения алгоритмов, реализованных в CPython, могут отличаться от того, что можно было бы ожидать от простого анализа алгоритмов и опыта других языков. Это потому, что Python предоставляет множество мощных функций и методов, реализованных в C, которые могут работать с списками и другими коллекциями, близкими к скорости, которые можно было бы получить на полностью скомпилированном языке, поэтому эти операции выполняются намного быстрее, чем эквивалентные алгоритмы, реализованные "вручную" с помощью Python код.

Код, который использует эти инструменты, может часто превосходить теоретически превосходные алгоритмы, которые пытаются делать все с помощью операций Python по отдельным элементам коллекции. Разумеется, фактическое количество обрабатываемых данных также влияет на это. Но для умеренных объемов данных код, который использует алгоритм O (n²), работающий на скорости C, может легко выполнить алгоритм O (n log n), который выполняет основную часть своей работы с отдельными операциями Python.

Многие из опубликованных ответов на этот вопрос с учетом инверсии используют алгоритм, основанный на mergesort. Теоретически это хороший подход, если размер массива не очень мал. Но Python встроен TimSort (гибридный стабильный алгоритм сортировки, полученный из сортировки слияния и сортировки вставки) работает со скоростью C, а mergesort, закодированный вручную в Python, не может надеяться конкурировать с ним за скорость.

Одно из более интригующих решений здесь, в ответе, отправленном Niklas B, использует встроенную сортировку для определения ранжирования элементов массива и Двоичное индексированное дерево (ака Fenwick tree) для хранения совокупных сумм, необходимых для вычисления количества инверсий. В процессе попытки понять эту структуру данных и алгоритм Niklas я написал несколько собственных вариантов (опубликовано ниже). Но я также обнаружил, что для умеренных размеров списка на самом деле быстрее использовать встроенную функцию sum Python, чем прекрасное дерево Фенвика.

def count_inversions(a):
    total = 0
    counts = [0] * len(a)
    rank = {v: i for i, v in enumerate(sorted(a))}
    for u in reversed(a):
        i = rank[u]
        total += sum(counts[:i])
        counts[i] += 1
    return total

В конце концов, когда размер списка приближается к 500, аспект O (n²) вызова sum внутри этого цикла for возвращает свою уродливую голову, и производительность начинает падать.

Mergesort не является единственным типом O (nlogn), и некоторые другие могут использоваться для выполнения подсчета инверсии. prasadvk answer использует двоичный древовидный вид, однако его код, похоже, находится на С++ или одной из его производных. Поэтому я добавил версию Python. Первоначально я использовал класс для реализации узлов дерева, но обнаружил, что dict значительно быстрее. В конечном итоге я использовал список, который еще быстрее, хотя и делает код немного менее удобочитаемым.

Один бонус дерева - это то, что его намного проще реализовать итеративно, чем mergesort. Python не оптимизирует рекурсию и имеет ограничение глубины рекурсии (хотя это может быть увеличено, если вам это действительно нужно). И, конечно, вызовы функций Python относительно медленны, поэтому, когда вы пытаетесь оптимизировать скорость, полезно избегать вызовов функций, когда это возможно.

Другой тип O (nlogn) - это почтенная сортировка радикса. Большим преимуществом является то, что он не сравнивает ключи друг с другом. Недостатком является то, что он лучше всего работает на непрерывных последовательностях целых чисел, в идеале - перестановке целых чисел в range(b**m), где b обычно равно 2. Я добавил несколько версий, основанных на сортировке radix, после попытки прочитать Подсчет пересчета, автономный подсчет ортогонального диапазона и связанные с ним проблемы, который связан в вычислении числа "инверсий" в перестановке.

Чтобы эффективно использовать сортировку radix для подсчета инверсий в общей последовательности seq длины n, мы можем создать перестановку range(n), которая имеет такое же количество инверсий, что и seq. Мы можем сделать это в (в худшем случае) O (nlogn) времени через TimSort. Трюк состоит в том, чтобы переставить индексы seq, отсортировав seq. Это проще объяснить небольшим примером.

seq = [15, 14, 11, 12, 10, 13]
b = [t[::-1] for t in enumerate(seq)]
print(b)
b.sort()
print(b)

Выход

[(15, 0), (14, 1), (11, 2), (12, 3), (10, 4), (13, 5)]
[(10, 4), (11, 2), (12, 3), (13, 5), (14, 1), (15, 0)]

Сопоставляя пары (значение, индекс) seq, мы перестановили индексы seq с тем же числом свопов, которые должны были поместить seq в исходный порядок из упорядоченного порядка. Мы можем создать эту перестановку, отсортировав range(n) с помощью подходящей ключевой функции:

print(sorted(range(len(seq)), key=lambda k: seq[k]))

Выход

[4, 2, 3, 5, 1, 0]

Мы можем избежать этого lambda с помощью метода seq .__getitem__:

sorted(range(len(seq)), key=seq.__getitem__)

Это только немного быстрее, но мы ищем все улучшения скорости, которые мы можем получить.;)


В приведенном ниже коде timeit тестируются все существующие алгоритмы Python на этой странице, а также несколько моих собственных: пару версий O (n²) грубой силы, несколько вариаций алгоритма Niklas B и, конечно, один на основе mergesort (который я написал, не ссылаясь на существующие ответы). У этого также есть свой кодекс дерева, основанный на списке, приблизительно полученный из кода prasadvk и различные функции, основанные на сортировке radix, некоторые из которых используют аналогичную стратегию для подходов слияния, а некоторые используют sum или дерево Fenwick.

Эта программа измеряет время выполнения каждой функции в серии случайных списков целых чисел; он также может проверить, что каждая функция дает те же результаты, что и другие, и что она не изменяет список ввода.

Каждый вызов timeit дает вектор, содержащий 3 результата, которые я сортирую. Основное значение, которое нужно посмотреть здесь, является минимальным, другие значения просто указывают на то, насколько надежным является это минимальное значение, как описано в примечании в timeit docs.

К сожалению, выход из этой программы слишком велик, чтобы включить в этот ответ, поэтому я отправляю его в свой собственный ответ сообщества ().

Выход из 3-х прогонов на моем старинном 32-битном одноядерном 2ГГц компьютере, на котором запущен Python 3.6.0 на старом дистрибутиве Debian. YMMV. Во время тестов я отключил свой веб-браузер и отключился от своего маршрутизатора, чтобы свести к минимуму влияние других задач на CPU.

Первый запуск проверяет все функции с размерами списка от 5 до 320, с размерами циклов от 4096 до 64 (при удвоении размера списка размер петли уменьшается вдвое). Случайный пул, используемый для построения каждого списка, составляет половину размера самого списка, поэтому мы, вероятно, получим много дубликатов. Некоторые из алгоритмов подсчета инверсии более чувствительны к дубликатам, чем другие.

Во втором прогоне используются более крупные списки: от 640 до 10240 и фиксированный размер шага 8. Чтобы сэкономить время, он устраняет несколько самых медленных функций из тестов. Мои функции O (n²) грубой силы слишком медленны при этих размерах, и, как упоминалось ранее, мой код, который использует sum, который так хорошо работает в списках с небольшими или умеренными значениями, просто не может идти в ногу с большими списками.

Заключительный прогон охватывает размеры списка от 20480 до 655360 и фиксированный размер петли 4, с 8 самыми быстрыми функциями. Для размеров списка до 40 000 или около того Тим Бэндч код является явным победителем. Хорошо, Тим! Код Niklas B - хороший универсальный исполнитель, хотя его избивают в меньших списках. Кодировка "python" на основе биссекции также неплохо работает, хотя она выглядит немного медленнее с огромными списками с большим количеством дубликатов, вероятно, из-за этого линейного цикла while, который он использует для перебора обманов.

Однако при очень больших размерах списка алгоритмы, основанные на бисекции, не могут конкурировать с истинными алгоритмами O (nlogn).

#!/usr/bin/env python3

''' Test speeds of various ways of counting inversions in a list

    The inversion count is a measure of how sorted an array is.
    A pair of items in a are inverted if i < j but a[j] > a[i]

    See https://stackoverflow.com/questions/337664/counting-inversions-in-an-array

    This program contains code by the following authors:
    mkso
    Niklas B
    B. M.
    Tim Babych
    python
    Zhe Hu
    prasadvk
    noman pouigt
    PM 2Ring

    Timing and verification code by PM 2Ring
    Collated 2017.12.16
    Updated 2017.12.21
'''

from timeit import Timer
from random import seed, randrange
from bisect import bisect, insort_left

seed('A random seed string')

# Merge sort version by mkso
def count_inversion_mkso(lst):
    return merge_count_inversion(lst)[1]

def merge_count_inversion(lst):
    if len(lst) <= 1:
        return lst, 0
    middle = len(lst) // 2
    left, a = merge_count_inversion(lst[:middle])
    right, b = merge_count_inversion(lst[middle:])
    result, c = merge_count_split_inversion(left, right)
    return result, (a + b + c)

def merge_count_split_inversion(left, right):
    result = []
    count = 0
    i, j = 0, 0
    left_len = len(left)
    while i < left_len and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            count += left_len - i
            j += 1
    result += left[i:]
    result += right[j:]
    return result, count

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Using a Binary Indexed Tree, aka a Fenwick tree, by Niklas B.
def count_inversions_NiklasB(a):
    res = 0
    counts = [0] * (len(a) + 1)
    rank = {v: i for i, v in enumerate(sorted(a), 1)}
    for x in reversed(a):
        i = rank[x] - 1
        while i:
            res += counts[i]
            i -= i & -i
        i = rank[x]
        while i <= len(a):
            counts[i] += 1
            i += i & -i
    return res

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Merge sort version by B.M
# Modified by PM 2Ring to deal with the global counter
bm_count = 0

def merge_count_BM(seq):
    global bm_count
    bm_count = 0
    sort_bm(seq)
    return bm_count

def merge_bm(l1,l2):
    global bm_count
    l = []
    while l1 and l2:
        if l1[-1] <= l2[-1]:
            l.append(l2.pop())
        else:
            l.append(l1.pop())
            bm_count += len(l2)
    l.reverse()
    return l1 + l2 + l

def sort_bm(l):
    t = len(l) // 2
    return merge_bm(sort_bm(l[:t]), sort_bm(l[t:])) if t > 0 else l

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Bisection based method by Tim Babych
def solution_TimBabych(A):
    sorted_left = []
    res = 0
    for i in range(1, len(A)):
        insort_left(sorted_left, A[i-1])
        # i is also the length of sorted_left
        res += (i - bisect(sorted_left, A[i]))
    return res

# Slightly faster, except for very small lists
def solutionE_TimBabych(A):
    res = 0
    sorted_left = []
    for i, u in enumerate(A):
        # i is also the length of sorted_left
        res += (i - bisect(sorted_left, u))
        insort_left(sorted_left, u)
    return res

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Bisection based method by "python"
def solution_python(A):
    B = list(A)
    B.sort()
    inversion_count = 0
    for i in range(len(A)):
        j = binarySearch_python(B, A[i])
        while B[j] == B[j - 1]:
            if j < 1:
                break
            j -= 1
        inversion_count += j
        B.pop(j)
    return inversion_count

def binarySearch_python(alist, item):
    first = 0
    last = len(alist) - 1
    found = False
    while first <= last and not found:
        midpoint = (first + last) // 2
        if alist[midpoint] == item:
            return midpoint
        else:
            if item < alist[midpoint]:
                last = midpoint - 1
            else:
                first = midpoint + 1

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Merge sort version by Zhe Hu
def inv_cnt_ZheHu(a):
    _, count = inv_cnt(a.copy())
    return count

def inv_cnt(a):
    n = len(a)
    if n==1:
        return a, 0
    left = a[0:n//2] # should be smaller
    left, cnt1 = inv_cnt(left)
    right = a[n//2:] # should be larger
    right, cnt2 = inv_cnt(right)

    cnt = 0
    i_left = i_right = i_a = 0
    while i_a < n:
        if (i_right>=len(right)) or (i_left < len(left)
            and left[i_left] <= right[i_right]):
            a[i_a] = left[i_left]
            i_left += 1
        else:
            a[i_a] = right[i_right]
            i_right += 1
            if i_left < len(left):
                cnt += len(left) - i_left
        i_a += 1
    return (a, cnt1 + cnt2 + cnt)

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# Merge sort version by noman pouigt
# From https://stackoverflow.com/q/47830098
def reversePairs_nomanpouigt(nums):
    def merge(left, right):
        if not left or not right:
            return (0, left + right)
        #if everything in left is less than right
        if left[len(left)-1] < right[0]:
            return (0, left + right)
        else:
            left_idx, right_idx, count = 0, 0, 0
            merged_output = []

            # check for condition before we merge it
            while left_idx < len(left) and right_idx < len(right):
                #if left[left_idx] > 2 * right[right_idx]:
                if left[left_idx] > right[right_idx]:
                    count += len(left) - left_idx
                    right_idx += 1
                else:
                    left_idx += 1

            #merging the sorted list
            left_idx, right_idx = 0, 0
            while left_idx < len(left) and right_idx < len(right):
                if left[left_idx] > right[right_idx]:
                    merged_output += [right[right_idx]]
                    right_idx += 1
                else:
                    merged_output += [left[left_idx]]
                    left_idx += 1
            if left_idx == len(left):
                merged_output += right[right_idx:]
            else:
                merged_output += left[left_idx:]
        return (count, merged_output)

    def partition(nums):
        count = 0
        if len(nums) == 1 or not nums:
            return (0, nums)
        pivot = len(nums)//2
        left_count, l = partition(nums[:pivot])
        right_count, r = partition(nums[pivot:])
        temp_count, temp_list = merge(l, r)
        return (temp_count + left_count + right_count, temp_list)
    return partition(nums)[0]

# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
# PM 2Ring
def merge_PM2R(seq):
    seq, count = merge_sort_count_PM2R(seq)
    return count

def merge_sort_count_PM2R(seq):
    mid = len(seq) // 2
    if mid == 0:
        return seq, 0
    left, left_total = merge_sort_count_PM2R(seq[:mid])
    right, right_total = merge_sort_count_PM2R(seq[mid:])
    total = left_total + right_total
    result = []
    i = j = 0
    left_len, right_len = len(left), len(right)
    while i < left_len and j < right_len:
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
            total += left_len - i
    result.extend(left[i:])
    result.extend(right[j:])
    return result, total

def rank_sum_PM2R(a):
    total = 0
    counts = [0] * len(a)
    rank = {v: i for i, v in enumerate(sorted(a))}
    for u in reversed(a):
        i = rank[u]
        total += sum(counts[:i])
        counts[i] += 1
    return total

# Fenwick tree functions adapted from C code on Wikipedia
def fen_sum(tree, i):
    ''' Return the sum of the first i elements, 0 through i-1 '''
    total = 0
    while i:
        total += tree[i-1]
        i -= i & -i
    return total

def fen_add(tree, delta, i):
    ''' Add delta to element i and thus 
        to fen_sum(tree, j) for all j > i 
    '''
    size = len(tree)
    while i < size:
        tree[i] += delta
        i += (i+1) & -(i+1)

def fenwick_PM2R(a):
    total = 0
    counts = [0] * len(a)
    rank = {v: i for i, v in enumerate(sorted(a))}
    for u in reversed(a):
        i = rank[u]
        total += fen_sum(counts, i)
        fen_add(counts, 1, i)
    return total

def fenwick_inline_PM2R(a):
    total = 0
    size = len(a)
    counts = [0] * size
    rank = {v: i for i, v in enumerate(sorted(a))}
    for u in reversed(a):
        i = rank[u]
        j = i + 1
        while i:
            total += counts[i]
            i -= i & -i
        while j < size:
            counts[j] += 1
            j += j & -j
    return total

def bruteforce_loops_PM2R(a):
    total = 0
    for i in range(1, len(a)):
        u = a[i]
        for j in range(i):
            if a[j] > u:
                total += 1
    return total

def bruteforce_sum_PM2R(a):
    return sum(1 for i in range(1, len(a)) for j in range(i) if a[j] > a[i])

# Using binary tree counting, derived from C++ code (?) by prasadvk
# /questions/39217/counting-inversions-in-an-array/284452#284452
def ltree_count_PM2R(a):
    total, root = 0, None
    for u in a:
        # Store data in a list-based tree structure
        # [data, count, left_child, right_child]
        p = [u, 0, None, None]
        if root is None:
            root = p
            continue
        q = root
        while True:
            if p[0] < q[0]:
                total += 1 + q[1]
                child = 2
            else:
                q[1] += 1
                child = 3
            if q[child]:
                q = q[child]
            else:
                q[child] = p
                break
    return total

# Counting based on radix sort, recursive version
def radix_partition_rec(a, L):
    if len(a) < 2:
        return 0
    if len(a) == 2:
        return a[1] < a[0]
    left, right = [], []
    count = 0
    for u in a:
        if u & L:
            right.append(u)
        else:
            count += len(right)
            left.append(u)
    L >>= 1
    if L:
        count += radix_partition_rec(left, L) + radix_partition_rec(right, L)
    return count

# The following functions determine swaps using a permutation of 
# range(len(a)) that has the same inversion count as `a`. We can create
# this permutation with `sorted(range(len(a)), key=lambda k: a[k])`
# but `sorted(range(len(a)), key=a.__getitem__)` is a little faster.

# Counting based on radix sort, iterative version
def radix_partition_iter(seq, L):
    count = 0
    parts = [seq]
    while L and parts:
        newparts = []
        for a in parts:
            if len(a) < 2:
                continue
            if len(a) == 2:
                count += a[1] < a[0]
                continue
            left, right = [], []
            for u in a:
                if u & L:
                    right.append(u)
                else:
                    count += len(right)
                    left.append(u)
            if left:
                newparts.append(left)
            if right:
                newparts.append(right)
        parts = newparts
        L >>= 1
    return count

def perm_radixR_PM2R(a):
    size = len(a)
    b = sorted(range(size), key=a.__getitem__)
    n = size.bit_length() - 1
    return radix_partition_rec(b, 1 << n)

def perm_radixI_PM2R(a):
    size = len(a)
    b = sorted(range(size), key=a.__getitem__)
    n = size.bit_length() - 1
    return radix_partition_iter(b, 1 << n)

# Plain sum of the counts of the permutation
def perm_sum_PM2R(a):
    total = 0
    size = len(a)
    counts = [0] * size
    for i in reversed(sorted(range(size), key=a.__getitem__)):
        total += sum(counts[:i])
        counts[i] = 1
    return total

# Fenwick sum of the counts of the permutation
def perm_fenwick_PM2R(a):
    total = 0
    size = len(a)
    counts = [0] * size
    for i in reversed(sorted(range(size), key=a.__getitem__)):
        j = i + 1
        while i:
            total += counts[i]
            i -= i & -i
        while j < size:
            counts[j] += 1
            j += j & -j
    return total

# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# All the inversion-counting functions
funcs = (
    solution_TimBabych,
    solutionE_TimBabych,
    solution_python,
    count_inversion_mkso,
    count_inversions_NiklasB,
    merge_count_BM,
    inv_cnt_ZheHu,
    reversePairs_nomanpouigt,
    fenwick_PM2R,
    fenwick_inline_PM2R,
    merge_PM2R,
    rank_sum_PM2R,
    bruteforce_loops_PM2R,
    bruteforce_sum_PM2R,
    ltree_count_PM2R,
    perm_radixR_PM2R,
    perm_radixI_PM2R,
    perm_sum_PM2R,
    perm_fenwick_PM2R,
)

def time_test(seq, loops, verify=False):
    orig = seq
    timings = []
    for func in funcs:
        seq = orig.copy()
        value = func(seq) if verify else None
        t = Timer(lambda: func(seq))
        result = sorted(t.repeat(3, loops))
        timings.append((result, func.__name__, value))
        assert seq==orig, 'Sequence altered by {}!'.format(func.__name__)
    first = timings[0][-1]
    timings.sort()
    for result, name, value in timings:
        result = ', '.join([format(u, '.5f') for u in result])
        print('{:24} : {}'.format(name, result))

    if verify:
        # Check that all results are identical
        bad = ['%s: %d' % (name, value)
            for _, name, value in timings if value != first]
        if bad:
            print('ERROR. Value: {}, bad: {}'.format(first, ', '.join(bad)))
        else:
            print('Value: {}'.format(first))
    print()

#Run the tests
size, loops = 5, 1 << 12
verify = True
for _ in range(7):
    hi = size // 2
    print('Size = {}, hi = {}, {} loops'.format(size, hi, loops))
    seq = [randrange(hi) for _ in range(size)]
    time_test(seq, loops, verify)
    loops >>= 1
    size <<= 1

#size, loops = 640, 8
#verify = False
#for _ in range(5):
    #hi = size // 2
    #print('Size = {}, hi = {}, {} loops'.format(size, hi, loops))
    #seq = [randrange(hi) for _ in range(size)]
    #time_test(seq, loops, verify)
    #size <<= 1

#size, loops = 163840, 4
#verify = False
#for _ in range(3):
    #hi = size // 2
    #print('Size = {}, hi = {}, {} loops'.format(size, hi, loops))
    #seq = [randrange(hi) for _ in range(size)]
    #time_test(seq, loops, verify)
    #size <<= 1

Подробнее см. здесь

Ответ 23

Я недавно должен был сделать это в R:

inversionNumber <- function(x){
    mergeSort <- function(x){
        if(length(x) == 1){
            inv <- 0
        } else {
            n <- length(x)
            n1 <- ceiling(n/2)
            n2 <- n-n1
            y1 <- mergeSort(x[1:n1])
            y2 <- mergeSort(x[n1+1:n2])
            inv <- y1$inversions + y2$inversions
            x1 <- y1$sortedVector
            x2 <- y2$sortedVector
            i1 <- 1
            i2 <- 1
            while(i1+i2 <= n1+n2+1){
                if(i2 > n2 || i1 <= n1 && x1[i1] <= x2[i2]){
                    x[i1+i2-1] <- x1[i1]
                    i1 <- i1 + 1
                } else {
                    inv <- inv + n1 + 1 - i1
                    x[i1+i2-1] <- x2[i2]
                    i2 <- i2 + 1
                }
            }
        }
        return (list(inversions=inv,sortedVector=x))
    }
    r <- mergeSort(x)
    return (r$inversions)
}

Ответ 24

Реализация Java:

import java.lang.reflect.Array;
import java.util.Arrays;


public class main {

public static void main(String[] args) {
    int[] arr = {6, 9, 1, 14, 8, 12, 3, 2};
    System.out.println(findinversion(arr,0,arr.length-1));
}

public static int findinversion(int[] arr,int beg,int end) {
    if(beg >= end)
        return 0;

    int[] result = new int[end-beg+1];
    int index = 0;
    int mid = (beg+end)/2;
    int count = 0, leftinv,rightinv;
    //System.out.println("...."+beg+"   "+end+"  "+mid);
    leftinv = findinversion(arr, beg, mid);
    rightinv = findinversion(arr, mid+1, end);
    l1:
    for(int i = beg, j = mid+1; i<=mid || j<=end;/*index < result.length;*/ ) {
        if(i>mid) {
            for(;j<=end;j++)
                result[index++]=arr[j];
            break l1;
        }
        if(j>end) {
            for(;i<=mid;i++)
                result[index++]=arr[i];
            break l1;
        }
        if(arr[i] <= arr[j]) {
            result[index++]=arr[i];
            i++;    
        } else {
            System.out.println(arr[i]+"  "+arr[j]);
            count = count+ mid-i+1;
            result[index++]=arr[j];
            j++;    
        }
    }

    for(int i = 0, j=beg; i< end-beg+1; i++,j++)
        arr[j]= result[i];
    return (count+leftinv+rightinv);
    //System.out.println(Arrays.toString(arr));
}

}

Ответ 25

Вот мой прием с помощью Scala:

trait MergeSort {
  def mergeSort(ls: List[Int]): List[Int] = {
    def merge(ls1: List[Int], ls2: List[Int]): List[Int] =
      (ls1, ls2) match {
        case (_, Nil) => ls1
        case (Nil, _) => ls2
        case (lowsHead :: lowsTail, highsHead :: highsTail) =>
          if (lowsHead <= highsHead) lowsHead :: merge(lowsTail, ls2)
          else highsHead :: merge(ls1, highsTail)
      }

    ls match {
      case Nil => Nil
      case head :: Nil => ls
      case _ =>
        val (lows, highs) = ls.splitAt(ls.size / 2)
        merge(mergeSort(lows), mergeSort(highs))
    }
  }
}

object InversionCounterApp extends App with MergeSort {
  @annotation.tailrec
  def calculate(list: List[Int], sortedListZippedWithIndex: List[(Int, Int)], counter: Int = 0): Int =
    list match {
      case Nil => counter
      case head :: tail => calculate(tail, sortedListZippedWithIndex.filterNot(_._1 == 1), counter + sortedListZippedWithIndex.find(_._1 == head).map(_._2).getOrElse(0))
    }

  val list: List[Int] = List(6, 9, 1, 14, 8, 12, 3, 2)
  val sortedListZippedWithIndex: List[(Int, Int)] = mergeSort(list).zipWithIndex
  println("inversion counter = " + calculate(list, sortedListZippedWithIndex))
  // prints: inversion counter = 28 
}

Ответ 26

Я думаю, что ответ el diablo можно оптимизировать для удаления шага 2b, в котором мы удаляем уже обработанные элементы.

Вместо этого мы можем определить

# инверсии для x = позиция x в отсортированном массиве -                      положение x в исходном массиве

Ответ 27

Код C легко понять:

#include<stdio.h>
#include<stdlib.h>

//To print an array
void print(int arr[],int n)
{
    int i;
    for(i=0,printf("\n");i<n;i++)
        printf("%d ",arr[i]);
    printf("\n");
}

//Merge Sort
int merge(int arr[],int left[],int right[],int l,int r)
{
    int i=0,j=0,count=0;
    while(i<l || j<r)
    {
        if(i==l)
        {
            arr[i+j]=right[j];
            j++;
        }
        else if(j==r)
        {
            arr[i+j]=left[i];
            i++;
        }
        else if(left[i]<=right[j])
        {
            arr[i+j]=left[i];
            i++;
        }
        else
        {
            arr[i+j]=right[j];
            count+=l-i;
            j++;
        }
    }
    //printf("\ncount:%d\n",count);
    return count;
}

//Inversion Finding
int inversions(int arr[],int high)
{
    if(high<1)
        return 0;

    int mid=(high+1)/2;
    int left[mid];
    int right[high-mid+1];

    int i,j;
    for(i=0;i<mid;i++)
        left[i]=arr[i];


    for(i=high-mid,j=high;j>=mid;i--,j--)
        right[i]=arr[j];

    //print(arr,high+1);
    //print(left,mid);
    //print(right,high-mid+1);

    return inversions(left,mid-1) + inversions(right,high-mid) + merge(arr,left,right,mid,high-mid+1);

}
int main()
{
    int arr[]={6,9,1,14,8,12,3,2};
    int n=sizeof(arr)/sizeof(arr[0]);
    print(arr,n);
    printf("%d ",inversions(arr,n-1));
    return 0;
}

Ответ 28

Здесь мое решение O (n log n) в Ruby:

def solution(t)
    sorted, inversion_count = sort_inversion_count(t)
    return inversion_count
end

def sort_inversion_count(t)
    midpoint = t.length / 2
    left_half = t[0...midpoint]
    right_half = t[midpoint..t.length]

    if midpoint == 0
        return t, 0
    end

    sorted_left_half, left_half_inversion_count = sort_inversion_count(left_half)
    sorted_right_half, right_half_inversion_count = sort_inversion_count(right_half)

    sorted = []
    inversion_count = 0
    while sorted_left_half.length > 0 or sorted_right_half.length > 0
        if sorted_left_half.empty?
            sorted.push sorted_right_half.shift
        elsif sorted_right_half.empty?
            sorted.push sorted_left_half.shift
        else
            if sorted_left_half[0] > sorted_right_half[0]
                inversion_count += sorted_left_half.length
                sorted.push sorted_right_half.shift
            else
                sorted.push sorted_left_half.shift
            end
        end
    end

    return sorted, inversion_count + left_half_inversion_count + right_half_inversion_count
end

И некоторые тестовые примеры:

require "minitest/autorun"

class TestCodility < Minitest::Test
    def test_given_example
        a = [-1, 6, 3, 4, 7, 4]
        assert_equal solution(a), 4
    end

    def test_empty
        a = []
        assert_equal solution(a), 0
    end

    def test_singleton
        a = [0]
        assert_equal solution(a), 0
    end

    def test_none
        a = [1,2,3,4,5,6,7]
        assert_equal solution(a), 0
    end

    def test_all
        a = [5,4,3,2,1]
        assert_equal solution(a), 10
    end

    def test_clones
        a = [4,4,4,4,4,4]
        assert_equal solution(a), 0
    end
end

Ответ 29

Оптимальным способом будет решить его путем сортировки слияния, где будет слияние, мы можем проверить, сколько инверсий требуется для сравнения левого и правого массива. Всякий раз, когда элемент слева массива больше элемента в правом массиве, он будет инверсией.

Метод сортировки слияния: -

Вот код. Код точно такой же, как и для сортировки слияния, кроме фрагмента кода в методе mergeToParent, где я рассчитываю инверсию при условии else (left[leftunPicked] < right[rightunPicked])

public class TestInversionThruMergeSort {

    static int count =0;

    public static void main(String[] args) {
        int[] arr = {6, 9, 1, 14, 8, 12, 3, 2};


        partition(arr);

        for (int i = 0; i < arr.length; i++) {

            System.out.println(arr[i]);
        }

        System.out.println("inversions are "+count);

    }

    public static void partition(int[] arr) {

        if (arr.length > 1) {

            int mid = (arr.length) / 2;
            int[] left = null;

            if (mid > 0) {
                left = new int[mid];

                for (int i = 0; i < mid; i++) {
                    left[i] = arr[i];
                }
            }

            int[] right = new int[arr.length - left.length];

            if ((arr.length - left.length) > 0) {
                int j = 0;
                for (int i = mid; i < arr.length; i++) {
                    right[j] = arr[i];
                    ++j;
                }
            }

            partition(left);
            partition(right);
            mergeToParent(left, right, arr);
        }

    }

    public static void mergeToParent(int[] left, int[] right, int[] parent) {

        int leftunPicked = 0;
        int rightunPicked = 0;
        int parentIndex = -1;

        while (rightunPicked < right.length && leftunPicked < left.length) {

            if (left[leftunPicked] < right[rightunPicked]) {
                parent[++parentIndex] = left[leftunPicked];
                ++leftunPicked;

            } else {
                count = count + left.length-leftunPicked;
                if ((rightunPicked < right.length)) {
                    parent[++parentIndex] = right[rightunPicked];
                    ++rightunPicked;
                }
            }

        }

        while (leftunPicked < left.length) {
            parent[++parentIndex] = left[leftunPicked];
            ++leftunPicked;
        }

        while (rightunPicked < right.length) {
            parent[++parentIndex] = right[rightunPicked];
            ++rightunPicked;
        }

    }

}

Другой подход, в котором мы можем сравнить входной массив с отсортированным массивом: - Эта реализация ответа Диабло. Хотя это не должно быть предпочтительным подходом, так как удаление n элементов из массива или списка - log (n ^ 2).

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;


public class TestInversion {

    public static void main(String[] args) {

        Integer [] arr1 = {6, 9, 1, 14, 8, 12, 3, 2};

        List<Integer> arr = new ArrayList(Arrays.asList(arr1));
        List<Integer> sortArr = new ArrayList<Integer>();

        for(int i=0;i<arr.size();i++){
            sortArr.add(arr.get(i));

        }


        Collections.sort(sortArr);

        int inversion = 0;

        Iterator<Integer> iter = arr.iterator();

        while(iter.hasNext()){

            Integer el = (Integer)iter.next();
            int index = sortArr.indexOf(el);

            if(index+1 > 1){
                inversion = inversion + ((index+1)-1);
            }

            //iter.remove();
            sortArr.remove(el);

        }

        System.out.println("Inversions are "+inversion);




    }


}

Ответ 30

другое решение Python

def inv_cnt(a):
n = len(a)
if n==1:
    return a,0
left = a[0:n//2] # should be smaller
left,cnt1 = inv_cnt(left)    
right = a[n//2:] # should be larger
right, cnt2 = inv_cnt(right)

cnt = 0    
i_left = i_right = i_a = 0
while i_a < n:
    if (i_right>=len(right)) or (i_left < len(left) and left[i_left] <= right[i_right]):
        a[i_a] = left[i_left]
        i_left += 1
    else:
        a[i_a] = right[i_right]
        i_right += 1              
        if i_left < len(left):
            cnt += len(left) - i_left        
    i_a += 1    

return (a, (cnt1 + cnt2 + cnt))