排列的循环数


23

考虑整数... ... 的置换,例如1nn = 6

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

如果将置换视为从[1,2,3,4,5,6]到的映射[5,2,4,3,6,1],则置换可以分解为不相交的周期。循环是相互映射的元素的子集。例如,1被映射到5,被映射到6,被映射回1。所以一个周期是[1,5,6]。其他周期是[2][3,4]。因此,此置换的周期数3

通常,排列的周期是唯一的(按顺序排列),大小排列的周期数n1到变化n

挑战

给定非空排列,输出其循环数。

输入是由形成的阵列n的整数12,..., n,其中n > 0。每个整数仅出现一次。如上例所示,它们出现的顺序定义了排列。

可以使用列表,在数字之间使用分隔符的字符串,每个数字的单独输入或任何合理的方法来代替数组。

对于大小的排列n,而不是基于1的整数集1,......,n你可以一直使用基于0套0,... n-1。如果是这样,请在您的答案中指出。

该代码应努力n达到20在合理的时间,说不到一分钟。

代码高尔夫。允许所有内置。

测试用例

假定基于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

有关

这个相关的挑战要求排列的实际周期,而不是数量。仅要求周期数可以导致产生实际周期的较短算法。


没关系,我的问题已在允许基于0的输入中说明。
orlp

@orlp那太快了!我什至没有看到您的问题
Luis Mendo

我们能否将索引到值的映射作为输入?

1
@Copper我认为是的,如果映射的域是按顺序设置的1...。n您能否阐明映射如何成为输入?它是数据结构吗?
Luis Mendo

@LuisMendo是的,它是一个数据结构,就像Python一样dict。我想{1: 2, 2: 1}代替输入[2, 1]

Answers:


12

J,4个字节

#@C.

这假设排列是基于0的。它使用内置C.函数,给定一个代表直接排列的列表,输出一个周期列表。然后#组成@该函数,返回该列表中的循环数。

在这里尝试。


1
那是作弊!:)
orlp

1
我本应该禁止内置命令的:-D
路易斯·门多

2
内在就是爱。内在就是生命。我确实同意,禁止内置命令会更有趣。在回答过多之前,随时可以更改规则。
英里

@miles不,我将保持原样。做得好!
路易斯·门多

7

JavaScript,99 98字节

此解决方案假定数组及其值的索引为零(例如[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}

说明

// 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
欢迎来到PPCG!不错的第一答案!根据我的经验,这也是最好的(即使不是最好的)第一答案!保持良好的工作!
GamrCorps'8

哇,非常感谢你!实际上,我不得不查找如何在JavaScript中执行lambda。我还不太熟悉ES6。
kamoroso94 '16

6

Mathematica,45个字节

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

它生成一个图形并计算其连接的组件。


6

Mathematica,37 28 27字节

#~PermutationCycles~Length&

感谢@alephalpha保存9个字节,而@miles再保存1个字节。


3
PermutationCycles[#,Length]&
alephalpha '16

3
哦,很整洁。我不知道PermutationCycles第二个参数可以更改其输出的标题。您还可以使用中缀表示法保存另一个字节#~PermutationCycles~Length&
2016年

1
另外,关于您的原始解决方案,#&它比还要短一些Identity。;)
Martin Ender

6

Python,77 69 67字节

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])可以完成0**p[i-1]
xnor

5

果冻12 10 9 字节

ị³$ÐĿ«/QL

由于@ Dennis节省了1个字节。

这使用基于1的排列。它的工作方式是反复应用置换,直到达到先前的置换,同时还保留其先前的值。通过跟踪更改,它将为沿该表的列的每个值创建轨道。然后,通过查找每列的最小值或最大值,可以创建该循环的标签。然后对标签列表进行重复数据删除,然后获取标签的长度,该长度将是不相交的循环数。

在这里尝试。

说明

ị³$ÐĿ«/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

很好的方法!
Luis Mendo'8

ị³$ÐĿ«/QL应该管用。
丹尼斯

@Dennis Wow,那真是个绝招!由于每个周期都是不相交的,因此采用max / min并将其用作标签将足以对结果进行重复数据删除+长度。
英里

5

Python,64位元组

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

这个打高尔夫球的代码恰好是习惯用法和可读性。使用0索引。

每个值查看其指向的内容以及指向的值指向的内容,并指向两者中较小的一个。经过足够的重复,每个元素都指向其循环中的最小元素。那么,所指向的不同元素的数量就是循环的数量。

进行n迭代就足够了。或者,我们可以迭代直到列表不再更改。这种策略给了我相同长度的递归函数,长度为64个字节:

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

减少为65个字节

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

set(_)转换可以缩短到{*_}在Python 3.5,保存2个字节。


4

Haskell,111个字节

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)

使用基于0的索引


4
该死的,你最好有一个好的编程字体:)1l!i|iIi!!1ll1|
orlp

@orlp,它是111个字节!:O
grooveplex

4

Pyth,9个字节

l{mS.u@QN

使用基于0的索引。 在线尝试

怎么运行的

  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个字节

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

使用基于零的索引。说明:reduce用于g在数组的每个元素上调用内部函数。c是周期数,e是数组元素,i是数组索引。如果该元素小于索引,则它是一个潜在的循环-该元素用于索引数组以递归查找循环中的下一个元素。如果我们以原始索引开始或结束,则这是一个新的循环,因此我们增加了循环计数。如果在任何时候我们发现一个大于索引的值,那么我们将在以后计算该周期。


当我在数组上运行您的代码时[2,1,0,3,4,5],它崩溃,并显示以下消息:“已超出最大调用堆栈大小”。
kamoroso94 '16

1
@ kamoroso94抱歉,一个错字已经潜入。现在应该修复。
尼尔

2

C,90字节

f()使用可变int数组(基于1的索引)进行调用。第二个参数是数组的大小。该函数返回循环数。

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;}

在ideone上尝试

算法:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.