Numero di cicli di una permutazione


23

Considera una permutazione degli interi 1, ..., ncome questa per n = 6:

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

Se si visualizza la permutazione come una mappatura da [1,2,3,4,5,6]a [5,2,4,3,6,1], la permutazione può essere scomposta in cicli disgiunti . Un ciclo è un sottoinsieme di elementi che si mappano l'un l'altro. Ad esempio, 1viene mappato 5, a cui viene mappato 6, a cui viene mappato nuovamente 1. Quindi un ciclo è [1,5,6]. Gli altri cicli sono [2]e [3,4]. Quindi il numero di cicli per questa permutazione è 3.

In generale, i cicli di una permutazione sono unici (fino all'ordine) e il numero di cicli per una permutazione delle dimensioni nvaria da 1a n.

La sfida

Data una permutazione non vuota, emette il suo numero di cicli.

Ingresso è una matrice formata dai nnumeri interi 1, 2, ..., n, dove n > 0. Ogni numero intero si verifica esattamente una volta. L'ordine in cui appaiono definiscono la permutazione, come nell'esempio sopra.

Invece di un array puoi usare un elenco, una stringa con un separatore tra i numeri, un input separato per ogni numero o qualsiasi cosa ragionevole.

Per una permutazione delle dimensioni n, invece del set di numeri interi basato su 1 1, ..., nè possibile utilizzare coerentemente il set in base 0 0, ..., n-1. In tal caso, indicalo nella tua risposta.

Il codice dovrebbe funzionare nfino a 20un tempo ragionevole, diciamo meno di un minuto.

Codice golf. Sono ammessi tutti i builtin.

Casi test

Ciò presuppone un input di array basato su 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

Relazionato

Questa sfida correlata richiede i cicli effettivi della permutazione, non il numero di essi. Richiedere solo il numero di cicli può portare a algoritmi più brevi che evitano di generare i cicli effettivi.


Non importa la mia domanda, è indicato nella domanda è consentito l'input basato su 0.
orlp

@orlp È stato veloce! Non ho nemmeno visto la tua domanda
Luis Mendo,

Possiamo prendere una mappatura degli indici ai valori come input?
Rame

1
@Copper Penso di sì, se il dominio della mappatura è l'insieme 1, ..., nin questo ordine. Puoi chiarire come può una mappatura essere un input? È una struttura di dati?
Luis Mendo,

@LuisMendo Sì, è una struttura di dati, come un Python dict. Voglio avere {1: 2, 2: 1}come input invece di [2, 1].
Rame

Risposte:


12

J, 4 byte

#@C.

Ciò presuppone che la permutazione sia basata su 0. Usa l'integrato C.che ha fornito un elenco che rappresenta una permutazione diretta genera un elenco di cicli. Quindi #composto @da quello restituisce il numero di cicli in quell'elenco.

Provalo qui.


1
Questo è barare! :)
orlp

1
Avrei dovuto vietare i builtin :-D
Luis Mendo il

2
I builtins sono amore. I builtins sono la vita. Sono d'accordo che sarebbe più divertente se i builtin fossero banditi. Sentiti libero di cambiare la regola adesso prima che troppe risposte.
miglia

@miles Nah, lo lascerò così com'è. Buon lavoro!
Luis Mendo,

7

JavaScript, 99 98 byte

Questa soluzione presuppone che l'array e i suoi valori siano indicizzati a zero (ad es [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}

Spiegazione

// 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
Benvenuti in PPCG! Bella prima risposta! Questa è anche una delle migliori, se non la migliore, prima risposta che ho visto nella mia esperienza! Mantenere il buon lavoro!
GamrCorps,

Wow, grazie mille! In realtà ho dovuto cercare come fare i lambda in JavaScript. Non ho ancora familiarità con le cose ES6.
kamoroso94

6

Mathematica, 45 byte

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

Genera un grafico e conta i suoi componenti collegati.


6

Mathematica, 37 28 27 byte

#~PermutationCycles~Length&

Grazie @alephalpha per aver salvato 9 byte e @miles per 1 byte in più.


3
PermutationCycles[#,Length]&
alephalpha,

3
Oh, è pulito. Non sapevo che avrei PermutationCyclespotuto prendere un secondo argomento per alterare la testa del suo output. È inoltre possibile utilizzare la notazione infissa per salvare un altro byte #~PermutationCycles~Length&.
miglia

1
Anche per quanto riguarda la tua soluzione originale, #&è un po 'più breve di Identity. ;)
Martin Ender il

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])può essere fatto come0**p[i-1]
xnor

5

Gelatina, 12 10 9 byte

ị³$ÐĿ«/QL

Salvato 1 byte grazie a @ Dennis .

Questo utilizza permutazioni basate su 1. Funziona applicando ripetutamente la permutazione fino a quando non raggiunge una permutazione precedente pur mantenendo i suoi valori precedenti. Tenendo traccia delle modifiche, creerà l'orbita per ogni valore lungo le colonne di quella tabella. Quindi, trovando il minimo o il massimo di ciascuna colonna, è possibile creare un'etichetta per quel ciclo. Quindi deduplica l'elenco di etichette e ottieni la lunghezza corrispondente al numero di cicli disgiunti.

Provalo qui.

Spiegazione

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

Approccio molto bello!
Luis Mendo,

ị³$ÐĿ«/QLdovrebbe funzionare.
Dennis

@Dennis Wow, è un bel trucco! Poiché ogni ciclo è disgiunto, prendere il massimo / minimo e usarlo come etichetta sarà sufficiente per deduplicare + lunghezza per il risultato.
miglia

5

Python, 64 byte

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

Questo codice golfizzato sembra essere idiomatico e leggibile. Utilizza l'indicizzazione 0.

Ogni valore guarda a cosa punta e a cosa punta il valore puntato, e punta al più piccolo dei due. Dopo sufficienti ripetizioni, ogni elemento indica l'elemento più piccolo del suo ciclo. Il numero di elementi distinti indicati è quindi il numero di cicli.

Basta fare niterazioni. In alternativa, potremmo ripetere fino a quando l'elenco non cambierà più. Questa strategia mi ha dato una funzione ricorsiva della stessa lunghezza, 64 byte:

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

La riduzione era di 65 byte

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

Le set(_)conversioni possono essere abbreviate {*_}in Python 3.5, risparmiando 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)

Utilizza l'indicizzazione basata su 0


4
Accidenti, è meglio avere un buon carattere di programmazione :)1l!i|iIi!!1ll1|
orlp

@orlp ed è 111 byte! : O
grooveplex,

4

Pyth, 9 byte

l{mS.u@QN

Utilizza indici basati su 0. Provalo online .

Come funziona

  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)

Utilizza l'indicizzazione in base zero. Spiegazione: reduceviene utilizzato per richiamare la funzione interna gsu ciascun elemento dell'array. cè il conteggio dei cicli, eè l'elemento array, iè l'indice array. Se l'elemento è inferiore all'indice, si tratta di un potenziale ciclo: l'elemento viene utilizzato per indicizzare l'array per trovare ricorsivamente l'elemento successivo nel ciclo. Se abbiamo iniziato o finito con l'indice originale, questo è un nuovo ciclo e incrementiamo il conteggio dei cicli. Se in qualsiasi momento troviamo un valore maggiore dell'indice, conteggeremo quel ciclo in seguito.


Quando ho eseguito il codice sull'array [2,1,0,3,4,5], si è bloccato con questo messaggio "Dimensione massima dello stack di chiamate superata".
kamoroso94

1
@ kamoroso94 Mi dispiace, si è insinuato un errore di battitura. Ora dovrebbe essere corretto.
Neil,

2

C, 90 byte

Chiama f()con un intarray mutabile , indicizzazione basata su 1. Il secondo parametro è la dimensione dell'array. La funzione restituisce il numero di cicli.

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

Provalo su ideone .

L'algoritmo:

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

2

GAP , 30 byte

Semplice, il secondo argomento da Cyclesdare all'insieme su cui dovrebbe agire la permutazione:

l->Size(Cycles(PermList(l),l))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.