Giá trị có thể trùng, vị trí gốc cần được giữ
Để liệt kê cặp chỉ số của dữ liệu ban đầu, mỗi phần tử đi cùng bản ghi (giá trị,chỉ số gốc). So sánh và trộn theo giá trị nhưng giữ chỉ số đi theo phần tử.
Khi giá trị phải nhỏ hơn, mỗi bản ghi trái còn lại sinh một cặp chỉ số gốc. Vì các phần tử trái ban đầu đứng trước phần tử phải, các cặp này thỏa i<j. Từ cặp chỉ số (i,j), đọc A gốc để có cặp giá trị (A[i],A[j]). Không dùng vị trí đã đổi sau sắp xếp như chỉ số của dãy gốc.
Mở rộng bước trộn để đưa ra từng cặp
```python
def pair_merge(B, C):
# Mỗi bản ghi là (giá trị, chỉ số gốc), hai dãy đã sắp theo giá trị.
result, pairs = [], []
i = j = 0
while i < len(B) and j < len(C):
if B[i][0] <= C[j][0]:
result.append(B[i])
i += 1
else:
result.append(C[j])
pairs.extend((B[t][1], C[j][1]) for t in range(i, len(B)))
j += 1
result.extend(B[i:])
result.extend(C[j:])
return result, pairs
```
B[t][0] là giá trị và B[t][1] là chỉ số gốc. Khi B[i][0]>C[j][0], dòng pairs.extend thêm từng cặp (B[t][1],C[j][1]) với t chạy từ i tới cuối B. Mỗi cặp đều phải được ghi, thay vì chỉ cộng một số lượng.
Ghép các danh sách trái, chéo và phải
```python
def pair_sort(records):
if len(records) <= 1:
return records, []
mid = len(records) // 2
B, pairsL = pair_sort(records[:mid])
C, pairsR = pair_sort(records[mid:])
merged, pairsM = pair_merge(B, C)
return merged, pairsL + pairsM + pairsR
```
```python
def inversion_indices(A):
records = [(value, i) for i, value in enumerate(A)]
_, pairs = pair_sort(records)
return pairs
```
pair_sort trả cả bản ghi đã sắp xếp và danh sách cặp. inversion_indices tạo nhãn gốc trước khi gọi; A gốc không bị sửa. Thứ tự các cặp trong kết quả có thể khác cách duyệt i,j, nhưng cần đủ và không trùng cặp chỉ số.
Xem đồng thời hai cách biểu diễn
Chạy:
```text
python3 g11-cs-thuc-hanh-chia-de-tri.py liet-ke "6 2 6 1"
```
Kết quả cần có 4 cặp chỉ số: (0,1),(0,3),(1,3),(2,3), không yêu cầu đúng thứ tự liệt kê này. Các cặp giá trị tương ứng có (6,1) hai lần vì chỉ số gốc khác nhau. Dòng số lượng cần khớp bản dem.
Với dãy giảm thực sự, có n(n−1)/2 cặp cần xuất. Chỉ việc ghi đủ các cặp đã có thể tốn O(n²), nên không giữ kết luận O(n log n) của bản chỉ đếm cho yêu cầu liệt kê mọi cặp.
Bài luyện
Với A gốc=[6,2,6,1], cặp giá trị của cặp chỉ số (2,3) là gì?
A[2]=6 và A[3]=1.
Đây là hai chỉ số, chưa phải hai giá trị.
Đã đảo thứ tự hai giá trị.
A[3]=1, không phải6.
Lời giải
Đọc đúng dãy gốc: phần tử chỉ số 2 bằng 6, phần tử chỉ số 3 bằng 1. Vì 2<3 và 6>1 nên (6,1) là cặp giá trị tương ứng.
Bài luyện
Vì sao phải giữ chỉ số gốc trong bản ghi khi trộn?
Điều kiện thứ tự chỉ số vẫn là một phần định nghĩa.
Nhãn gốc cho biết cặp thuộc dữ liệu ban đầu.
Trộn có thể đưa phần tử tới vị trí khác.
Cần giữ cả hai thông tin, không thay thế nhau.
Lời giải
Thuật toán đổi thứ tự bản ghi theo giá trị. Chỉ số gốc đi kèm giúp xuất đúng vị trí ban đầu, kể cả khi có nhiều phần tử cùng giá trị.
Bài luyện
Tại sao không hứa liệt kê mọi nghịch đảo luôn chỉ cần O(n log n)?
Số cặp tăng theo dữ liệu và kích thước.
Các hàm đã trả danh sách kết quả.
Kích thước đầu ra trong trường hợp xấu nhất đã có bậc n².
Số cặp có thể rất lớn.
Lời giải
Với n giá trị giảm thực sự, mọi cặp chỉ số i<j là nghịch đảo. Cần xuất n(n−1)/2 cặp, nên riêng đầu ra đã yêu cầu công việc bậc O(n²) trong trường hợp này.
Bài luyện
Hai cặp giá trị (6,1) trong dãy mẫu được xử lí thế nào khi liệt kê?
Sẽ làm thiếu kết quả của bài toán cặp vị trí.
Đổi thứ tự sẽ không còn cặp tương ứng.
Không đáp ứng yêu cầu cặp chỉ số.
Hai nghịch đảo khác nhau là (0,3) và (2,3).
Lời giải
Giá trị giống nhau chưa có nghĩa cùng một cặp vị trí. Hai số 6 ở hai chỉ số khác nhau đều lớn hơn số 1 phía sau, nên cần liệt kê cả hai nghịch đảo.
Đang học