Số chu kỳ của một hoán vị


23

Hãy xem xét một hoán vị của các số nguyên 1, ... n, chẳng hạn như số nguyên này cho n = 6:

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

Nếu bạn xem hoán vị là ánh xạ từ [1,2,3,4,5,6]đến [5,2,4,3,6,1], hoán vị có thể được dịch ngược thành các chu kỳ rời rạc . Một chu trình là tập hợp con của các phần tử ánh xạ với nhau. Ví dụ: 1được ánh xạ tới 5, được ánh xạ tới 6, được ánh xạ trở lại 1. Vì vậy, một chu kỳ là [1,5,6]. Các chu kỳ khác là [2][3,4]. Do đó số chu kỳ cho hoán vị này là 3.

Nói chung, các chu kỳ của một hoán vị là duy nhất (theo thứ tự) và số chu kỳ cho một hoán vị kích thước nthay đổi từ 1đến n.

Các thách thức

Cho một hoán vị không trống, xuất ra số chu kỳ của nó.

Đầu vào là một mảng hình thành bởi các nsố nguyên 1, 2, ..., n, nơi n > 0. Mỗi số nguyên xảy ra chính xác một lần. Thứ tự xuất hiện chúng xác định hoán vị, như trong ví dụ trên.

Thay vì một mảng, bạn có thể sử dụng một danh sách, một chuỗi có dấu phân cách giữa các số, một đầu vào riêng cho mỗi số hoặc bất cứ điều gì hợp lý.

Để hoán vị kích thước n, thay vì bộ số nguyên dựa trên 1 1, ..., nbạn luôn có thể sử dụng bộ dựa trên 0 0, ..., n-1. Nếu vậy, xin vui lòng cho biết nó trong câu trả lời của bạn.

Mã phải hoạt động ntối đa 20trong một thời gian hợp lý, nói ít hơn một phút.

Mã golf. Tất cả các nội dung cho phép.

Các trường hợp thử nghiệm

Điều này giả định đầu vào mảng, dựa trên 1.

 [1] -> 1
 [3,2,1] -> 2
 [2,3,4,5,1] -> 1
 [5,2,4,3,6,1] -> 3
 [8,6,4,5,2,1,7,3] -> 2
 [4,5,11,12,7,1,3,9,10,6,8,2] -> 1
 [4,2,5,11,12,7,1,3,9,10,6,8] -> 5
 [5,8,6,18,16,9,14,10,11,12,4,20,15,19,2,17,1,13,7,3] -> 3
 [14,5,17,15,10,18,1,3,4,13,11,16,2,12,9,7,20,6,19,8] -> 7

Liên quan

Thử thách liên quan này yêu cầu các chu kỳ thực tế của hoán vị, chứ không phải số lượng của chúng. Chỉ yêu cầu số lượng chu kỳ có thể dẫn đến các thuật toán ngắn hơn mà tạo ra các chu kỳ thực tế.


Không bao giờ trả lời câu hỏi của tôi, nó được nêu trong câu hỏi đầu vào dựa trên 0 được cho phép.
orlp

@orlp Thế là nhanh! Tôi thậm chí còn không được xem câu hỏi của bạn
Luis Mendo

Chúng ta có thể lấy ánh xạ của các chỉ mục thành các giá trị làm đầu vào không?
Đồng

1
@Copper Tôi nghĩ là có, nếu miền của ánh xạ là tập hợp 1, ..., ntheo thứ tự đó. Bạn có thể làm rõ làm thế nào một ánh xạ có thể là một đầu vào? Nó có phải là một cấu trúc dữ liệu?
Luis Mendo

@LuisMendo Vâng, đó là cấu trúc dữ liệu, giống như Python dict. Tôi muốn có {1: 2, 2: 1}một đầu vào thay vì [2, 1].
Đồng

Câu trả lời:


12

J, 4 byte

#@C.

Điều này giả định rằng hoán vị là dựa trên 0. Nó sử dụng nội dung C.được cung cấp một danh sách đại diện cho một hoán vị trực tiếp đưa ra một danh sách các chu kỳ. Sau đó, #sáng tác @trên đó trả về số lượng chu kỳ trong danh sách đó.

Hãy thử nó ở đây.


1
Như thế là gian lận! :)
orlp

1
Lẽ ra tôi nên cấm các nội dung :-D
Luis Mendo

2
Nội tâm là tình yêu. Nội tích là cuộc sống. Tôi đồng ý rằng sẽ vui hơn khi các nội dung bị cấm. Hãy thay đổi quy tắc ngay bây giờ trước khi có quá nhiều câu trả lời.
dặm

@miles Nah, tôi sẽ để nó như vậy. Làm tốt lắm!
Luis Mendo

7

JavaScript, 99 98 byte

Giải pháp này giả định mảng và các giá trị của nó là không có chỉ mục (ví dụ [2, 1, 0]).

f=a=>{h={},i=c=0;while(i<a.length){s=i;while(!h[i]){h[i]=1;i=a[i]}c++;i=s;while(h[++i]);}return c}

Giải trình

// assumes the array is valid and zero-indexed
var findCycles = (array) => {
    var hash = {};  // remembers visited nodes
    var index = 0;  // current node
    var count = 0;  // number of cycles
    var start;      // starting node of cycle

    // loop until all nodes visited
    while(index < array.length) {
        start = index;  // cache starting node

        // loop until found previously visited node
        while(!hash[index]) {
            hash[index] = 1;    // mark node as visited
            index = array[index];   // get next node
        }
        count++;    // increment number of cycles

        index = start + 1;  // assume next node is right after

        // loop until found unvisited node
        while(hash[index]) {
            index++;    // get next node
        }
    }

    return count;   // return number of cycles
};

3
Chào mừng đến với PPCG! Câu trả lời đầu tiên tốt đẹp! Đây cũng là một trong những điều tốt nhất, nếu không phải là câu trả lời đầu tiên, tôi đã thấy trong kinh nghiệm của mình! Hãy tiếp tục phát huy!
GamrCorps

Wow, cảm ơn bạn rất nhiều! Tôi thực sự đã phải tìm cách làm lambdas trong JavaScript. Tôi chưa quen với công cụ ES6.
kamoroso94

6

Toán học, 45 byte

Length@ConnectedComponents@Thread[Sort@#->#]&

Nó tạo ra một biểu đồ và đếm các thành phần được kết nối của nó.


6

Toán học, 37 28 27 byte

#~PermutationCycles~Length&

Cảm ơn @alephalpha vì đã lưu 9 byte và @miles để có thêm 1 byte.


3
PermutationCycles[#,Length]&
alephalpha

3
Ôi thật gọn gàng. Tôi không biết PermutationCyclescó thể lấy một đối số thứ hai để thay đổi phần đầu ra của nó. Bạn cũng có thể sử dụng ký hiệu infix để lưu một byte khác #~PermutationCycles~Length&.
dặm

1
Cũng liên quan đến giải pháp ban đầu của bạn, #&là một chút công bằng ngắn hơn Identity. ;)
Martin Ender

6

Python, 77 69 67 byte

f=lambda p,i=1:i and0 **p[i-1]+f(p[:i-1]+[0]+p[i:],p[i-1]or max(p))

(not p[i-1])có thể được thực hiện dưới dạng0**p[i-1]
xnor

5

Thạch, 12 10 9 byte

ị³$ÐĿ«/QL

Đã lưu 1 byte nhờ @ Dennis .

Điều này sử dụng hoán vị dựa trên 1. Nó hoạt động bằng cách áp dụng hoán vị lặp đi lặp lại cho đến khi đạt được hoán vị trước đó trong khi vẫn giữ các giá trị trước đó. Bằng cách theo dõi các thay đổi, nó sẽ tạo ra quỹ đạo cho mọi giá trị dọc theo các cột của bảng đó. Sau đó, bằng cách tìm mức tối thiểu hoặc tối đa của mỗi cột, một nhãn cho chu trình đó có thể được tạo. Sau đó lặp lại danh sách các nhãn đó và lấy chiều dài của nó sẽ là số chu kỳ rời rạc.

Hãy thử nó ở đây.

Giải trình

ị³$ÐĿ«/QL  Input: permutation p
  $        Chain (ị³) as a monad
 ³           The input p
ị            For each value x, get the value at index x in p
   ÐĿ      Invoke it on p initially, and repeat it on its next value until it returns
           to a previous value and keep track of the results
           This will create a table where each column is the orbit of each value
     «/    Get the minimum value along each column of that table
       Q   Deduplicate
        L  Get the length and return

Cách tiếp cận rất hay!
Luis Mendo

ị³$ÐĿ«/QLnên làm việc.
Dennis

@Dennis Wow, đó là một mẹo gọn gàng! Vì mỗi chu kỳ là khác nhau, nên lấy tối đa / phút và sử dụng nó làm nhãn sẽ đủ để lặp lại + độ dài cho kết quả.
dặm

5

Python, 64 byte

l=input()
for _ in l:l=[min(x,l[x])for x in l]
print len(set(l))

Mã golf này xảy ra là thành ngữ và có thể đọc được. Sử dụng chỉ mục 0.

Mỗi giá trị xem xét những gì nó trỏ đến và những gì giá trị nhọn trỏ đến, và chỉ đến nhỏ hơn của hai. Sau khi lặp lại đủ, mỗi phần tử trỏ đến phần tử nhỏ nhất trong chu kỳ của nó. Số phần tử riêng biệt được trỏ đến sau đó là số chu kỳ.

Nó đủ để làm nlặp đi lặp lại. Ngoài ra, chúng tôi có thể lặp đi lặp lại cho đến khi danh sách không còn thay đổi. Chiến lược này đã cho tôi một hàm đệ quy có cùng độ dài, 64 byte:

f=lambda l,p=0:len(set(l*(l==p)))or f([min(x,l[x])for x in l],l)

Giảm là 65 byte

lambda l:len(set(reduce(lambda l,_:[min(x,l[x])for x in l],l,l)))

Các set(_)chuyển đổi có thể được rút ngắn thành {*_}Python 3.5, tiết kiệm 2 byte.


4

Haskell, 111 byte

l!i|l!!i<0=l|1<2=(take i l++[-1]++drop(i+1)l)!(l!!i)
f(x:y)|x>=0=0|1<2=1+f y
c l|l==[-1|x<-l]=0|1<2=1+c(l!f l)

Sử dụng lập chỉ mục dựa trên 0


4
Chết tiệt, tốt hơn hết là bạn nên có một phông chữ lập trình tốt :)1l!i|iIi!!1ll1|
orlp

@orlp và đó là 111 byte! : O
grooveplex

4

Bình thường, 9 byte

l{mS.u@QN

Sử dụng các chỉ mục dựa trên 0. Hãy thử trực tuyến .

Làm thế nào nó hoạt động

  m         map for d in input:
    .u        cumulative fixed-point: starting at N=d, repeatedly replace N with
      @QN       input[N]
              until a duplicate is found, and return all intermediate results
   S          sort
 {          deduplicate
l           length

3

JavaScript (ES6), 49 byte

a=>a.reduce(g=(c,e,i)=>e<i?g(c,a[e],i):c+=e==i,0)

Sử dụng lập chỉ mục dựa trên không. Giải thích: reduceđược sử dụng để gọi hàm bên trong gtrên mỗi phần tử của mảng. clà số chu kỳ, elà phần tử mảng, ilà chỉ số mảng. Nếu phần tử nhỏ hơn chỉ mục, thì đó là một chu kỳ tiềm năng - phần tử được sử dụng để lập chỉ mục vào mảng để tìm phần tử tiếp theo trong chu kỳ một cách đệ quy. Nếu chúng tôi bắt đầu hoặc kết thúc với chỉ số ban đầu thì đây là một chu kỳ mới và chúng tôi tăng số lượng chu kỳ. Nếu tại bất kỳ thời điểm nào, chúng tôi tìm thấy một giá trị lớn hơn chỉ mục thì chúng tôi sẽ tính chu kỳ đó sau.


Khi tôi chạy mã của bạn trên mảng [2,1,0,3,4,5], nó bị lỗi với thông báo này "Vượt quá kích thước ngăn xếp cuộc gọi tối đa".
kamoroso94

1
@ kamoroso94 Xin lỗi về điều đó, một lỗi đánh máy đã xuất hiện. Nên sửa ngay bây giờ.
Neil

2

C, 90 byte

Gọi f()với một intmảng có thể thay đổi , lập chỉ mục dựa trên 1. Tham số thứ hai là kích thước của mảng. Hàm trả về số chu kỳ.

i,j,c;f(a,n)int*a;{for(c=i=0;i<n;++i)for(j=0,c+=!!a[i];a[i];a[i]=0,i=j-1)j=a[i];return c;}

Hãy thử nó trên ideone .

Thuật toán:

For each index
    If index is non-zero
        Increment counter
        Traverse the cycle, replacing each index in it with 0.

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.