Nombre de cycles d'une permutation

23

Considérons une permutation des entiers 1, ... n,, comme celui-ci pour n = 6:

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

Si vous voyez la permutation comme un mappage de [1,2,3,4,5,6]à [5,2,4,3,6,1], la permutation peut être décomposée en cycles disjoints . Un cycle est un sous-ensemble d'éléments qui se mappent les uns aux autres. Par exemple, 1obtient mappé sur 5, qui est mappé sur 6, qui est mappé sur 1. Donc, un cycle est [1,5,6]. Les autres cycles sont[2] et [3,4]. Ainsi, le nombre de cycles pour cette permutation est 3.

En général, les cycles d'une permutation sont uniques (dans l'ordre) et le nombre de cycles pour une permutation de taille nvarie de 1à n.

Le défi

Étant donné une permutation non vide, affichez son nombre de cycles.

L' entrée est une matrice formée par les nnombres entiers 1, 2...,nn > 0. Chaque entier apparaît exactement une fois. L'ordre dans lequel ils apparaissent définit la permutation, comme dans l'exemple ci-dessus.

Au lieu d'un tableau, vous pouvez utiliser une liste, une chaîne avec un séparateur entre les nombres, une entrée distincte pour chaque numéro, ou tout ce qui est raisonnable.

Pour une permutation de taille n, au lieu de l'ensemble basé sur 1 d'entiers 1, ..., nvous pouvez toujours utiliser l'ensemble basé sur 00 , ..., n-1. Si oui, veuillez l'indiquer dans votre réponse.

Le code devrait fonctionner njusqu'à 20un délai raisonnable, disons moins d'une minute.

Code golf. Tous les buildins autorisés.

Cas de test

Cela suppose une entrée de tableau basée sur 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

en relation

Ce défi connexe demande les cycles réels de la permutation, pas le nombre d'entre eux. Ne nécessiter que le nombre de cycles peut conduire à des algorithmes plus courts qui contournent la génération des cycles réels.

Luis Mendo
la source
Peu importe ma question, il est indiqué dans la question que l'entrée basée sur 0 est autorisée.
orlp
@orlp C'était rapide! Je n'ai même pas pu voir votre question
Luis Mendo
Pouvons-nous prendre un mappage d'index à des valeurs en entrée?
Copper
1
@Copper Je pense que oui, si le domaine de la cartographie est l'ensemble 1, ..., ndans cet ordre. Pouvez-vous préciser comment un mappage peut être une entrée? Est-ce une structure de données?
Luis Mendo
@LuisMendo Oui, c'est une structure de données, comme un Python dict. Je veux avoir {1: 2, 2: 1}une entrée au lieu de [2, 1].
Copper

Réponses:

12

J, 4 octets

#@C.

Cela suppose que la permutation est basée sur 0. Il utilise la fonction intégrée C.qui, à partir d'une liste représentant une permutation directe, génère une liste de cycles. Puis #composé @sur qui renvoie le nombre de cycles dans cette liste.

Essayez-le ici.

miles
la source
1
C'est de la triche! :)
orlp
1
J'aurais dû interdire les buildins :-D
Luis Mendo
2
Les bâtis sont l'amour. Les bâtis sont la vie. Je suis d'accord que ce serait plus amusant d'interdire les buildins. N'hésitez pas à changer la règle dès maintenant avant de trop répondre.
miles
@miles Nah, je vais le laisser tel quel. Bon travail!
Luis Mendo
7

JavaScript, 99 98 octets

Cette solution suppose que le tableau et ses valeurs sont indexés à zéro (par exemple [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}

Explication

// 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
};
kamoroso94
la source
3
Bienvenue chez PPCG! Belle première réponse! C'est aussi l'une des meilleures, sinon la meilleure, première réponse que j'ai vue dans mon expérience! Continuez votre bon travail!
GamrCorps
Woah, merci beaucoup! En fait, j'ai dû chercher comment faire les lambdas en JavaScript. Je ne suis pas encore familier avec les trucs ES6.
kamoroso94
6

Mathematica, 45 octets

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

Il génère un graphique et compte ses composants connectés.

alephalpha
la source
6

Mathematica, 37 28 27 octets

#~PermutationCycles~Length&

Merci à @alephalpha d'avoir enregistré 9 octets et à @miles pour 1 octet de plus.

Martin
la source
3
PermutationCycles[#,Length]&
alephalpha
3
Oh c'est bien. Je ne savais pas qu'il PermutationCyclespouvait prendre un deuxième argument pour modifier la tête de sa sortie. Vous pouvez également utiliser la notation infixe pour enregistrer un autre octet #~PermutationCycles~Length&.
miles du
1
En ce qui concerne également votre solution d'origine, #&est un peu plus court que Identity. ;)
Martin Ender
6

Python, 77 69 67 octets

f=lambda p,i=1:i and0 **p[i-1]+f(p[:i-1]+[0]+p[i:],p[i-1]or max(p))
orlp
la source
(not p[i-1])peut être fait comme0**p[i-1]
xnor
5

Gelée, 12 10 9 octets

ị³$ÐĿ«/QL

1 octet enregistré grâce à @ Dennis .

Cela utilise des permutations basées sur 1. Il fonctionne en appliquant la permutation à plusieurs reprises jusqu'à ce qu'il atteigne une permutation précédente tout en conservant ses valeurs précédentes. En gardant une trace des changements, il créera l'orbite pour chaque valeur le long des colonnes de cette table. Ensuite, en trouvant le minimum ou le maximum de chaque colonne, une étiquette pour ce cycle peut être créée. Dédupliquez ensuite cette liste d'étiquettes et obtenez sa longueur qui sera le nombre de cycles disjoints.

Essayez-le ici.

Explication

ị³$ÐĿ«/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
miles
la source
Très belle approche!
Luis Mendo
ị³$ÐĿ«/QLdevrait marcher.
Dennis
@Dennis Wow, c'est une astuce intéressante! Étant donné que chaque cycle est disjoint, prendre soit le max / min et l'utiliser comme une étiquette sera suffisant pour dédupliquer + longueur pour le résultat.
miles
5

Python, 64 octets

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

Ce code golfé est idiomatique et lisible. Utilise l'indexation 0.

Chaque valeur examine ce vers quoi elle pointe et vers quoi pointe la valeur pointée et pointe vers la plus petite des deux. Après suffisamment de répétitions, chaque élément pointe vers le plus petit élément de son cycle. Le nombre d'éléments distincts pointés est alors le nombre de cycles.

Il suffit de faire des nitérations. Alternativement, nous pourrions répéter jusqu'à ce que la liste ne change plus. Cette stratégie m'a donné une fonction récursive de même longueur, 64 octets:

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

La réduction était de 65 octets

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

Les set(_)conversions peuvent être raccourcies {*_}en Python 3.5, économisant 2 octets.

xnor
la source
4

Haskell, 111 octets

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)

Utilise l'indexation basée sur 0

Programme homme
la source
4
Merde, vous feriez mieux d'avoir une bonne police de programmation :)1l!i|iIi!!1ll1|
orlp
@orlp et c'est 111 octets! : O
grooveplex
4

Pyth, 9 octets

l{mS.u@QN

Utilise des index basés sur 0. Essayez-le en ligne .

Comment ça marche

  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
Anders Kaseorg
la source
3

JavaScript (ES6), 49 octets

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

Utilise une indexation à base zéro. Explication: reduceest utilisée pour appeler la fonction interne gsur chaque élément du tableau. cest le nombre de cycles, eest l'élément de tableau, iest l'indice de tableau. Si l'élément est inférieur à l'index, alors c'est un cycle potentiel - l'élément est utilisé pour indexer dans le tableau pour trouver l'élément suivant dans le cycle de manière récursive. Si nous avons commencé ou fini avec l'index d'origine, alors c'est un nouveau cycle et nous incrémentons le nombre de cycles. Si à un moment donné, nous trouvons une valeur supérieure à l'indice, nous comptons ce cycle plus tard.

Neil
la source
Lorsque j'ai exécuté votre code sur le tableau [2,1,0,3,4,5], il s'est écrasé avec ce message "La taille maximale de la pile d'appels a été dépassée".
kamoroso94
1
@ kamoroso94 Désolé, une faute de frappe s'est glissée. Devrait être corrigée maintenant.
Neil
2

C, 90 octets

Appelez f()avec un inttableau mutable , 1 indexation basée. Le deuxième paramètre est la taille du tableau. La fonction renvoie le nombre de cycles.

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

Essayez-le sur ideone .

L'algorithme:

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

GAP , 30 octets

Simple, le deuxième argument Cyclesdonne l’ensemble sur lequel la permutation doit agir:

l->Size(Cycles(PermList(l),l))
Christian Sievers
la source