Đếm nghịch đảo trong một mảng


108

Tôi đang thiết kế một thuật toán để làm như sau: Cho mảng A[1... n], với mọi i < j, tìm tất cả các cặp đảo ngược như vậy A[i] > A[j]. Tôi đang sử dụng sắp xếp hợp nhất và sao chép mảng A sang mảng B và sau đó so sánh hai mảng, nhưng tôi gặp khó khăn khi biết cách sử dụng điều này để tìm số lượng nghịch đảo. Bất kỳ gợi ý hoặc trợ giúp sẽ được đánh giá rất cao.

Câu trả lời:


139

Vì vậy, đây là giải pháp O (n log n) trong 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);
}

Đây là loại hợp nhất gần như bình thường, toàn bộ phép thuật được ẩn trong chức năng hợp nhất. Lưu ý rằng trong khi sắp xếp thuật toán loại bỏ các nghịch đảo. Trong khi thuật toán hợp nhất đếm số lần đảo ngược bị loại bỏ (có thể nói là đã sắp xếp ra).

Thời điểm duy nhất khi các phép đảo ngược bị loại bỏ là khi thuật toán lấy phần tử từ phía bên phải của một mảng và hợp nhất nó vào mảng chính. Số nghịch đảo bị loại bỏ bởi thao tác này là số phần tử còn lại từ mảng bên trái sẽ được hợp nhất. :)

Hy vọng nó đủ giải thích.


2
Tôi đã thử chạy điều này và tôi không nhận được câu trả lời chính xác. Bạn có phải gọi invCount (intArray) bên trong main để bắt đầu không? Với intArray là mảng không được sắp xếp của int? Tôi đã chạy nó với một mảng nhiều số nguyên và nhận được -1887062008 là câu trả lời của tôi. Tôi đang làm gì sai?
Điểm gần

4
+1, Xem giải pháp tương tự trong C ++ 11 , bao gồm giải pháp dựa trên trình lặp chung và thử nghiệm ngẫu nhiên mẫu sử dụng trình tự từ 5-25 phần tử. Thưởng thức!.
WhozCraig

3
Đây không phải là một giải pháp. Tôi đã thử chạy nó và nó cho kết quả không chính xác.
mirgee

2
Xin lỗi vì câu hỏi mới mẻ, nhưng thêm left.length - ivào bộ đếm đảo ngược thì sao? Tôi nghĩ rằng chỉ cần thêm 1 sẽ hợp lý, vì bạn rơi vào trường hợp logic khi so sánh giữa hai mảng con có phần tử mảng bên trái lớn hơn mảng bên phải. Có ai có thể giải thích cho tôi như tôi 5 không?
Alfredo Gallegos

2
@AlfredoGallegos, một minh họa ngắn gọn về câu trả lời của Marek. Xét hai mảng: [6, 8] và [4, 5]. Khi bạn thấy rằng 6 lớn hơn 4, bạn lấy 4 và đặt nó vào arr. Nhưng nó không phải là một sự đảo ngược. Bạn đã tìm thấy nghịch đảo cho tất cả các phần tử trong mảng bên trái lớn hơn 6. Trong trường hợp của chúng tôi, nó cũng bao gồm 8. Vì vậy, 2 được thêm vào count, bằng left.length - i.
ilya

86

Tôi đã tìm thấy nó trong thời gian O (n * log n) bằng phương pháp sau.

  1. Hợp nhất sắp xếp mảng A và tạo một bản sao (mảng B)
  2. Lấy A [1] và tìm vị trí của nó trong mảng B đã sắp xếp thông qua tìm kiếm nhị phân. Số nghịch đảo của phần tử này sẽ nhỏ hơn một so với số chỉ mục của vị trí của nó trong B vì mọi số thấp hơn xuất hiện sau phần tử đầu tiên của A sẽ là một nghịch đảo.

    2a. tích lũy số lần đảo ngược để chống lại số lần chuyển đổi biến.

    2b. loại bỏ A [1] khỏi mảng A và cả vị trí tương ứng của nó trong mảng B

  3. chạy lại từ bước 2 cho đến khi không còn phần tử nào trong A.

Đây là một ví dụ về thuật toán này. Mảng gốc A = (6, 9, 1, 14, 8, 12, 3, 2)

1: Hợp nhất sắp xếp và sao chép vào mảng B

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

2: Lấy A [1] và tìm kiếm nhị phân để tìm nó trong mảng B

A [1] = 6

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

6 nằm ở vị trí thứ 4 của mảng B, do đó có 3 nghịch đảo. Chúng ta biết điều này vì 6 nằm ở vị trí đầu tiên trong mảng A, do đó bất kỳ phần tử nào có giá trị thấp hơn xuất hiện sau đó trong mảng A sẽ có chỉ số là j> i (vì i trong trường hợp này là 1).

2.b: Xóa A [1] khỏi mảng A và cả vị trí tương ứng của nó trong mảng B (các phần tử in đậm bị xóa).

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: Chạy lại từ bước 2 trên mảng A và B mới.

A [1] = 9

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

9 bây giờ ở vị trí thứ 5 của mảng B, do đó có 4 nghịch đảo. Chúng ta biết điều này vì 9 nằm ở vị trí đầu tiên trong mảng A, do đó bất kỳ phần tử nào có giá trị thấp hơn xuất hiện sau đó sẽ có chỉ số là j> i (vì i trong trường hợp này lại là 1). Xóa A [1] khỏi mảng A và cả vị trí tương ứng của nó trong mảng B (các phần tử in đậm bị xóa)

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)

Tiếp tục trong mạch này sẽ cho chúng ta tổng số lần nghịch đảo cho mảng A sau khi hoàn tất vòng lặp.

Bước 1 (sắp xếp hợp nhất) sẽ sử dụng O (n * log n) để thực thi. Bước 2 sẽ thực hiện n lần và tại mỗi lần thực hiện sẽ thực hiện một tìm kiếm nhị phân lấy O (log n) để chạy với tổng số O (n * log n). Do đó, tổng thời gian chạy sẽ là O (n * log n) + O (n * log n) = O (n * log n).

Cảm ơn bạn đã giúp đỡ. Viết ra các mảng mẫu trên một tờ giấy thực sự giúp hình dung vấn đề.


1
tại sao sử dụng sắp xếp hợp nhất không phải là sắp xếp nhanh?
Alcott

5
@Alcott Sắp xếp nhanh có thời gian chạy kém nhất là O (n ^ 2), khi danh sách đã được sắp xếp và xoay vòng đầu tiên được chọn mỗi vòng. Trường hợp xấu nhất Merge sort là O (n log n)
user482594

29
Bước loại bỏ khỏi một mảng tiêu chuẩn làm cho thuật toán của bạn là O (n ^ 2), do thay đổi các giá trị. (Đó là lý do tại sao sắp xếp chèn là O (n ^ 2))
Kyle Butt

bắt đầu với phần tử đầu tiên của mảng B và đếm các phần tử trước nó trong mảng A cũng sẽ cho kết quả tương tự, miễn là bạn loại bỏ chúng như bạn đã mô tả trong câu trả lời của mình.
tutak

@el diablo Làm thế nào để loại bỏ các phần tử để tránh n ^ 2 phức tạp ??
Jerky

26

Trong 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)

13
Tôi cảm thấy bối rối với cách điều này quản lý để đạt được +13 - Tôi không đặc biệt giỏi về Python, nhưng nó có vẻ khá giống với phiên bản Java được trình bày 2 năm trước , ngoại trừ điều này không cung cấp bất kỳ giải thích nào . Việc đăng câu trả lời bằng mọi ngôn ngữ khác đều có hại cho IMO - có thể có hàng nghìn ngôn ngữ, nếu không muốn nói là nhiều hơn - tôi hy vọng không ai tranh luận rằng chúng ta nên đăng hàng nghìn câu trả lời cho một câu hỏi - Stack Exchange không được tạo ra cho điều đó .
Bernhard Barker

1
@tennenrishin Được rồi, có thể không phải hàng nghìn. Nhưng chúng ta vẽ đường thẳng ở đâu? Hiện tại, theo tôi đếm, có mười câu trả lời đưa ra cùng một cách tiếp cận . Đó là khoảng 43% câu trả lời (không bao gồm câu trả lời không) - khá nhiều không gian để chiếm dụng vì có nửa tá cách tiếp cận khác được trình bày ở đây. Ngay cả khi chỉ có 2 câu trả lời cho cùng một cách tiếp cận, điều đó vẫn làm loãng câu trả lời một cách không cần thiết. Và tôi đã đưa ra một lập luận khá phù hợp cho câu trả lời này đặc biệt không hữu ích trong nhận xét trước của tôi.
Bernhard Barker

3
@Dukeling Giống như bạn, tôi không quen với Python và quen thuộc hơn với Java. Tôi thấy giải pháp này khó đọc hơn nhiều so với giải pháp Java. Vì vậy, có lý do rằng đối với một số người, câu chuyện có thể đúng ở mức độ tương tự.
Museful

3
Đối với đại đa số người dùng, python gần với mã sudo. Tôi thực sự thấy điều này dễ đọc hơn nhiều so với java mặc dù nó không có lời giải thích. Tôi thấy không cần phải quá khó chịu nếu nó giúp ích cho một số độc giả.
Francisco Vargas

2
Giải pháp này hoàn toàn tốt và có thể đọc được đối với người dùng python. Mọi người muốn xem cách những người khác triển khai điều này bằng Python.
Aerin

24

Tôi tự hỏi tại sao chưa ai đề cập đến cây được lập chỉ mục nhị phân . Bạn có thể sử dụng một để duy trì tổng tiền tố trên giá trị của các phần tử hoán vị của bạn. Sau đó, bạn có thể chỉ cần tiếp tục từ phải sang trái và đếm cho mọi phần tử số phần tử nhỏ hơn nó ở bên phải:

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

Độ phức tạp là O (n log n), và hệ số hằng số rất thấp.


có lẽ là phương pháp tốt nhất :)
Nilutpal Borgohain

@NilutpalBorgohain Cảm ơn :) Có vẻ như yêu cầu ít mã nhất trong số các ứng cử viên O (n log n).
Niklas B.

1
Cảm ơn vì điều đó. Ý nghĩa của i -= i & -idòng là gì? Và tương tựi += i & -i
Gerard Condon

1
@GerardCondon về cơ bản đó là cấu trúc dữ liệu BIT. Một liên kết giải thích nó có thể được tìm thấy trong câu trả lời
Niklas B.

TIL về cây Fenwick. Cảm ơn! Tôi đã đăng một câu trả lời mà không một timeitso sánh tất cả các câu trả lời Python cho câu hỏi này, vì vậy nó bao gồm mã của bạn. Bạn có thể quan tâm đến việc xem kết quả thời gian.
PM 2Ring

14

Tôi đã có một câu hỏi tương tự như thế này cho bài tập về nhà. Tôi đã bị hạn chế rằng nó phải có hiệu suất O (nlogn).

Tôi đã sử dụng ý tưởng bạn đề xuất về việc sử dụng Mergesort, vì nó đã có hiệu quả chính xác. Tôi vừa chèn một số mã vào hàm hợp nhất về cơ bản: Bất cứ khi nào một số từ mảng bên phải được thêm vào mảng đầu ra, tôi thêm vào tổng số nghịch đảo, số lượng số còn lại trong mảng bên trái.

Điều này rất có ý nghĩa với tôi bây giờ tôi đã nghĩ về nó đủ rồi. Bạn đếm bao nhiêu lần có một số lớn hơn đến trước bất kỳ số nào.

hth.


6
tôi ủng hộ câu trả lời của bạn, khác biệt chủ yếu từ merge sort là chức năng merge khi phần tử của mảng thứ 2 ngay được sao chép vào mảng output => increment đảo ngược theo số lượng các yếu tố còn lại trong mảng 1 trái
Alex.Salnikov

11

Mục đích chính của câu trả lời này là để so sánh tốc độ của các phiên bản Python khác nhau được tìm thấy ở đây, nhưng tôi cũng có một vài đóng góp của riêng mình. (FWIW, tôi vừa phát hiện ra câu hỏi này trong khi thực hiện tìm kiếm trùng lặp).

Tốc độ thực thi tương đối của các thuật toán được triển khai trong CPython có thể khác với những gì người ta mong đợi từ một phân tích đơn giản về các thuật toán và từ kinh nghiệm với các ngôn ngữ khác. Đó là bởi vì Python cung cấp nhiều hàm và phương thức mạnh mẽ được triển khai bằng C có thể hoạt động trên danh sách và các bộ sưu tập khác với tốc độ gần bằng tốc độ mà một ngôn ngữ được biên dịch đầy đủ sẽ có được, vì vậy các hoạt động đó chạy nhanh hơn nhiều so với các thuật toán tương đương được thực hiện "thủ công" với Python mã.

Mã tận dụng các công cụ này thường có thể làm tốt hơn các thuật toán vượt trội về mặt lý thuyết cố gắng thực hiện mọi thứ với các hoạt động Python trên các mục riêng lẻ của bộ sưu tập. Tất nhiên, số lượng dữ liệu thực tế đang được xử lý cũng có tác động đến điều này. Nhưng đối với lượng dữ liệu vừa phải, mã sử dụng thuật toán O (n²) chạy ở tốc độ C có thể dễ dàng đánh bại thuật toán O (n log n) thực hiện phần lớn công việc của nó với các hoạt động Python riêng lẻ.

Nhiều câu trả lời đã đăng cho câu hỏi đếm nghịch đảo này sử dụng một thuật toán dựa trên kết hợp. Về mặt lý thuyết, đây là một cách tiếp cận tốt, trừ khi kích thước mảng rất nhỏ. Nhưng TimSort tích hợp của Python (một thuật toán sắp xếp ổn định kết hợp, bắt nguồn từ sắp xếp hợp nhất và sắp xếp chèn) chạy ở tốc độ C và một tổ hợp hợp nhất được mã hóa bằng tay trong Python không thể hy vọng cạnh tranh với nó về tốc độ.

Một trong những giải pháp hấp dẫn hơn ở đây, trong câu trả lời được đăng bởi Niklas B , sử dụng sắp xếp tích hợp để xác định thứ hạng của các mục mảng và Cây lập chỉ mục nhị phân (còn gọi là cây Fenwick) để lưu trữ tổng tích lũy cần thiết để tính toán đảo ngược đếm. Trong quá trình cố gắng hiểu cấu trúc dữ liệu này và thuật toán của Niklas, tôi đã viết một vài biến thể của riêng mình (đăng bên dưới). Nhưng tôi cũng phát hiện ra rằng đối với kích thước danh sách vừa phải, việc sử dụng hàm tích hợp của Python thực sự nhanhsum hơn so với cây Fenwick đáng yêu.

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

Cuối cùng, khi kích thước danh sách đạt khoảng 500, khía cạnh O (n²) của việc gọi sumbên trong forvòng lặp đó sẽ khiến nó trở nên xấu xí và hiệu suất bắt đầu giảm mạnh.

Mergesort không phải là kiểu O (nlogn) duy nhất và một số kiểu khác có thể được sử dụng để thực hiện đếm đảo ngược. Câu trả lời của prasadvk sử dụng sắp xếp cây nhị phân, tuy nhiên mã của anh ấy dường như bằng C ++ hoặc một trong các dẫn xuất của nó. Vì vậy, tôi đã thêm một phiên bản Python. Ban đầu tôi đã sử dụng một lớp để triển khai các nút cây, nhưng phát hiện ra rằng một câu lệnh nhanh hơn đáng kể. Cuối cùng tôi đã sử dụng danh sách, thậm chí còn nhanh hơn, mặc dù nó làm cho mã khó đọc hơn một chút.

Một điểm cộng của treeort là việc thực hiện lặp đi lặp lại dễ dàng hơn nhiều so với kết hợp. Python không tối ưu hóa đệ quy và nó có giới hạn độ sâu đệ quy (mặc dù điều đó có thể được tăng lên nếu bạn thực sự cần). Và tất nhiên các lệnh gọi hàm trong Python tương đối chậm, vì vậy khi bạn đang cố gắng tối ưu hóa tốc độ thì tốt nhất là nên tránh các lệnh gọi hàm, khi thực tế.

Một kiểu O (nlogn) khác là kiểu cơ số đáng kính. Ưu điểm lớn là nó không so sánh các phím với nhau. Điều bất lợi là nó hoạt động tốt nhất trên các chuỗi số nguyên liền nhau, lý tưởng nhất là hoán vị của các số nguyên trong range(b**m)đó bthường là 2. Tôi đã thêm một vài phiên bản dựa trên sắp xếp cơ số sau khi cố gắng đọc Đếm nghịch đảo, Đếm phạm vi trực giao ngoại tuyến và Các vấn đề liên quan là được liên kết trong việc tính số "nghịch đảo" trong một hoán vị .

Để sử dụng sắp xếp cơ số một cách hiệu quả để đếm các nghịch đảo trong một dãy tổng quát seqcó độ dài n, chúng ta có thể tạo một hoán vị của range(n)nó có cùng số nghịch đảo với seq. Chúng tôi có thể làm điều đó trong (ít nhất) thời gian O (nlogn) thông qua TimSort. Bí quyết là hoán vị các chỉ số của seqbằng cách sắp xếp seq. Sẽ dễ dàng hơn để giải thích điều này với một ví dụ nhỏ.

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

đầu ra

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

Bằng cách sắp xếp các cặp (giá trị, chỉ số), seqchúng tôi đã hoán vị các chỉ số của seqvới cùng một số lượng hoán đổi được yêu cầu để đưa seqvào thứ tự ban đầu của nó từ thứ tự đã sắp xếp của nó. Chúng ta có thể tạo hoán vị đó bằng cách sắp xếp range(n)với một hàm khóa phù hợp:

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

đầu ra

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

Chúng ta có thể tránh điều đó lambdabằng cách sử dụng seq's .__getitem__phương pháp:

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

Điều này chỉ nhanh hơn một chút, nhưng chúng tôi đang tìm kiếm tất cả các cải tiến về tốc độ mà chúng tôi có thể nhận được. ;)


Đoạn mã dưới đây thực hiện timeitcác bài kiểm tra trên tất cả các thuật toán Python hiện có trên trang này, cộng với một số thuật toán của riêng tôi: một vài phiên bản O (n²) brute-force O (n²), một vài biến thể trên thuật toán của Niklas B và tất nhiên là một phiên bản dựa trên hợp nhất (mà tôi đã viết mà không tham khảo các câu trả lời hiện có). Nó cũng có mã sắp xếp cây dựa trên danh sách của tôi gần như bắt nguồn từ mã của prasadvk và các chức năng khác nhau dựa trên sắp xếp cơ số, một số sử dụng chiến lược tương tự như các phương pháp hợp nhất và một số sử dụng sumhoặc cây Fenwick.

Chương trình này đo thời gian thực hiện của mỗi hàm trên một loạt danh sách ngẫu nhiên các số nguyên; nó cũng có thể xác minh rằng mỗi hàm cho kết quả giống như các hàm khác và nó không sửa đổi danh sách đầu vào.

Mỗi timeitcuộc gọi đưa ra một vectơ chứa 3 kết quả, tôi sắp xếp. Giá trị chính để xem đây là một mức tối thiểu, các giá trị khác chỉ đơn thuần là đưa ra một chỉ dẫn về cách đáng tin cậy rằng giá trị tối thiểu là, như đã trình bày tại Thuyết minh trong các timeittài liệu mô-đun .

Thật không may, kết quả từ chương trình này quá lớn để đưa vào câu trả lời này, vì vậy tôi sẽ đăng nó trong câu trả lời của riêng nó (wiki cộng đồng) .

Kết quả là từ 3 lần chạy trên máy 32 bit lõi đơn 2GHz cổ đại của tôi chạy Python 3.6.0 trên một bản phân phối phái sinh Debian cũ. YMMV. Trong quá trình kiểm tra, tôi tắt trình duyệt Web và ngắt kết nối khỏi bộ định tuyến để giảm thiểu tác động của các tác vụ khác lên CPU.

Lần chạy đầu tiên kiểm tra tất cả các chức năng với kích thước danh sách từ 5 đến 320, với kích thước vòng lặp từ 4096 đến 64 (khi kích thước danh sách tăng gấp đôi, kích thước vòng lặp giảm một nửa). Nhóm ngẫu nhiên được sử dụng để xây dựng mỗi danh sách có kích thước bằng một nửa của chính danh sách, vì vậy chúng tôi có khả năng nhận được rất nhiều bản sao. Một số thuật toán đếm nghịch đảo nhạy cảm với các bản sao hơn các thuật toán khác.

Lần chạy thứ hai sử dụng danh sách lớn hơn: 640 đến 10240 và kích thước vòng lặp cố định là 8. Để tiết kiệm thời gian, nó loại bỏ một số chức năng chậm nhất khỏi các bài kiểm tra. My brute-force O (n ²) chức năng chỉ là cách quá chậm ở các kích thước, và như đã đề cập trước đó, mã của tôi mà sử dụng sum, mà làm như vậy tốt trên nhỏ vào danh sách vừa phải, chỉ có thể không theo kịp trên danh sách lớn.

Lần chạy cuối cùng bao gồm kích thước danh sách từ 20480 đến 655360 và kích thước vòng lặp cố định là 4, với 8 chức năng nhanh nhất. Đối với kích thước danh sách dưới 40.000 hoặc lâu hơn, mã của Tim Babych là người chiến thắng rõ ràng. Làm tốt lắm Tim! Mã của Niklas B cũng là một nghệ sĩ giỏi toàn diện, mặc dù nó bị đánh bại trong các danh sách nhỏ hơn. Mã dựa trên phân giác của "python" cũng hoạt động khá tốt, mặc dù nó có vẻ chậm hơn một chút với danh sách khổng lồ có nhiều bản sao, có thể do whilevòng lặp tuyến tính mà nó sử dụng để vượt qua lỗi.

Tuy nhiên, đối với kích thước danh sách rất lớn, các thuật toán dựa trên phân giác không thể cạnh tranh với các thuật toán O (nlogn) thực sự.

#!/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

Vui lòng xem ở đây để biết kết quả đầu ra


Cảm ơn, điều đó khá thú vị :) Cho thấy rõ ràng lợi ích của việc sử dụng mô-đun C - đó là phân giác.
Tim Babych

Vấn đề là người chiến thắng sử dụng thuật toán bậc hai (về mặt lý thuyết). đối với kích thước ~ 100 000, nó sẽ bị đánh bại bởi những người khác. Tôi đã chỉnh sửa bài đăng của mình để đặt giải pháp tốc độ C gần như tuyến tính của python.
BM

@BM Chắc chắn rồi, nhưng cách tiếp cận chia đôi của Tim khá tốt cho đến khi bạn đạt đến kích thước 45.000 hoặc hơn. Tôi có một số giải pháp khác mà tôi sẽ bổ sung ở đây vào ngày hôm sau hoặc lâu hơn.
PM 2Ring

@TimBabych Bạn đang nói bisectlà C? Tôi khá chắc chắn đó là Python.
Stefan Pochmann


10

Có thể tìm thấy số lần đảo ngược bằng cách phân tích quá trình hợp nhất theo loại hợp nhất: quá trình hợp nhất

Khi sao chép một phần tử từ mảng thứ hai sang mảng hợp nhất (9 trong ví dụ này), nó sẽ giữ vị trí của nó tương đối so với các phần tử khác. Khi sao chép một phần tử từ mảng đầu tiên sang mảng hợp nhất (5 ở đây), nó sẽ được đảo ngược với tất cả các phần tử ở trong mảng thứ hai (2 đảo ngược với 3 và 4). Vì vậy, một chút sửa đổi của sắp xếp hợp nhất có thể giải quyết vấn đề trong O (n ln n).
Đối với ví dụ, chỉ cần bỏ ghi chú hai dòng # trong mã python hợp nhất bên dưới để có số lượng.

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

CHỈNH SỬA 1

Tác vụ tương tự có thể đạt được với phiên bản sắp xếp nhanh ổn định, được biết là nhanh hơn một chút:

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)

Chọn trục xoay làm yếu tố cuối cùng, các lần đảo ngược được tính tốt và thời gian thực hiện tốt hơn 40% so với hợp nhất ở trên.

CHỈNH SỬA 2

Đối với hiệu suất trong python, phiên bản numpy & numba:

Đầu tiên là phần numpy, sử dụng argsort O (n ln n):

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)

Và phần numba cho cách tiếp cận BIT hiệu quả :

@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  

Tôi đã đăng một câu trả lời mà không một timeitso sánh tất cả các câu trả lời Python cho câu hỏi này, vì vậy nó bao gồm mã của bạn. Bạn có thể quan tâm đến việc xem kết quả thời gian.
PM 2Ring

Không có vấn đề về hiệu suất trong bài đăng này ... Tôi sẽ thử trong một thời gian. Numpy numba được phép;)?
BM

Tôi chưa bao giờ sử dụng Numba, nhưng tôi đã sử dụng Numpy một chút và nghĩ đến việc tự mình thêm phiên bản Numpy, nhưng tôi quyết định chỉ giới hạn các thử nghiệm đối với các giải pháp chỉ sử dụng thư viện tiêu chuẩn. Nhưng tôi đoán sẽ rất thú vị khi xem giải pháp Numpy so sánh như thế nào. Tôi nghi ngờ rằng nó sẽ không nhanh hơn trên các danh sách nhỏ.
PM 2Ring

Tốc độ tăng gấp 100 lần thật ấn tượng! Nhưng tôi không thể chạy nó vì tôi không có Numba. Và như tôi đã nói trước đó, sẽ không công bằng nếu đưa nó vào timeitbộ sưu tập của tôi .
PM 2Ring

8

Lưu ý rằng câu trả lời của Geoffrey Irving là sai.

Số lần đảo ngược trong một mảng bằng một nửa tổng khoảng cách các phần tử phải được di chuyển để sắp xếp mảng. Do đó, nó có thể được tính bằng cách sắp xếp mảng, duy trì hoán vị kết quả p [i], rồi tính tổng abs (p [i] -i) / 2. Điều này mất O (n log n) thời gian, là tối ưu.

Một phương pháp thay thế được đưa ra tại http://mathworld.wolfram.com/PermutationInversion.html . Phương thức này tương đương với tổng của max (0, p [i] -i), bằng tổng abs (p [i] -i]) / 2 vì tổng khoảng cách các phần tử di chuyển sang trái bằng tổng khoảng cách các yếu tố di chuyển sang phải.

Lấy dãy {3, 2, 1} làm ví dụ. Có ba nghịch đảo: (3, 2), (3, 1), (2, 1), do đó số nghịch đảo là 3. Tuy nhiên, theo phương pháp đã trích dẫn, câu trả lời sẽ là 2.


Thay vào đó, bạn có thể tìm ra câu trả lời chính xác bằng cách đếm số lần hoán đổi liền kề được yêu cầu tối thiểu. Xem cuộc thảo luận: stackoverflow.com/questions/20990127/…
Isaac Turner


4

Đây là một giải pháp khả thi với biến thể của cây nhị phân. Nó thêm một trường gọi là rightSubTreeSize vào mỗi nút cây. Tiếp tục chèn số vào cây nhị phân theo thứ tự chúng xuất hiện trong mảng. Nếu số đi lhs của nút thì số nghịch đảo cho phần tử đó sẽ là (1 + rightSubTreeSize). Vì tất cả các phần tử đó lớn hơn phần tử hiện tại và chúng sẽ xuất hiện sớm hơn trong mảng. Nếu phần tử chuyển đến rhs của một nút, chỉ cần tăng rightSubTreeSize của nó. Sau đây là mã.

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;

Đây là một cách tiếp cận thú vị và nó có vẻ khá nhanh. Tuy nhiên, so sánh đó cần phải được so sánh if(p->data < q->data)nếu không các bản sao không được xử lý chính xác. Và không cần phải kiểm tra qở đầu vòng lặp, một whilevòng lặp vô điều kiện hoạt động tốt. Ngoài ra, bạn đã quên đề cập đến ngôn ngữ này là gì. :) Và hàm của bạn dường như bị mất dòng tiêu đề.
PM 2Ring

Tôi vừa thêm một phiên bản Python dựa trên thuật toán cây của bạn vào câu trả lời của tôi. Tất nhiên nó không nhanh như phiên bản được biên dịch đầy đủ, nhưng nó hoạt động khá tốt, so với các phiên bản Python khác.
PM 2Ring

3
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);
}

3
Điều này có khác nhiều so với các giải pháp JavaPython đã đăng không? Ngoài ra, các câu trả lời chỉ có mã không phải là IMO đặc biệt tốt, đặc biệt khi xem xét câu hỏi này thậm chí không chỉ định một ngôn ngữ.
Bernhard Barker

2

Vì đây là một câu hỏi cũ nên tôi sẽ cung cấp câu trả lời của mình bằng 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];
  }
}

-1 bởi vì một câu trả lời ở mọi ngôn ngữ khác sẽ dẫn đến quá nhiều câu trả lời một cách vô vọng, tất cả đều trùng lặp thông tin đã được trình bày trong các câu trả lời khác. Ngoài ra, đây về cơ bản là một câu trả lời chỉ có mã mà không có giải thích, tốt nhất, chủ yếu phù hợp với các câu hỏi thực sự về ngôn ngữ đó.
Bernhard Barker

2

Đây là giải pháp 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;
}

2

Câu trả lời này chứa kết quả của các timeitbài kiểm tra được tạo bởi mã trong câu trả lời chính của tôi . Hãy xem câu trả lời đó để biết chi tiết!

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

1

Đây là mã C để đếm nghịch đảo

#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;
}

Giải thích chi tiết tại đây: http://www.geeksforgeeks.org/counting-inversions/


1

O (n log n) thời gian, O (n) giải pháp không gian trong java.

Một hợp nhất, với một tinh chỉnh để bảo toàn số lần đảo ngược được thực hiện trong bước hợp nhất. (để biết hợp nhất được giải thích rõ ràng, hãy xem tại http://www.vogella.com/tutorials/JavaAlgorithmMergesort/article.html )

Vì kết hợp có thể được thực hiện tại chỗ, độ phức tạp không gian có thể được cải thiện thành O (1).

Khi sử dụng cách sắp xếp này, sự đảo ngược chỉ xảy ra trong bước hợp nhất và chỉ khi chúng ta phải đặt một phần tử của phần thứ hai trước các phần tử của nửa đầu, ví dụ:

  • 0 5 10 15

sáp nhập với

  • 1 6 22

chúng ta có 3 + 2 + 0 = 5 nghịch đảo:

  • 1 với {5, 10, 15}
  • 6 với {10, 15}
  • 22 với {}

Sau khi chúng tôi thực hiện 5 lần đảo ngược, danh sách hợp nhất mới của chúng tôi là 0, 1, 5, 6, 10, 15, 22

Có một nhiệm vụ demo trên Codility được gọi là ArrayInversionCount, nơi bạn có thể kiểm tra giải pháp của mình.

    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;
    }

}

1

Đây là triển khai perl O (n * log (n)):

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

Câu trả lời của tôi bằng Python:

1- Sắp xếp Mảng trước và tạo một bản sao của nó. Trong chương trình của tôi, B đại diện cho mảng đã sắp xếp. 2- Lặp lại mảng ban đầu (chưa được sắp xếp) và tìm chỉ mục của phần tử đó trên danh sách đã sắp xếp. Cũng ghi lại chỉ số của phần tử. 3- Đảm bảo rằng phần tử không có bất kỳ bản sao nào, nếu có thì bạn cần thay đổi giá trị chỉ mục của mình bằng -1. Điều kiện while trong chương trình của tôi chính xác là làm điều đó. 4- Tiếp tục đếm phần nghịch đảo sẽ tạo ra giá trị chỉ mục của bạn và xóa phần tử khi bạn đã tính toán phần tử nghịch đảo của nó.

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])

Tôi đã đăng một câu trả lời mà không một timeitso sánh tất cả các câu trả lời Python cho câu hỏi này, vì vậy nó bao gồm mã của bạn. Bạn có thể quan tâm đến việc xem kết quả thời gian.
PM 2Ring

1

Tôi có một giải pháp khác nhưng tôi sợ rằng nó sẽ chỉ hoạt động cho các phần tử mảng riêng biệt.

//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;
}

Để giải thích mã của tôi, chúng tôi tiếp tục thêm các phần tử từ cuối Mảng. Đối với bất kỳ phần tử mảng nào đến, chúng tôi tìm chỉ số của phần tử đầu tiên trong vectơ v lớn hơn phần tử đến của chúng tôi và gán giá trị đó cho số đảo ngược của chỉ số của phần tử đến Sau đó chúng ta chèn phần tử đó vào vectơ v ở vị trí chính xác sao cho vectơ v vẫn theo thứ tự được sắp xếp.

//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

1

Một giải pháp Python khác, một giải pháp ngắn gọn. Sử dụng mô-đun phân giác nội trang, cung cấp các chức năng để chèn phần tử vào vị trí của nó trong mảng đã sắp xếp và tìm chỉ mục của phần tử trong mảng đã sắp xếp.

Ý tưởng là lưu trữ các phần tử còn lại của thứ n trong mảng như vậy, điều này sẽ cho phép chúng ta dễ dàng tìm thấy số lượng chúng lớn hơn thứ 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

1
Tôi đã đăng một câu trả lời mà không một timeitso sánh tất cả các câu trả lời Python cho câu hỏi này, vì vậy nó bao gồm mã của bạn. Bạn có thể quan tâm đến việc xem kết quả thời gian. : D
PM 2Ring

0

Câu trả lời dễ dàng O (n ^ 2) là sử dụng các vòng lặp for lồng nhau và tăng một bộ đếm cho mỗi lần nghịch đảo

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;

Bây giờ tôi cho rằng bạn muốn một giải pháp hiệu quả hơn, tôi sẽ suy nghĩ về nó.


3
Đối với các câu hỏi bài tập về nhà, tốt nhất là đưa ra các gợi ý hữu ích hơn là một giải pháp thực tế. Dạy một người câu cá.
Bác sĩ Jones

3
Đó là giải pháp rõ ràng mà mọi học sinh khác sẽ nhận được đầu tiên, tôi cho rằng giáo viên của họ muốn cách thực hiện tốt hơn sẽ giúp họ nhận được nhiều điểm hơn.
mbillard

Không nhất thiết, nó phụ thuộc vào cấp độ của khóa học lập trình. Nó không quá đơn giản cho người mới bắt đầu.
Bác sĩ Jones

họ rất có thể muốn n * log (n) giải pháp
aderesh

0

Một giải pháp khả thi trong C ++ đáp ứng yêu cầu về độ phức tạp thời gian O (N * log (N)) sẽ như sau.

#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);

}

Nó khác với cách sắp xếp hợp nhất thông thường chỉ bởi bộ đếm.


Điều này có vẻ khá giống với các giải pháp JavaPython được đăng trước đó dường như sử dụng cùng một thuật toán, và do đó tôi không cảm thấy nó bổ sung nhiều giá trị ngoài chúng.
Bernhard Barker

0

Đây là giải pháp O (n log n) của tôi trong 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

Và một số trường hợp thử nghiệm:

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

0

Cách tối ưu hóa tốt nhất sẽ là giải quyết nó thông qua sắp xếp hợp nhất, nơi sẽ tự hợp nhất, chúng ta có thể kiểm tra số lượng nghịch đảo được yêu cầu bằng cách so sánh mảng trái và phải. Bất cứ khi nào phần tử ở mảng bên trái lớn hơn phần tử ở mảng bên phải, nó sẽ là nghịch đảo.

Hợp nhất sắp xếp Phương pháp tiếp cận: -

Đây là mã. Mã giống hệt như sắp xếp hợp nhất ngoại trừ đoạn mã theo mergeToParentphương pháp mà tôi đang đếm đảo ngược trong điều kiện khác(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;
        }

    }

}

Một cách tiếp cận khác mà chúng ta có thể so sánh mảng đầu vào với mảng được sắp xếp: - Việc thực hiện câu trả lời Diablo. Mặc dù cách tiếp cận này không nên được ưu tiên vì loại bỏ n phần tử khỏi một mảng hoặc danh sách là 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);
        
        
        

    }


}

0

Số lượng đảo ngược tối đa có thể có cho một danh sách kích thước ncó thể được tổng quát bằng một biểu thức:

maxPossibleInversions = (n * (n-1) ) / 2

Vì vậy, đối với một mảng có kích thước 6tối đa, số lần nghịch đảo sẽ bằng15 .

Để đạt được sự phức tạp của n logn chúng ta có thể sử dụng thuật toán nghịch đảo trên sắp xếp hợp nhất.

Dưới đây là các bước tổng quát:

  • Chia mảng thành hai
  • Gọi quy trình mergeSort. Nếu phần tử trong mảng con bên trái lớn hơn phần tử trong mảng con bên phảiinversionCount += leftSubArray.length

Đó là nó!

Đây là một ví dụ đơn giản, tôi đã thực hiện bằng 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);

0

Thực hiện đếm nghịch đảo trong một mảng với sắp xếp hợp nhất trong Swift:

Lưu ý rằng số lượng hoán đổi được tăng lên bởi

nSwaps += mid + 1 - iL 

(là độ dài tương đối của phía bên trái của mảng trừ đi chỉ số của phần tử hiện tại ở phía bên trái)

... bởi vì đó là số phần tử mà phần tử ở phía bên phải của mảng phải bỏ qua (# nghịch đảo) để được sắp xếp.

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

0

Hầu hết các câu trả lời đều dựa trên MergeSortnhưng nó không phải là cách duy nhất để giải quyết vấn đề này làO(nlogn)

Tôi sẽ thảo luận về một số cách tiếp cận.

  1. Sử dụng một Balanced Binary Search Tree

    • Bổ sung cây của bạn để lưu trữ tần số cho các phần tử trùng lặp.
    • Ý tưởng là tiếp tục đếm các nút lớn hơn khi cây được chuyển từ gốc sang lá để chèn.

Một cái gì đó như thế này.

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;
}
  1. Sử dụng một Binary Indexed Tree
    • Tạo BIT tổng kết.
    • Lặp lại từ cuối và bắt đầu tìm số phần tử lớn hơn.
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;
}
  1. Sử dụng một Segment Tree
    • Tạo một cây phân đoạn tổng kết.
    • Vòng lặp từ cuối mảng và truy vấn giữa [0, a[i]-1]và cập nhậta[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;
}

Ngoài ra, khi sử dụng BIThoặc Segment-Treemột ý tưởng hay là làmCoordinate 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;
}

0

C ++ Θ (n lg n) Lời giải với việc in ra cặp tạo thành số lượng nghịch đảo.

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 ;
}

-1

Sử dụng kết hợp hợp nhất, trong bộ đếm tăng dần bước hợp nhất nếu số được sao chép vào đầu ra là từ mảng bên phải.


Việc tăng bộ đếm (có lẽ là một) cho mỗi phần tử sẽ cung cấp cho bạn quá ít nghịch đảo.
Bernhard Barker

-1

Gần đây tôi đã phải làm điều này trong 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)
}

1
@Dukeling Điều gì đã khiến bạn rút lại nhận xét của mình nhưng không phản đối?
Museful
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.