Tìm tất cả các chu kỳ


9

Tôi có một tập hợp hữu hạn , một hàm f : S S , và một trật tự tổng < trên S . Tôi muốn tìm số lượng các chu kỳ khác nhau trong S .Sf:SS<SS

Đối với một nguyên tố Tôi có thể sử dụng thuật toán Floyd (hoặc Brent, vv) để tìm độ dài của chu kỳ lặp đi lặp lại rằng các ứng dụng của f gửi s để; với một chút nỗ lực hơn, tôi có thể xác định chu kỳ này (ví dụ: bằng phần tử < -minimal) của nó. Một phương pháp tồi để giải quyết vấn đề sẽ là lặp lại từng phần tử này, sắp xếp các phần tử tối thiểu kết quả loại bỏ các bản sao và trả về số đếm. Nhưng điều này có khả năng liên quan đến nhiều lần vượt qua các yếu tố tương tự và yêu cầu không gian lớn.sSfs<

Phương pháp nào có hiệu suất thời gian và không gian tốt hơn? Tôi thậm chí không chắc cách tốt nhất để đo không gian cần thiết là gì nếu là hàm nhận dạng thì bất kỳ phương thức nào lưu trữ tất cả các chu kỳ sẽ sử dụng không gian Ω ( n ) .fΩ(n)


4
Một trong những cách tự nhiên để đo không gian là coi S là tập hợp các chuỗi n-bit và f là một lời tiên tri. Sau đó, thuật toán ngây thơ mà bạn mô tả đòi hỏi không gian theo cấp số nhân. Người ta có thể tìm kiếm một thuật toán chỉ sử dụng không gian đa thức, nhưng điều này nghe có vẻ không khả thi đối với tôi.
Tsuyoshi Ito

Đó là điều tôi muốn nói "Tôi không biết cách tốt nhất để đo không gian". Có lẽ tôi nên nhắm mục tiêu O (poly (n) + y) trong đó y là đầu ra, để không gian được sử dụng là đa thức miễn là y đủ nhỏ.
Charles

Có chức năng của bạn e có bất kỳ thuộc tính có thể sử dụng? Có hay không thuật toán là đa thức hoặc hàm mũ theo cách ưa thích của bạn thể hiện kích thước đầu vào sẽ có một chút tranh luận nếu câu trả lời thực tế là các thuật toán sẽ mất cả thời gian và không gian trên thứ tự của các phần tử của S .
Niel de Beaudrap

@Niel de Beaudrap: Tôi không chắc những thuộc tính nào sẽ hữu ích. Tôi hy vọng rằng số lượng các chu kỳ khác biệt là nhỏ, tuy nhiên, có lẽ là ; đó là lý do tại sao tôi đề xuất một hàm của y n thay vì chỉ n . Tôi sẵn sàng sử dụng số mũ theo số mũ của số lượng đầu ra, nếu cần. O(n3)ynn
Charles

Câu trả lời:


7

Nếu tất cả những gì bạn muốn làm là đếm số chu kỳ, bạn có thể làm điều này với 2 | S | bit (cộng với thay đổi) giá trị của không gian. Có vẻ như bạn sẽ không thể làm tốt hơn nhiều trừ khi S hoặc f có một số thuộc tính đặc biệt thuận tiện.

Bắt đầu với một mảng A lưu trữ số nguyên {0,1,2} - một cho mỗi phần tử của S - được khởi tạo thành 0; chúng tôi sẽ chỉ ra những như (unexplored), (partially explored), và (fully explored). Khởi tạo bộ đếm chu kỳ về không. Đối với mỗi phần tử s  ∈  S theo thứ tự, hãy làm như sau:

  1. Nếu A [ s ] =  (fully explored), bỏ qua bước 6.
  2. Đặt A [ s ] ←  (partially explored)và đặt một trình vòng lặp j  ←  f (s) .
  3. Trong khi A [ j ] =  (unexplored), đặt A [ j ] ←  (partially explored)và đặt j  ←  f (j) .
  4. Nếu A [ j ] =  (partially explored), chúng tôi đã đóng một chu kỳ mới; gia tăng c thêm 1. (Nếu bạn muốn giữ một bản ghi của một số đại diện của chu kỳ này, giá trị hiện tại của j sẽ là một lựa chọn tùy ý; tất nhiên, đây sẽ không nhất thiết là yếu tố tối thiểu trong chu trình theo sở thích của bạn order <.) Mặt khác, chúng ta có A [ j ] =  (fully explored), có nghĩa là chúng ta đã phát hiện ra một quỹ đạo được khám phá trước kết thúc trong một chu kỳ đã được tính; không tăng c .
  5. Để chỉ ra rằng quỹ đạo bắt đầu từ s đã được khám phá đầy đủ, hãy đặt j  ←  s .
    Trong khi A [ j ] =  (partially explored), đặt A [ j ] ←  (fully explored)và đặt j  ←  f (j) .
  6. Tiến tới phần tử tiếp theo s  ∈  S .

Do đó, mỗi chu kỳ giữa các quỹ đạo do f gây ra sẽ được tính một lần; và bất kỳ yếu tố nào bạn ghi lại dưới dạng đại diện sẽ là các yếu tố của các chu kỳ riêng biệt. Yêu cầu bộ nhớ là 2 | S | cho mảng A, O (log | S |) cho số chu kỳ và các tỷ lệ cược và kết thúc khác.

Mỗi yếu tố s  ∈  S sẽ được truy cập ít nhất hai lần: một lần khi giá trị của A [ s ] được thay đổi từ (unexplored)thành (partially explored)và một lần cho thay đổi thành (fully explored). Tổng số lần mà bất kỳ nút nào được xem xét lại sau khi bị (fully explored)giới hạn ở trên bởi số lần thử để tìm chu kỳ mới không thực hiện được, nhiều nhất là | S | - phát sinh từ việc iterating vòng lặp chính thông qua tất cả các yếu tố của S . Vì vậy, chúng ta có thể mong đợi quá trình này liên quan đến tối đa 3 | S | nút truyền tải, đếm tất cả thời gian mà các nút được truy cập hoặc xem lại.

Nếu bạn muốn theo dõi các yếu tố đại diện của các chu kỳ và bạn thực sự muốn chúng là các yếu tố tối thiểu, bạn có thể giới hạn số lượt truy cập nút thành 4 | S |, nếu bạn thêm một "vòng quanh chu kỳ" bổ sung ở bước 4 để tìm một đại diện nhỏ hơn so với người bạn đóng vòng lặp. (Nếu các quỹ đạo dưới f chỉ bao gồm các chu kỳ, công việc bổ sung này có thể tránh được, nhưng điều đó không đúng với f tùy ý .)


Tuyệt vời, điều này cải thiện thuật toán không gian ngớ ngẩn mà tôi có trong tâm trí. Tôi thực sự không cần đại diện; Tôi đã giới thiệu < chỉ trong trường hợp nó sẽ hữu ích cho một số thuật toán. Ôi(|S|đăng nhập|S|)<
Charles

Tôi tự hỏi nếu có một cách để sử dụng ít không gian hơn trong trường hợp có ít chu kỳ mà không sử dụng nhiều hơn không gian đa thức. À, không vấn đề gì; điều này sẽ làm cho nhu cầu của tôi.
Charles

1
Dường như với tôi rằng điều này nên ở #L (sử dụng nguồn ma trận). Đây có thể là # L-hard?
Kaveh

@Charles: xem câu trả lời gần đây hơn của tôi sẽ cung cấp cho bạn các cải tiến nếu bạn biết rằng # Motorcycle ∈ o ( | S | ). Nó sử dụng nhiều hơn polylog | S | không gian, nhưng nếu bạn sẵn sàng đánh đổi không gian và thời gian thì có thể tốt hơn cho bạn.
Niel de Beaudrap

@Niel de Beaudrap: Cảm ơn bạn! +1 cho cả hai. Thuật toán này có vẻ tốt nhất miễn là dữ liệu vừa với bộ nhớ; một khi nó tràn ra tôi sẽ xem xét việc sử dụng cái khác. (Có thể cái khác sẽ tốt hơn nếu tôi có thể phù hợp với mọi thứ trong bộ nhớ cache, nhưng điều đó có thể quá rắc rối.)
Charles

5

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 sampleslư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:

  1. Nếu s ở trong samples, bỏ qua bước 5.
  2. 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.
  3. Trong khi j không ở samples:
    - Nếu t  ∈  G , chèn j vào cả hai cursamplesamples.
    - Tăng t và đặt j  ←  f (j) .
  4. 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 .
  5. 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 sampleslà 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.


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.