Nếu bạn có rất ít chu kỳ, đây là một thuật toán sẽ sử dụng ít không gian hơn, nhưng sẽ mất nhiều thời gian hơn để chấm dứt.
[Biên tập.] Phân tích thời gian chạy trước đây của tôi đã bỏ lỡ chi phí quan trọng để xác định xem các nút chúng tôi truy cập có nằm trong số các nút được lấy mẫu trước đó hay không; Câu trả lời này đã được sửa đổi một chút để sửa lỗi này.
Chúng tôi một lần nữa lặp qua tất cả các yếu tố của S . Khi chúng ta khám phá quỹ đạo của các yếu tố s ∈ S , chúng tôi lấy mẫu từ các nút mà chúng ta đã truy cập, để có thể kiểm tra nếu chúng ta đi qua chúng một lần nữa. Chúng tôi cũng duy trì một danh sách các mẫu từ 'thành phần' - các hiệp hội quỹ đạo chấm dứt trong một chu kỳ chung (và do đó tương đương với các chu kỳ) - đã được truy cập trước đó.
Khởi tạo một danh sách trống các thành phần , complist
. Mỗi thành phần được đại diện bởi một tập hợp các mẫu từ thành phần đó; chúng tôi cũng duy trì một cây tìm kiếm samples
lưu trữ tất cả các yếu tố đã được chọn làm mẫu cho một số thành phần hoặc thành phần khác. Đặt G là một chuỗi các số nguyên lên đến n , trong đó thành viên có thể được xác định một cách hiệu quả bằng cách tính toán một số vị từ boolean; ví dụ, quyền hạn của 2 hoặc hoàn hảo p thứ quyền hạn đối với một số nguyên p . Với mỗi s ∈ S , hãy làm như sau:
- Nếu s ở trong
samples
, bỏ qua bước 5.
- Khởi tạo một danh sách trống
cursample
, một trình vòng lặp j ← f ( s ) và một bộ đếm t ← 1.
- Trong khi j không ở
samples
:
- Nếu t ∈ G , chèn j vào cả hai cursample
và samples
.
- Tăng t và đặt j ← f (j) .
- Kiểm tra xem j có ở trong không
cursample
. Nếu không, chúng ta đã gặp một thành phần được khám phá trước đó: chúng ta kiểm tra thành phần j thuộc về và chèn tất cả các thành phần củacursample
vào phần tử thích hợp complist
để tăng nó. Mặt khác, chúng tôi đã gặp lại một yếu tố từ quỹ đạo hiện tại, nghĩa là chúng tôi đã đi qua một chu kỳ ít nhất một lần mà không gặp phải bất kỳ đại diện nào của các chu kỳ được phát hiện trước đó: chúng tôi chèn cursample
, như một tập hợp các mẫu từ một thành phần mới được tìm thấy, vàocomplist
.
- Tiến tới phần tử tiếp theo s ∈ S .
Với n = | S |, đặt X (n) là hàm tăng đơn điệu mô tả số chu kỳ dự kiến ( ví dụ X (n) = n 1/3 ) và đặt Y (n) = y (n) log ( n ) ∈ ( X (n) log ( n )) là hàm tăng đơn điệu xác định mục tiêu cho việc sử dụng bộ nhớ ( ví dụ: y (n) = n 1/2 ). Chúng tôi yêu cầu y (n) ( X (n) ) vì sẽ mất ít nhất X (n) log ( n ) để lưu trữ một mẫu từ mỗi thành phần.
Chúng ta càng lấy được nhiều yếu tố của một quỹ đạo, chúng ta càng có khả năng nhanh chóng chọn một mẫu trong chu kỳ ở cuối quỹ đạo và từ đó nhanh chóng phát hiện chu kỳ đó. Từ quan điểm tiệm cận, sau đó có ý nghĩa để có được nhiều mẫu như giới hạn bộ nhớ của chúng tôi cho phép: chúng tôi cũng có thể đặt G để có các phần tử y (n) dự kiến nhỏ hơn n .
- Nếu chiều dài tối đa của một quỹ đạo trong S được dự kiến là L , chúng ta có thể đặt G là bội số nguyên của L / y (n) .
- Nếu không có độ dài dự kiến, chúng tôi có thể chỉ cần lấy mẫu một lần mỗi n / y (n)các yếu tố; trong mọi trường hợp, giới hạn trên của các khoảng giữa các mẫu.
Nếu, khi tìm kiếm một thành phần mới, chúng ta bắt đầu duyệt qua các phần tử của S mà chúng ta đã truy cập trước đó (từ một thành phần mới được phát hiện hoặc một thành phần cũ đã được tìm thấy chu kỳ đầu cuối), nó sẽ mất tối đa n / y ( n) lặp đi lặp lại để gặp một yếu tố được lấy mẫu trước đó; đây là giới hạn trên về số lần, với mỗi lần thử tìm thành phần mới, chúng ta duyệt qua các nút dự phòng. Bởi vì chúng tôi làm n nỗ lực như vậy, chúng ta sau đó sẽ dư thừa thăm yếu tố của S tối đa là n 2 / y (n) lần trong tổng số.
Công việc cần thiết để kiểm tra tư cách thành viên samples
là O ( y (n) log y (n) ), chúng tôi lặp lại ở mỗi lần truy cập: chi phí tích lũy của kiểm tra này là O ( n 2 log y (n) ). Ngoài ra còn có chi phí thêm các mẫu vào bộ sưu tập tương ứng của chúng, mà tích lũy là O ( y (n) log y (n) ). Cuối cùng, mỗi lần chúng ta gặp lại một thành phần được phát hiện trước đó, chúng ta phải dành tới X (n) log * y (n) thời gian để xác định thành phần nào chúng ta đã khám phá lại; vì điều này có thể xảy ra tới n lần, công việc tích lũy có liên quan được giới hạn bởi n X (n) log y (n) .
Do đó, công việc tích lũy được thực hiện trong việc kiểm tra xem các nút mà chúng tôi truy cập có nằm trong số các mẫu chi phối thời gian chạy hay không: chi phí này là O ( n 2 log y (n) ). Sau đó, chúng ta nên làm cho y (n) càng nhỏ càng tốt, đó là nói O ( X (n) ).
Do đó, người ta có thể liệt kê số chu kỳ (tương đương với số lượng thành phần kết thúc trong các chu kỳ đó) trong không gian O ( X (n) log ( n )), lấy O ( n 2 log X (n) ) thời gian để làm như vậy, trong đó X (n) là số chu kỳ dự kiến.