câu trả lời tuyệt vời của caf in ra mỗi số xuất hiện k lần trong mảng k-1 lần. Đó là hành vi hữu ích, nhưng câu hỏi được cho là yêu cầu mỗi bản sao chỉ được in một lần và anh ta ám chỉ đến khả năng thực hiện điều này mà không vượt qua giới hạn thời gian / không gian tuyến tính. Điều này có thể được thực hiện bằng cách thay thế vòng lặp thứ hai của anh ta bằng mã giả sau:
for (i = 0; i < N; ++i) {
if (A[i] != i && A[A[i]] == A[i]) {
print A[i];
A[A[i]] = i;
}
}
Điều này khai thác thuộc tính mà sau khi vòng lặp đầu tiên chạy, nếu bất kỳ giá trị nào m
xuất hiện nhiều hơn một lần, thì một trong những lần xuất hiện đó được đảm bảo ở đúng vị trí, cụ thể làA[m]
. Nếu cẩn thận, chúng ta có thể sử dụng vị trí "nhà riêng" đó để lưu trữ thông tin về việc có bản sao nào đã được in hay chưa.
Trong phiên bản của caf, khi chúng ta xem qua mảng, A[i] != i
ngụ ý rằng đó A[i]
là một bản sao. Trong phiên bản của tôi, tôi dựa trên một bất biến hơi khác: A[i] != i && A[A[i]] == A[i]
ngụ ý rằng đó A[i]
là một bản sao mà chúng ta chưa từng thấy trước đây . (Nếu bạn bỏ phần "mà chúng tôi chưa từng thấy trước đây", phần còn lại có thể được thấy là ngụ ý bởi sự thật bất biến của caf và đảm bảo rằng tất cả các bản sao đều có một số bản sao ở vị trí nhà riêng.) Thuộc tính này có tại đầu tiên (sau khi kết thúc vòng lặp đầu tiên của caf) và tôi chỉ ra bên dưới rằng nó được duy trì sau mỗi bước.
Khi chúng ta đi qua mảng, thành công trong A[i] != i
một phần của thử nghiệm ngụ ý rằng đó A[i]
có thể là một bản sao chưa từng thấy trước đây. Nếu chúng tôi chưa từng nhìn thấy nó trước đây, thì chúng tôi hy vọng A[i]
vị trí nhà của nó sẽ tự chỉ về chính nó - đó là những gì được kiểm tra trong nửa sau củaif
điều kiện. Nếu đúng như vậy, chúng tôi sẽ in nó và thay đổi vị trí nhà để quay lại bản sao được tìm thấy đầu tiên này, tạo ra một "chu kỳ" gồm 2 bước.
Để thấy rằng thao tác này không làm thay đổi tính bất biến của chúng ta, hãy giả sử m = A[i]
đối với một vị trí cụ thể i
thỏa mãn A[i] != i && A[A[i]] == A[i]
. Rõ ràng là thay đổi mà chúng tôi thực hiện ( A[A[i]] = i
) sẽ hoạt động để ngăn các trường hợp không phải ở nhà khác xuất hiện m
dưới dạng bản sao bằng cách khiến nửa sau của các if
điều kiện của chúng không thành công, nhưng liệu nó có hoạt động khi i
đến vị trí chính m
không? Đúng vậy, bởi vì bây giờ, mặc dù ở thời điểm mới này, i
chúng tôi thấy rằng nửa đầu của if
điều kiện A[i] != i
, là đúng, nhưng nửa sau sẽ kiểm tra xem vị trí mà nó trỏ đến có phải là vị trí nhà hay không và nhận thấy rằng nó không phải. Trong tình huống này, chúng ta không còn biết liệu m
hoặcA[m]
là giá trị trùng lặp, nhưng chúng ta biết rằng một trong hai cách,nó đã được báo cáo , bởi vì 2 chu kỳ này được đảm bảo không xuất hiện trong kết quả của vòng lặp đầu tiên của caf. (Lưu ý rằng nếu m != A[m]
sau đó chính xác một trong số m
và A[m]
xảy ra nhiều lần, và trường hợp kia hoàn toàn không xảy ra.)
a[a[i]]
và ràng buộc không gian O (1) gợi ýswap()
thao tác là khóa.