답변:
그래서 여기에 Java의 O (n log n) 솔루션이 있습니다.
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);
}
이것은 거의 정상적인 병합 정렬이며, 전체 마법은 병합 기능에 숨겨져 있습니다. 정렬 알고리즘은 반전을 제거합니다. 병합 알고리즘은 제거 된 반전의 수를 계산합니다 (정렬 된 것으로 말할 수 있음).
반전이 제거되는 유일한 순간은 알고리즘이 배열의 오른쪽에서 요소를 가져 와서 기본 배열에 병합하는 경우입니다. 이 작업에 의해 제거 된 반전 수는 병합 할 왼쪽 배열에서 남은 요소 수입니다. :)
충분히 설명하기를 바랍니다.
left.length - i
. 반전 카운터에 추가 하면 어떻게됩니까? 두 하위 배열 간의 비교가 오른쪽 배열보다 왼쪽 배열 요소가 더 큰 논리적 사례에 빠졌기 때문에 1을 더하는 것이 합리적이라고 생각합니다. 누구나 5 살인 것처럼 설명 할 수 있나요?
arr
. 그러나 그것은 하나의 반전이 아닙니다. 6보다 큰 왼쪽 배열의 모든 요소에 대한 반전을 찾았습니다. 우리의 경우에는 8도 포함됩니다. 따라서,도 2에 추가 count
로 동일하다, left.length - i
.
다음 방법으로 O (n * log n) 시간에 발견했습니다.
A [1]을 가져와 이진 검색을 통해 정렬 된 배열 B에서 위치를 찾습니다. 이 요소의 반전 수는 A의 첫 번째 요소 다음에 나타나는 모든 낮은 숫자가 반전이기 때문에 B에서 해당 위치의 인덱스 번호보다 하나 적습니다.
2a. 카운터 변수 num_inversions에 반전 수를 축적합니다.
2b. 배열 A와 배열 B의 해당 위치에서 A [1]을 제거합니다.
다음은이 알고리즘의 실행 예입니다. 원래 배열 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은 배열 B의 4 번째 위치에 있으므로 3 개의 반전이 있습니다. 6이 배열 A의 첫 번째 위치에 있었기 때문에이를 알고 있습니다. 따라서 배열 A에 나중에 나타나는 하위 값 요소는 j> i의 인덱스를 갖게됩니다 (이 경우 i는 1이기 때문에).
2.b : 배열 A와 배열 B의 해당 위치에서 A [1]을 제거합니다 (굵은 요소는 제거됨).
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 : 새 A 및 B 어레이에서 2 단계부터 다시 실행합니다.
A [1] = 9
B = (1, 2, 3, 8, 9, 12, 14)
9는 이제 배열 B의 5 번째 위치에 있으므로 4 개의 반전이 있습니다. 9가 배열 A의 첫 번째 위치에 있었기 때문에이를 알고 있습니다. 따라서 이후에 나타나는 낮은 값 요소는 j> i의 인덱스를 갖습니다 (이 경우 i는 다시 1이기 때문). 배열 A와 배열 B의 해당 위치에서 A [1]을 제거합니다 (굵은 요소는 제거됨).
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 (n * log n)에 대해 실행하는 데 O (log n)를 사용하는 이진 검색을 수행합니다. 따라서 총 실행 시간은 O (n * log n) + O (n * log n) = O (n * log n)입니다.
당신의 도움을 주셔서 감사합니다. 종이에 샘플 배열을 작성하는 것은 문제를 시각화하는 데 정말 도움이되었습니다.
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)
왜 아무도 바이너리 인덱스 트리를 언급하지 않았는지 궁금 합니다. 하나를 사용하여 순열 요소의 값에 대한 접두사 합계를 유지할 수 있습니다. 그런 다음 오른쪽에서 왼쪽으로 진행하고 모든 요소에 대해 오른쪽보다 작은 요소 수를 계산할 수 있습니다.
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)이고 상수 계수는 매우 낮습니다.
i -= i & -i
라인 의 의미는 무엇입니까 ? 그리고 유사i += i & -i
실제로 숙제에 대해 이와 비슷한 질문이있었습니다. O (nlogn) 효율성이 있어야한다는 제한이있었습니다.
Mergesort 사용에 대해 제안한 아이디어는 이미 올바른 효율성이므로 사용했습니다. 기본적으로 병합 함수에 몇 가지 코드를 삽입했습니다. 오른쪽 배열의 숫자가 출력 배열에 추가 될 때마다 총 반전 수, 왼쪽 배열에 남아있는 숫자의 양을 더합니다.
이것에 대해 충분히 생각 했으니 이제는 이치에 맞습니다. 숫자 앞에 더 많은 숫자가 몇 번이나 나오는지 계산합니다.
hth.
이 답변의 주된 목적은 여기에서 찾은 다양한 Python 버전의 속도를 비교하는 것이지만 저도 몇 가지 기여했습니다. (FWIW, 중복 검색을 수행하는 동안이 질문을 발견했습니다).
CPython에서 구현 된 알고리즘의 상대적 실행 속도는 알고리즘의 간단한 분석 및 다른 언어 경험에서 기대하는 것과 다를 수 있습니다. 파이썬은 완전 컴파일 된 언어에서 얻을 수있는 속도에 가까운 속도로 목록 및 기타 컬렉션에서 작동 할 수있는 C로 구현 된 많은 강력한 함수와 메서드를 제공하기 때문에 이러한 작업은 Python으로 "수동으로"구현 된 동일한 알고리즘보다 훨씬 빠르게 실행됩니다. 암호.
이러한 도구를 활용하는 코드는 컬렉션의 개별 항목에 대해 Python 작업으로 모든 작업을 수행하려는 이론적으로 우수한 알고리즘보다 성능이 뛰어난 경우가 많습니다. 물론 처리되는 실제 데이터 양도 이것에 영향을 미칩니다. 그러나 적당한 양의 데이터의 경우 C 속도로 실행되는 O (n²) 알고리즘을 사용하는 코드는 개별 Python 작업으로 대량 작업을 수행하는 O (n log n) 알고리즘을 쉽게 이길 수 있습니다.
이 반전 계산 질문에 대한 많은 게시 된 답변은 mergesort를 기반으로 한 알고리즘을 사용합니다. 이론적으로 이것은 배열 크기가 매우 작지 않는 한 좋은 접근 방식입니다. 그러나 Python의 내장 TimSort (병합 정렬 및 삽입 정렬에서 파생 된 안정적인 하이브리드 정렬 알고리즘)는 C 속도로 실행되며 Python에서 수동으로 코딩 된 병합 정렬은 속도면에서 경쟁 할 수 없습니다.
Niklas B가 게시 한 답변 에서 더 흥미로운 솔루션 중 하나는 내장 정렬을 사용하여 배열 항목의 순위를 결정하고 Binary Indexed Tree (일명 Fenwick 트리)를 사용하여 반전을 계산하는 데 필요한 누적 합계를 저장합니다. 카운트. 이 데이터 구조와 Niklas의 알고리즘을 이해하려는 과정에서 몇 가지 변형을 작성했습니다 (아래 게시 됨). 그러나 나는 또한 적당한 목록 크기의 경우 사랑스러운 Fenwick 트리보다 Python의 내장 함수 를 사용하는 것이 실제로 더 빠르다 는 것을 발견했습니다 sum
.
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 개가 sum
되면 for
루프 내부를 호출하는 O (n²) 측면이 추악한 머리를 뒤로하고 성능이 떨어지기 시작합니다.
Mergesort는 유일한 O (nlogn) 정렬이 아니며, 여러 다른 정렬을 사용하여 반전 계산을 수행 할 수 있습니다. prasadvk의 답변 은 이진 트리 정렬을 사용하지만 그의 코드는 C ++ 또는 그 파생물 중 하나 인 것으로 보입니다. 그래서 파이썬 버전을 추가했습니다. 원래 클래스를 사용하여 트리 노드를 구현했지만 dict가 눈에 띄게 더 빠르다는 것을 발견했습니다. 나는 결국 더 빠른 목록을 사용했지만 코드를 조금 덜 읽기 쉽게 만들었습니다.
treesort의 한 가지 보너스는 mergesort보다 반복적으로 구현하는 것이 훨씬 쉽다는 것입니다. 파이썬은 재귀를 최적화하지 않으며 재귀 깊이 제한이 있습니다 ( 정말 필요한 경우 늘릴 수 있음 ). 물론 Python 함수 호출은 상대적으로 느리기 때문에 속도를 최적화하려는 경우 실용적인 경우 함수 호출을 피하는 것이 좋습니다.
또 다른 O (nlogn) 정렬은 유서 깊은 기수 정렬입니다. 큰 장점은 키를 서로 비교하지 않는다는 것입니다. 그것의 단점은 정수의 연속 시퀀스, 이상적에서 정수의 순열에서 가장 잘 작동한다는 것입니다 range(b**m)
어디에 b
나는 일종의 읽는 후에 기수에 따라 몇 가지 버전을 추가 일반적으로 2 인 계수의 자리 바꿈, 오프라인 직교 범위 카운팅을하고, 관련 문제 입니다 순열에서 '반전'수 를 계산하는 데 연결됩니다 .
기수 정렬을 효과적으로 사용 seq
하여 길이 n 의 일반적인 시퀀스에서 반전을 계산하기 range(n)
위해와 동일한 수의 반전을 갖는 순열을 만들 수 있습니다 seq
. TimSort를 통해 (최악의 경우) O (nlogn) 시간에이를 수행 할 수 있습니다. 트릭은 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의 코드에서 대략적으로 파생 된 목록 기반 트리 정렬 코드와 기수 정렬을 기반으로하는 다양한 기능, 병합 정렬 접근 방식과 유사한 전략을 사용하는 일부, sum
또는 Fenwick 트리를 사용 하는 일부 기능이 있습니다.
이 프로그램은 임의의 정수 목록에서 각 함수의 실행 시간을 측정합니다. 또한 각 함수가 다른 함수와 동일한 결과를 제공하고 입력 목록을 수정하지 않는지 확인할 수 있습니다.
각 timeit
호출은 3 개의 결과를 포함하는 벡터를 제공합니다. 여기서 살펴볼 주요 값은 최소값이고 다른 값 은 timeit
모듈 문서 의 참고에서 논의 된대로 최소값이 얼마나 신뢰할 수 있는지를 나타냅니다 .
불행히도이 프로그램의 출력은이 답변에 포함하기에는 너무 커서 자체 (커뮤니티 위키) 답변에 게시하고 있습니다.
출력은 오래된 Debian 파생 배포판에서 Python 3.6.0을 실행하는 오래된 32 비트 싱글 코어 2GHz 컴퓨터에서 3 번 실행 된 것입니다. YMMV. 테스트 중에 웹 브라우저를 종료하고 다른 작업이 CPU에 미치는 영향을 최소화하기 위해 라우터에서 연결을 끊었습니다.
첫 번째 실행은 목록 크기가 5 ~ 320이고 루프 크기가 4096 ~ 64 인 모든 함수를 테스트합니다 (목록 크기가 두 배가되면 루프 크기가 절반으로 줄어 듭니다). 각 목록을 구성하는 데 사용되는 임의 풀은 목록 자체의 절반 크기이므로 많은 중복 항목 을 얻을 수 있습니다. 반전 계산 알고리즘 중 일부는 다른 것보다 중복에 더 민감합니다.
두 번째 실행에서는 더 큰 목록 (640 ~ 10240)과 고정 루프 크기 8을 사용합니다. 시간을 절약하기 위해 테스트에서 가장 느린 여러 기능을 제거합니다. 내 무차별 O는 (n²) 기능은 있습니다 방법 이 크기에 너무 느리게하고, 이전에 내 코드를 언급 한 바와 같이 사용하는 sum
중간 목록에 작은에 잘하지, 단지 큰 목록에 유지할 수 없습니다.
최종 실행은 20480에서 655360까지의 목록 크기와 8 개의 가장 빠른 기능을 포함하는 고정 루프 크기 4를 다룹니다. 40,000 개 이하의 목록 크기의 경우 Tim Babych의 코드가 확실한 승자입니다. 잘 했어요 Tim! Niklas B의 코드는 더 작은 목록에서 패배하지만 좋은 만능 수행자입니다. "파이썬"의 이분법 기반 코드도 꽤 잘 작동하지만, 중복이 많은 거대한 목록에서는 약간 느려 보이지만, 아마도 복제 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 /programming/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
# https://stackoverflow.com/a/16056139
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
bisect
C 라고 말하는 건가요? 나는 그것이 파이썬이라고 확신합니다.
병합 정렬에서 병합 프로세스를 분석하여 반전 수를 찾을 수 있습니다.
두 번째 배열에서 병합 배열로 요소를 복사 할 때 (이 예제의 9 개) 다른 요소에 비해 상대적으로 위치를 유지합니다. 첫 번째 배열의 요소를 병합 배열 (여기서는 5)로 복사 할 때 모든 요소가 두 번째 배열에 남아있는 상태로 반전됩니다 (3과 4로 2 개의 반전). 따라서 병합 정렬을 약간 수정하면 O (n ln n)의 문제를 해결할 수 있습니다.
예를 들어 아래의 mergesort python 코드에서 # 줄의 주석 처리를 제거하여 개수를 확인하십시오.
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
1 편집
약간 더 빠른 것으로 알려진 안정적인 버전의 빠른 정렬을 사용하여 동일한 작업을 수행 할 수 있습니다.
def part(l):
pivot=l[-1]
small,big = [],[]
count = big_count = 0
for x in l:
if x <= pivot:
small.append(x)
count += big_count
else:
big.append(x)
big_count += 1
return count,small,big
def quick_count(l):
if len(l)<2 : return 0
count,small,big = part(l)
small.pop()
return count + quick_count(small) + quick_count(big)
피벗을 마지막 요소로 선택하면 반전이 잘 계산되며 위의 병합보다 실행 시간이 40 % 더 빠릅니다.
2 편집
파이썬의 성능을 위해 numpy 및 numba 버전 :
먼저 argsort O (n ln n)를 사용하는 numpy 부분 :
def count_inversions(a):
n = a.size
counts = np.arange(n) & -np.arange(n) # The BIT
ags = a.argsort(kind='mergesort')
return BIT(ags,counts,n)
효율적인 BIT 접근 방식을 위한 numba 부분 :
@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 res
timeit
컬렉션 에 포함하는 것은 공정하지 않을 것 입니다.
Geoffrey Irving의 대답은 잘못되었습니다.
배열의 반전 수는 배열을 정렬하기 위해 이동해야하는 총 거리 요소의 절반입니다. 따라서 배열을 정렬하고 결과 순열 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가됩니다.
이것을 확인하십시오 : http://www.cs.jhu.edu/~xfliu/600.363_F03/hw_solution/solution1.pdf
정답이 되었으면합니다.
다음은 이진 트리의 변형으로 가능한 한 가지 솔루션입니다. 각 트리 노드에 rightSubTreeSize라는 필드를 추가합니다. 배열에 나타나는 순서대로 이진 트리에 숫자를 계속 삽입하십시오. number가 노드의 lhs이면 해당 요소의 반전 수는 (1 + rightSubTreeSize)가됩니다. 이러한 모든 요소는 현재 요소보다 크고 배열에서 더 일찍 나타 났을 것입니다. 요소가 노드의 rhs로 이동하면 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;
if(p->data < q->data)
그렇지 않으면 중복이 올바르게 처리되지 않습니다. q
루프의 맨 위에서 테스트 할 필요가 없으며 무조건 while
루프가 잘 작동합니다. 또한 이것이 어떤 언어인지 언급하지 않았습니다. :) 함수가 헤더 행을 잃은 것 같습니다.
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);
}
이것은 오래된 질문이므로 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];
}
}
다음은 C ++ 솔루션입니다.
/**
*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;
}
이 답변에는 timeit
내 주요 답변 의 코드에 의해 생성 된 테스트 결과가 포함되어 있습니다 . 자세한 내용은 해당 답변을 참조하십시오!
count_inversions speed test results
Size = 5, hi = 2, 4096 loops
ltree_count_PM2R : 0.04871, 0.04872, 0.04876
bruteforce_loops_PM2R : 0.05696, 0.05700, 0.05776
solution_TimBabych : 0.05760, 0.05822, 0.05943
solutionE_TimBabych : 0.06642, 0.06704, 0.06760
bruteforce_sum_PM2R : 0.07523, 0.07545, 0.07563
perm_sum_PM2R : 0.09873, 0.09875, 0.09935
rank_sum_PM2R : 0.10449, 0.10463, 0.10468
solution_python : 0.13034, 0.13061, 0.13221
fenwick_inline_PM2R : 0.14323, 0.14610, 0.18802
perm_radixR_PM2R : 0.15146, 0.15203, 0.15235
merge_count_BM : 0.16179, 0.16267, 0.16467
perm_radixI_PM2R : 0.16200, 0.16202, 0.16768
perm_fenwick_PM2R : 0.16887, 0.16920, 0.17075
merge_PM2R : 0.18262, 0.18271, 0.18418
count_inversions_NiklasB : 0.19183, 0.19279, 0.20388
count_inversion_mkso : 0.20060, 0.20141, 0.20398
inv_cnt_ZheHu : 0.20815, 0.20841, 0.20906
fenwick_PM2R : 0.22109, 0.22137, 0.22379
reversePairs_nomanpouigt : 0.29620, 0.29689, 0.30293
Value: 5
Size = 10, hi = 5, 2048 loops
solution_TimBabych : 0.05954, 0.05989, 0.05991
solutionE_TimBabych : 0.05970, 0.05972, 0.05998
perm_sum_PM2R : 0.07517, 0.07519, 0.07520
ltree_count_PM2R : 0.07672, 0.07677, 0.07684
bruteforce_loops_PM2R : 0.07719, 0.07724, 0.07817
rank_sum_PM2R : 0.08587, 0.08823, 0.08864
bruteforce_sum_PM2R : 0.09470, 0.09472, 0.09484
solution_python : 0.13126, 0.13154, 0.13185
perm_radixR_PM2R : 0.14239, 0.14320, 0.14474
perm_radixI_PM2R : 0.14632, 0.14669, 0.14679
fenwick_inline_PM2R : 0.16796, 0.16831, 0.17030
perm_fenwick_PM2R : 0.18189, 0.18212, 0.18638
merge_count_BM : 0.19816, 0.19870, 0.19948
count_inversions_NiklasB : 0.21807, 0.22031, 0.22215
merge_PM2R : 0.22037, 0.22048, 0.26106
fenwick_PM2R : 0.24290, 0.24314, 0.24744
count_inversion_mkso : 0.24895, 0.24899, 0.25205
inv_cnt_ZheHu : 0.26253, 0.26259, 0.26590
reversePairs_nomanpouigt : 0.35711, 0.35762, 0.35973
Value: 20
Size = 20, hi = 10, 1024 loops
solutionE_TimBabych : 0.05687, 0.05696, 0.05720
solution_TimBabych : 0.06126, 0.06151, 0.06168
perm_sum_PM2R : 0.06875, 0.06906, 0.07054
rank_sum_PM2R : 0.07988, 0.07995, 0.08002
ltree_count_PM2R : 0.11232, 0.11239, 0.11257
bruteforce_loops_PM2R : 0.12553, 0.12584, 0.12592
solution_python : 0.13472, 0.13540, 0.13694
bruteforce_sum_PM2R : 0.15820, 0.15849, 0.16021
perm_radixI_PM2R : 0.17101, 0.17148, 0.17229
perm_radixR_PM2R : 0.17891, 0.18087, 0.18366
perm_fenwick_PM2R : 0.20554, 0.20708, 0.21412
fenwick_inline_PM2R : 0.21161, 0.21163, 0.22047
merge_count_BM : 0.24125, 0.24261, 0.24565
count_inversions_NiklasB : 0.25712, 0.25754, 0.25778
merge_PM2R : 0.26477, 0.26566, 0.31297
fenwick_PM2R : 0.28178, 0.28216, 0.29069
count_inversion_mkso : 0.30286, 0.30290, 0.30652
inv_cnt_ZheHu : 0.32024, 0.32041, 0.32447
reversePairs_nomanpouigt : 0.45812, 0.45822, 0.46172
Value: 98
Size = 40, hi = 20, 512 loops
solutionE_TimBabych : 0.05784, 0.05787, 0.05958
solution_TimBabych : 0.06452, 0.06475, 0.06479
perm_sum_PM2R : 0.07254, 0.07261, 0.07263
rank_sum_PM2R : 0.08537, 0.08540, 0.08572
ltree_count_PM2R : 0.11744, 0.11749, 0.11792
solution_python : 0.14262, 0.14285, 0.14465
perm_radixI_PM2R : 0.18774, 0.18776, 0.18922
perm_radixR_PM2R : 0.19425, 0.19435, 0.19609
bruteforce_loops_PM2R : 0.21500, 0.21511, 0.21686
perm_fenwick_PM2R : 0.23338, 0.23375, 0.23674
fenwick_inline_PM2R : 0.24947, 0.24958, 0.25189
bruteforce_sum_PM2R : 0.27627, 0.27646, 0.28041
merge_count_BM : 0.28059, 0.28128, 0.28294
count_inversions_NiklasB : 0.28557, 0.28759, 0.29022
merge_PM2R : 0.29886, 0.29928, 0.30317
fenwick_PM2R : 0.30241, 0.30259, 0.35237
count_inversion_mkso : 0.34252, 0.34356, 0.34441
inv_cnt_ZheHu : 0.37468, 0.37569, 0.37847
reversePairs_nomanpouigt : 0.50725, 0.50770, 0.50943
Value: 369
Size = 80, hi = 40, 256 loops
solutionE_TimBabych : 0.06339, 0.06373, 0.06513
solution_TimBabych : 0.06984, 0.06994, 0.07009
perm_sum_PM2R : 0.09171, 0.09172, 0.09186
rank_sum_PM2R : 0.10468, 0.10474, 0.10500
ltree_count_PM2R : 0.14416, 0.15187, 0.18541
solution_python : 0.17415, 0.17423, 0.17451
perm_radixI_PM2R : 0.20676, 0.20681, 0.20936
perm_radixR_PM2R : 0.21671, 0.21695, 0.21736
perm_fenwick_PM2R : 0.26197, 0.26252, 0.26264
fenwick_inline_PM2R : 0.28111, 0.28249, 0.28382
count_inversions_NiklasB : 0.31746, 0.32448, 0.32451
merge_count_BM : 0.31964, 0.33842, 0.35276
merge_PM2R : 0.32890, 0.32941, 0.33322
fenwick_PM2R : 0.34355, 0.34377, 0.34873
count_inversion_mkso : 0.37689, 0.37698, 0.38079
inv_cnt_ZheHu : 0.42923, 0.42941, 0.43249
bruteforce_loops_PM2R : 0.43544, 0.43601, 0.43902
bruteforce_sum_PM2R : 0.52106, 0.52160, 0.52531
reversePairs_nomanpouigt : 0.57805, 0.58156, 0.58252
Value: 1467
Size = 160, hi = 80, 128 loops
solutionE_TimBabych : 0.06766, 0.06784, 0.06963
solution_TimBabych : 0.07433, 0.07489, 0.07516
perm_sum_PM2R : 0.13143, 0.13175, 0.13179
rank_sum_PM2R : 0.14428, 0.14440, 0.14922
solution_python : 0.20072, 0.20076, 0.20084
ltree_count_PM2R : 0.20314, 0.20583, 0.24776
perm_radixI_PM2R : 0.23061, 0.23078, 0.23525
perm_radixR_PM2R : 0.23894, 0.23915, 0.24234
perm_fenwick_PM2R : 0.30984, 0.31181, 0.31503
fenwick_inline_PM2R : 0.31933, 0.32680, 0.32722
merge_count_BM : 0.36003, 0.36387, 0.36409
count_inversions_NiklasB : 0.36796, 0.36814, 0.37106
merge_PM2R : 0.36847, 0.36848, 0.37127
fenwick_PM2R : 0.37833, 0.37847, 0.38095
count_inversion_mkso : 0.42746, 0.42747, 0.43184
inv_cnt_ZheHu : 0.48969, 0.48974, 0.49293
reversePairs_nomanpouigt : 0.67791, 0.68157, 0.72420
bruteforce_loops_PM2R : 0.82816, 0.83175, 0.83282
bruteforce_sum_PM2R : 1.03322, 1.03378, 1.03562
Value: 6194
Size = 320, hi = 160, 64 loops
solutionE_TimBabych : 0.07467, 0.07470, 0.07483
solution_TimBabych : 0.08036, 0.08066, 0.08077
perm_sum_PM2R : 0.21142, 0.21201, 0.25766
solution_python : 0.22410, 0.22644, 0.22897
rank_sum_PM2R : 0.22820, 0.22851, 0.22877
ltree_count_PM2R : 0.24424, 0.24595, 0.24645
perm_radixI_PM2R : 0.25690, 0.25710, 0.26191
perm_radixR_PM2R : 0.26501, 0.26504, 0.26729
perm_fenwick_PM2R : 0.33483, 0.33507, 0.33845
fenwick_inline_PM2R : 0.34413, 0.34484, 0.35153
merge_count_BM : 0.39875, 0.39919, 0.40302
fenwick_PM2R : 0.40434, 0.40439, 0.40845
merge_PM2R : 0.40814, 0.41531, 0.51417
count_inversions_NiklasB : 0.41681, 0.42009, 0.42128
count_inversion_mkso : 0.47132, 0.47192, 0.47385
inv_cnt_ZheHu : 0.54468, 0.54750, 0.54893
reversePairs_nomanpouigt : 0.76164, 0.76389, 0.80357
bruteforce_loops_PM2R : 1.59125, 1.60430, 1.64131
bruteforce_sum_PM2R : 2.03734, 2.03834, 2.03975
Value: 24959
Run 2
Size = 640, hi = 320, 8 loops
solutionE_TimBabych : 0.04135, 0.04374, 0.04575
ltree_count_PM2R : 0.06738, 0.06758, 0.06874
perm_radixI_PM2R : 0.06928, 0.06943, 0.07019
fenwick_inline_PM2R : 0.07850, 0.07856, 0.08059
perm_fenwick_PM2R : 0.08151, 0.08162, 0.08170
perm_sum_PM2R : 0.09122, 0.09133, 0.09221
rank_sum_PM2R : 0.09549, 0.09603, 0.11270
merge_count_BM : 0.10733, 0.10807, 0.11032
count_inversions_NiklasB : 0.12460, 0.19865, 0.20205
solution_python : 0.13514, 0.13585, 0.13814
Size = 1280, hi = 640, 8 loops
solutionE_TimBabych : 0.04714, 0.04742, 0.04752
perm_radixI_PM2R : 0.15325, 0.15388, 0.15525
solution_python : 0.15709, 0.15715, 0.16076
fenwick_inline_PM2R : 0.16048, 0.16160, 0.16403
ltree_count_PM2R : 0.16213, 0.16238, 0.16428
perm_fenwick_PM2R : 0.16408, 0.16416, 0.16449
count_inversions_NiklasB : 0.19755, 0.19833, 0.19897
merge_count_BM : 0.23736, 0.23793, 0.23912
perm_sum_PM2R : 0.32946, 0.32969, 0.33277
rank_sum_PM2R : 0.34637, 0.34756, 0.34858
Size = 2560, hi = 1280, 8 loops
solutionE_TimBabych : 0.10898, 0.11005, 0.11025
perm_radixI_PM2R : 0.33345, 0.33352, 0.37656
ltree_count_PM2R : 0.34670, 0.34786, 0.34833
perm_fenwick_PM2R : 0.34816, 0.34879, 0.35214
fenwick_inline_PM2R : 0.36196, 0.36455, 0.36741
solution_python : 0.36498, 0.36637, 0.40887
count_inversions_NiklasB : 0.42274, 0.42745, 0.42995
merge_count_BM : 0.50799, 0.50898, 0.50917
perm_sum_PM2R : 1.27773, 1.27897, 1.27951
rank_sum_PM2R : 1.29728, 1.30389, 1.30448
Size = 5120, hi = 2560, 8 loops
solutionE_TimBabych : 0.26914, 0.26993, 0.27253
perm_radixI_PM2R : 0.71416, 0.71634, 0.71753
perm_fenwick_PM2R : 0.71976, 0.72078, 0.72078
fenwick_inline_PM2R : 0.72776, 0.72804, 0.73143
ltree_count_PM2R : 0.81972, 0.82043, 0.82290
solution_python : 0.83714, 0.83756, 0.83962
count_inversions_NiklasB : 0.87282, 0.87395, 0.92087
merge_count_BM : 1.09496, 1.09584, 1.10207
rank_sum_PM2R : 5.02564, 5.06277, 5.06666
perm_sum_PM2R : 5.09088, 5.12999, 5.13512
Size = 10240, hi = 5120, 8 loops
solutionE_TimBabych : 0.71556, 0.71718, 0.72201
perm_radixI_PM2R : 1.54785, 1.55096, 1.55515
perm_fenwick_PM2R : 1.55103, 1.55353, 1.59298
fenwick_inline_PM2R : 1.57118, 1.57240, 1.57271
ltree_count_PM2R : 1.76240, 1.76247, 1.80944
count_inversions_NiklasB : 1.86543, 1.86851, 1.87208
solution_python : 2.01490, 2.01519, 2.06423
merge_count_BM : 2.35215, 2.35301, 2.40023
rank_sum_PM2R : 20.07048, 20.08399, 20.13200
perm_sum_PM2R : 20.10187, 20.12551, 20.12683
Run 3
Size = 20480, hi = 10240, 4 loops
solutionE_TimBabych : 1.07636, 1.08243, 1.09569
perm_radixI_PM2R : 1.59579, 1.60519, 1.61785
perm_fenwick_PM2R : 1.66885, 1.68549, 1.71109
fenwick_inline_PM2R : 1.72073, 1.72752, 1.77217
ltree_count_PM2R : 1.96900, 1.97820, 2.02578
count_inversions_NiklasB : 2.03257, 2.05005, 2.18548
merge_count_BM : 2.46768, 2.47377, 2.52133
solution_python : 2.49833, 2.50179, 3.79819
Size = 40960, hi = 20480, 4 loops
solutionE_TimBabych : 3.51733, 3.52008, 3.56996
perm_radixI_PM2R : 3.51736, 3.52365, 3.56459
perm_fenwick_PM2R : 3.76097, 3.80900, 3.87974
fenwick_inline_PM2R : 3.95099, 3.96300, 3.99748
ltree_count_PM2R : 4.49866, 4.54652, 5.39716
count_inversions_NiklasB : 4.61851, 4.64303, 4.73026
merge_count_BM : 5.31945, 5.35378, 5.35951
solution_python : 6.78756, 6.82911, 6.98217
Size = 81920, hi = 40960, 4 loops
perm_radixI_PM2R : 7.68723, 7.71986, 7.72135
perm_fenwick_PM2R : 8.52404, 8.53349, 8.53710
fenwick_inline_PM2R : 8.97082, 8.97561, 8.98347
ltree_count_PM2R : 10.01142, 10.01426, 10.03216
count_inversions_NiklasB : 10.60807, 10.62424, 10.70425
merge_count_BM : 11.42149, 11.42342, 11.47003
solutionE_TimBabych : 12.83390, 12.83485, 12.89747
solution_python : 19.66092, 19.67067, 20.72204
Size = 163840, hi = 81920, 4 loops
perm_radixI_PM2R : 17.14153, 17.16885, 17.22240
perm_fenwick_PM2R : 19.25944, 19.27844, 20.27568
fenwick_inline_PM2R : 19.78221, 19.80219, 19.80766
ltree_count_PM2R : 22.42240, 22.43259, 22.48837
count_inversions_NiklasB : 22.97341, 23.01516, 23.98052
merge_count_BM : 24.42683, 24.48559, 24.51488
solutionE_TimBabych : 60.96006, 61.20145, 63.71835
solution_python : 73.75132, 73.79854, 73.95874
Size = 327680, hi = 163840, 4 loops
perm_radixI_PM2R : 36.56715, 36.60221, 37.05071
perm_fenwick_PM2R : 42.21616, 42.21838, 42.26053
fenwick_inline_PM2R : 43.04987, 43.09075, 43.13287
ltree_count_PM2R : 49.87400, 50.08509, 50.69292
count_inversions_NiklasB : 50.74591, 50.75012, 50.75551
merge_count_BM : 52.37284, 52.51491, 53.43003
solutionE_TimBabych : 373.67198, 377.03341, 377.42360
solution_python : 411.69178, 411.92691, 412.83856
Size = 655360, hi = 327680, 4 loops
perm_radixI_PM2R : 78.51927, 78.66327, 79.46325
perm_fenwick_PM2R : 90.64711, 90.80328, 91.76126
fenwick_inline_PM2R : 93.32482, 93.39086, 94.28880
count_inversions_NiklasB : 107.74393, 107.80036, 108.71443
ltree_count_PM2R : 109.11328, 109.23592, 110.18247
merge_count_BM : 111.05633, 111.07840, 112.05861
solutionE_TimBabych : 1830.46443, 1836.39960, 1849.53918
solution_python : 1911.03692, 1912.04484, 1914.69786
다음은 카운트 반전을위한 C 코드입니다.
#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/에 있습니다.
O (n log n) 시간, Java의 O (n) 공간 솔루션
병합 단계 동안 수행 된 반전 수를 보존하기 위해 조정 된 병합 정렬. (잘 설명 된 병합 정렬은 http://www.vogella.com/tutorials/JavaAlgorithmsMergesort/article.html 을 참조하십시오. )
병합 정렬이 제자리에있을 수 있기 때문에 공간 복잡도는 O (1)로 향상 될 수 있습니다.
이 정렬을 사용할 때 반전은 병합 단계에서만 발생하며 첫 번째 부분의 요소 앞에 두 번째 부분의 요소를 넣어야하는 경우에만 발생합니다.
병합
3 + 2 + 0 = 5 반전이 있습니다.
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;
}
}
다음은 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);
}
파이썬으로 내 대답 :
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])
음, 다른 솔루션이 있지만 고유 한 배열 요소에만 작동하는 것이 두렵습니다.
//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;
}
내 코드를 설명하기 위해 Array의 끝에서 요소를 계속 추가합니다. 들어오는 배열 요소에 대해 들어오는 요소보다 큰 벡터 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
또 다른 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
쉬운 O (n ^ 2) 대답은 중첩 된 for 루프를 사용하고 모든 반전에 대해 카운터를 증가시키는 것입니다.
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;
이제 더 효율적인 솔루션을 원한다고 가정합니다. 생각해 보겠습니다.
O (N * log (N)) 시간 복잡도 요구 사항을 충족하는 C ++의 가능한 솔루션은 다음과 같습니다.
#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);
}
일반 병합 정렬과는 카운터 만 다릅니다.
다음은 Ruby의 O (n log n) 솔루션입니다.
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
가장 최적화 된 방법은 병합 정렬을 통해 해결하는 것입니다. 여기서 병합하면 왼쪽과 오른쪽 배열을 비교하여 얼마나 많은 반전이 필요한지 확인할 수 있습니다. 왼쪽 배열의 요소가 오른쪽 배열의 요소보다 클 때마다 반전됩니다.
병합 정렬 방식 :-
다음은 코드입니다. 코드는 mergeToParent
다른 조건에서 반전을 계산하는 방법의 코드 스 니펫을 제외하고 병합 정렬과 똑같습니다.(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);
}
}
크기 목록에 가능한 최대 반전 수 n
는 다음 표현식으로 일반화 할 수 있습니다.
maxPossibleInversions = (n * (n-1) ) / 2
따라서 크기 배열의 6
경우 가능한 최대 반전은 동일 15
합니다.
복잡성을 달성하려면 n logn
병합 정렬에서 반전 알고리즘을 피기 백 할 수 있습니다.
일반화 된 단계는 다음과 같습니다.
inversionCount += leftSubArray.length
그게 다야!
이것은 Javascript를 사용하여 만든 간단한 예입니다.
var arr = [6,5,4,3,2,1]; // Sample input array
var inversionCount = 0;
function mergeSort(arr) {
if(arr.length == 1)
return arr;
if(arr.length > 1) {
let breakpoint = Math.ceil((arr.length/2));
// Left list starts with 0, breakpoint-1
let leftList = arr.slice(0,breakpoint);
// Right list starts with breakpoint, length-1
let rightList = arr.slice(breakpoint,arr.length);
// Make a recursive call
leftList = mergeSort(leftList);
rightList = mergeSort(rightList);
var a = merge(leftList,rightList);
return a;
}
}
function merge(leftList,rightList) {
let result = [];
while(leftList.length && rightList.length) {
/**
* The shift() method removes the first element from an array
* and returns that element. This method changes the length
* of the array.
*/
if(leftList[0] <= rightList[0]) {
result.push(leftList.shift());
}else{
inversionCount += leftList.length;
result.push(rightList.shift());
}
}
while(leftList.length)
result.push(leftList.shift());
while(rightList.length)
result.push(rightList.shift());
console.log(result);
return result;
}
mergeSort(arr);
console.log('Number of inversions: ' + inversionCount);
Swift에서 병합 정렬을 사용하여 배열의 계수 반전 구현 :
스왑 수는 다음만큼 증가합니다.
nSwaps += mid + 1 - iL
(배열 왼쪽의 상대적 길이에서 왼쪽에있는 현재 요소의 인덱스를 뺀 값)
... 이는 배열의 오른쪽에있는 요소가 정렬되기 위해 건너 뛰어야하는 요소의 수 (반전 수)이기 때문입니다.
func merge(arr: inout [Int], arr2: inout [Int], low: Int, mid: Int, high: Int) -> Int {
var nSwaps = 0;
var i = low;
var iL = low;
var iR = mid + 1;
while iL <= mid && iR <= high {
if arr2[iL] <= arr2[iR] {
arr[i] = arr2[iL]
iL += 1
i += 1
} else {
arr[i] = arr2[iR]
nSwaps += mid + 1 - iL
iR += 1
i += 1
}
}
while iL <= mid {
arr[i] = arr2[iL]
iL += 1
i += 1
}
while iR <= high {
arr[i] = arr2[iR]
iR += 1
i += 1
}
return nSwaps
}
func mergeSort(arr: inout [Int]) -> Int {
var arr2 = arr
let nSwaps = mergeSort(arr: &arr, arr2: &arr2, low: 0, high: arr.count-1)
return nSwaps
}
func mergeSort(arr: inout [Int], arr2: inout [Int], low: Int, high: Int) -> Int {
if low >= high {
return 0
}
let mid = low + ((high - low) / 2)
var nSwaps = 0;
nSwaps += mergeSort(arr: &arr2, arr2: &arr, low: low, high: mid)
nSwaps += mergeSort(arr: &arr2, arr2: &arr, low: mid+1, high: high)
nSwaps += merge(arr: &arr, arr2: &arr2, low: low, mid: mid, high: high)
return nSwaps
}
var arrayToSort: [Int] = [2, 1, 3, 1, 2]
let nSwaps = mergeSort(arr: &arrayToSort)
print(arrayToSort) // [1, 1, 2, 2, 3]
print(nSwaps) // 4
대부분의 답변은 기반 MergeSort
이지만이 문제를 해결하는 유일한 방법은 아닙니다.O(nlogn)
몇 가지 접근 방식에 대해 설명하겠습니다.
사용 Balanced Binary Search Tree
이 같은.
Node *insert(Node* root, int data, int& count){
if(!root) return new Node(data);
if(root->data == data){
root->freq++;
count += getSize(root->right);
}
else if(root->data > data){
count += getSize(root->right) + root->freq;
root->left = insert(root->left, data, count);
}
else root->right = insert(root->right, data, count);
return balance(root);
}
int getCount(int *a, int n){
int c = 0;
Node *root = NULL;
for(auto i=0; i<n; i++) root = insert(root, a[i], c);
return c;
}
Binary Indexed Tree
int getInversions(int[] a) {
int n = a.length, inversions = 0;
int[] bit = new int[n+1];
compress(a);
BIT b = new BIT();
for (int i=n-1; i>=0; i--) {
inversions += b.getSum(bit, a[i] - 1);
b.update(bit, n, a[i], 1);
}
return inversions;
}
Segment Tree
[0, a[i]-1]
하고 업데이트합니다.a[i] with 1
int getInversions(int *a, int n) {
int N = n + 1, c = 0;
compress(a, n);
int tree[N<<1] = {0};
for (int i=n-1; i>=0; i--) {
c+= query(tree, N, 0, a[i] - 1);
update(tree, N, a[i], 1);
}
return c;
}
또한 사용시 BIT
또는 Segment-Tree
좋은 아이디어는Coordinate compression
void compress(int *a, int n) {
int temp[n];
for (int i=0; i<n; i++) temp[i] = a[i];
sort(temp, temp+n);
for (int i=0; i<n; i++) a[i] = lower_bound(temp, temp+n, a[i]) - temp + 1;
}
C ++ Θ (n lg n) 반전 카운트를 구성하는 쌍을 인쇄하는 솔루션입니다.
int merge(vector<int>&nums , int low , int mid , int high){
int size1 = mid - low +1;
int size2= high - mid;
vector<int>left;
vector<int>right;
for(int i = 0 ; i < size1 ; ++i){
left.push_back(nums[low+i]);
}
for(int i = 0 ; i <size2 ; ++i){
right.push_back(nums[mid+i+1]);
}
left.push_back(INT_MAX);
right.push_back(INT_MAX);
int i = 0 ;
int j = 0;
int start = low;
int inversion = 0 ;
while(i < size1 && j < size2){
if(left[i]<right[j]){
nums[start] = left[i];
start++;
i++;
}else{
for(int l = i ; l < size1; ++l){
cout<<"("<<left[l]<<","<<right[j]<<")"<<endl;
}
inversion += size1 - i;
nums[start] = right[j];
start++;
j++;
}
}
if(i == size1){
for(int c = j ; c< size2 ; ++c){
nums[start] = right[c];
start++;
}
}
if(j == size2){
for(int c = i ; c< size1 ; ++c){
nums[start] = left[c];
start++;
}
}
return inversion;
}
int inversion_count(vector<int>& nums , int low , int high){
if(high>low){
int mid = low + (high-low)/2;
int left = inversion_count(nums,low,mid);
int right = inversion_count(nums,mid+1,high);
int inversion = merge(nums,low,mid,high) + left + right;
return inversion;
}
return 0 ;
}
출력에 복사 된 숫자가 오른쪽 배열에서 가져온 경우 병합 단계 증가 카운터에서 mergesort를 사용하십시오.
최근에 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)
}