Combinata: Combina il sottofattoriale


25

I numeri sottofattoriali o rencontres ( A000166 ) sono una sequenza di numeri simili ai numeri fattoriali che si presentano nella combinatoria delle permutazioni. In particolare, il n ° subfactorial ! N indica il numero di alterazioni di un insieme di n elementi. Uno squilibrio è una permutazione in cui nessun elemento rimane nella stessa posizione. Il sottofattoriale può essere definito tramite la seguente relazione di ricorrenza:

!n = (n-1) (!(n-1) + !(n-2))

In effetti, la stessa relazione di ricorrenza vale per il fattoriale, ma per il sottofattoriale partiamo da:

!0 = 1
!1 = 0

(Per il fattoriale avremmo, ovviamente, 1! = 1. )

Il tuo compito è calcolare ! N , dato n .

Regole

Come il fattoriale, il sottofattoriale cresce molto rapidamente. Va bene se il tuo programma può gestire solo input n tale che ! N può essere rappresentato dal tipo di numero nativo della tua lingua. Tuttavia, il tuo algoritmo deve in teoria funzionare per n . Ciò significa che puoi presumere che i risultati integrali e il valore intermedio possano essere rappresentati esattamente dalla tua lingua. Si noti che ciò esclude la costante e se è memorizzata o calcolata con precisione finita.

Il risultato deve essere un numero intero esatto (in particolare, non è possibile approssimare il risultato con notazione scientifica).

È possibile scrivere un programma o una funzione e utilizzare uno dei metodi standard ricevere input e fornire output.

È possibile utilizzare qualsiasi linguaggio di programmazione , ma si noti che queste scappatoie sono vietate per impostazione predefinita.

Questo è , quindi la risposta valida più breve - misurata in byte - vince.

Casi test

n     !n
0     1
1     0
2     1
3     2
4     9
5     44
6     265
10    1334961
12    176214841
13    2290792932
14    32071101049
20    895014631192902121
21    18795307255050944540
100   34332795984163804765195977526776142032365783805375784983543400282685180793327632432791396429850988990237345920155783984828001486412574060553756854137069878601

Risposte:


19

Funciton , 336 byte

Il conteggio dei byte presuppone la codifica UTF-16 con la distinta base.

┌─╖┌─╖  ┌─╖ 
│f╟┤♭╟┐┌┤♭╟┐
╘╤╝╘═╝├┘╘═╝├────┐
 │┌─╖ │ ┌┐┌┘╔═╗╓┴╖
 ││f╟─┴┐└┴┼─╢0║║f║
 │╘╤╝  │  │ ╚═╝╙─╜
 │┌┴╖ ┌┴╖┌┴╖ ╔═╗
 ││+╟┐│×╟┤?╟┐║1║
 │╘╤╝│╘╤╝╘╤╝┘╚╤╝
 └─┘ └─┘  └───┘

Questo definisce una funzione f che accetta un numero intero e genera un altro numero intero con una rotazione di 90 gradi a sinistra. Funziona con input arbitrariamente grandi.

Provalo online!

Considerando che questo è Funciton è anche ragionevolmente veloce (n = 20 richiede circa 14 secondi su TIO). Il rallentamento principale deriva dalla doppia ricorsione, poiché non credo che l'interprete di Funciton memorizzi automaticamente le funzioni.

Sfortunatamente, alcuni caratteri a spaziatura fissa non eseguono correttamente il monospazio e / o inseriscono piccoli spazi tra le linee. Ecco uno screenshot del codice di TIO in tutta la sua bellezza:

inserisci qui la descrizione dell'immagine

Penso che potrebbe essere possibile a golf questo un po 'di più, per esempio modificando la condizione di >0per <1e scambiando i rami della condizionale, in modo da poter riutilizzare il numero letterale, o forse utilizzando una formula completamente diversa, ma sono abbastanza felice di quanto sia già compatto.

Spiegazione

Questo in pratica implementa la ricorsione fornita nella sfida, sebbene utilizzi il caso base ! (- 1) =! 0 = 1 . n-1 e n-2 vengono calcolati con la funzione predecessore e il risultato intermedio n-1 viene riutilizzato in tre punti. Non c'è molto altro, quindi passerò rapidamente attraverso il flusso di controllo:

               ─┐
               ╓┴╖
               ║f║
               ╙─╜

Questa è l'intestazione funzione che emette ingresso della funzione n lungo la linea divisoria. Questo raggiunge immediatamente la giunzione a T, che duplica semplicemente il valore.

        ┌┐┌┘╔═╗
        └┴┼─╢0║
          │ ╚═╝

La 0casella è solo un valore letterale numerico. Una giunzione a 4 vie calcola due funzioni: il percorso che porta in fondo calcola 0 <n , che useremo per determinare il caso base. Il percorso che va a sinistra calcola separatamente 0 << n (uno spostamento a sinistra), ma scartiamo questo valore con il costrutto Starkov .

         ┌┴╖ ╔═╗
         ┤?╟┐║1║
         ╘╤╝┘╚╤╝
          └───┘

Conduciamo questo nel condizionale a tre vie ?. Se il valore è falso, restituiamo il risultato costante 1. L'estremità libera a destra di ?è l'uscita della funzione. Sto ruotando di 180 gradi qui, in modo che l'orientamento relativo di input e output di fsia più conveniente nel resto del programma.

Se la condizione era vera, verrà utilizzato l'altro valore. Diamo un'occhiata al percorso che conduce a questo ramo. (Si noti che la valutazione di Funciton è in realtà pigra, quindi questo ramo non verrà mai valutato se non è necessario, il che rende possibile la ricorsione in primo luogo.)

        ┌─╖ 
      ┐┌┤♭╟┐
      ├┘╘═╝
      │
     ─┴┐

Nell'altro ramo calcoliamo prima n-1 e quindi dividiamo il percorso due volte in modo da ottenere tre copie del valore (una per il coefficiente di ricorrenza, una per il primo sottofattoriale, l'ultima per n-2 ).

┌─╖┌─╖
│f╟┤♭╟
╘╤╝╘═╝
 │┌─╖
 ││f╟
 │╘╤╝
 │┌┴╖
 ││+╟
 │╘╤╝
 └─┘ 

Come ho detto, decrementiamo di nuovo una copia con un'altra , quindi alimentiamo sia n-1 che n-2 in modo ricorsivo fe infine sommiamo i due risultati nel file +.

       ┐
       │
      ┌┴╖
     ┐│×╟
     │╘╤╝
     └─┘

Non resta che moltiplicare n-1 per ! (N-1) +! (N-2) .


13

Oasi , 5 byte

Usa la formula data da Martin. Codice:

+n<*X

Versione dissezionata:

+n<*

con a(0) = 1e a(1) = 0.

Spiegazione a(n) =:

+       # Add the previous two terms, a(n - 1) + a(n - 2).
 n<     # Compute n - 1.
   *    # Multiply the top two elements.

Provalo online!


Bel trucco usando X:-) A proposito, hai implementare questo ancora? Uno di questi giorni non saremo in grado di cavartela cambiando i valori iniziali
Luis Mendo il

@LuisMendo Sì, l'ho fatto! È usato come flag di comando ( ecco un link alla pagina delle informazioni). Grazie per il suggerimento :).
Adnan,


7

Gelatina , 7 byte

R=Œ!Ḅċ0

Questo approccio costruisce gli squilibri, quindi è piuttosto lento.

Provalo online!

Come funziona

R=Œ!Ḅċ0  Main link. Argument: n

R        Range; yield [1, ..., n].
  Œ!     Yield all permutations of [1, ..., n].
 =       Perform elementwise comparison of [1, ..., n] and each permutation.
    Ḅ    Unbinary; convert each result from base 2 to integer. This yields 0 for
         derangements, a positive value otherwise.
     ċ0  Count the number of zeroes.

7

Brachylog (2), 11 byte

⟦₁{p:?\≠ᵐ}ᶜ

Provalo online!

Spiegazione

Questa è fondamentalmente solo una traduzione diretta della specifica dall'inglese al Brachylog (e ha quindi il vantaggio di poter essere facilmente modificata per gestire piccole modifiche alla specifica, come trovare il numero di variazioni di un elenco specifico).

⟦₁{p:?\≠ᵐ}ᶜ
⟦₁           Start with a list of {the input} distinct elements
  {      }ᶜ  Then count the number of ways to
   p         permute that list
      \      such that taking corresponding elements
    :?       in {the permutation} and the list of distinct elements
       ≠     gives different elements
        ᵐ    at every position

5

Lingue con soluzioni integrate

Seguendo il suggerimento di xnor questa è una risposta in CW in cui dovrebbero essere modificate le soluzioni banali basate su un singolo built-in per calcolare il sottofattoriale o generare tutti gli squilibri.

Mathematica, 12 byte

Subfactorial

sospiro Mathematica ...
epicbob57

5

Python 3 , 35 32 byte

f=lambda n:n<1or(-1)**n+n*f(n-1)

Questo utilizza la relazione di ricorrenza ! N = n! (N-1) + (-1) n dalla risposta Haskell di @ Laikoni , con il caso base ! 0 = 1 .

Provalo online!


Penso che si può anche utilizzare l'altra equazione data qui , che permetterebbe di risparmiare due byte: f=lambda n:n<1or n*f(n-1)+(-1)**n.
Adnan,

1
Tre byte con un piccolo riordino. ;)
Dennis il

1
La parte divertente di questa ricorrenza è che se riporti il ​​case base a n=-1, non importa affatto quale valore usi. Ciò potrebbe essere utile per alcune lingue (ad es. In Mathematica potresti effettivamente lasciarlo indefinito se salvasse qualche byte).
Martin Ender,

5

M , 9 byte

o2!÷Øe+.Ḟ

Come puoi vedere rimuovendo , M usa la matematica simbolica, quindi non ci saranno problemi di precisione.

Provalo online! Non la soluzione più breve che è stata pubblicata, ma veloce .

Come funziona

o2!÷Øe+.Ḟ  Main link. Argument: n

o2         Replace input 0 with 2, as the following formula fails for 0.
  !        Compute the factorial of n or 2.
   ֯e     Divide the result by e, Euler's natural number.
      +.   Add 1/2 to the result.
        Ḟ  Floor; round down to the nearest integer.

5

MATL , 9 8 byte

:tY@-!As

Analogamente alla risposta di Jelly di @Dennis , questo in realtà costruisce le permutazioni e conta quante di esse sono squilibri; quindi è lento.

Provalo online!

:     % Input n implicitly: Push [1 2 ... n]
t     % Duplicate 
Y@    % Matrix of all permutations, each on a row
-     % Element-wise subtract. A zero in a row means that row is not a derangement
!     % Transpose
A     % True for columns that don't contain zeros
s     % Sum. Implicitly display

3

Matematica , 21 byte

Round@If[#>0,#!/E,1]&

Sono molto nuovo e non ho idea di cosa sto facendo ...

Provalo online!


1
Due alternative allo stesso numero di byte: Round[(#/. 0->2)!/E]&e ±0=1;±n_:=Round[n!/E](anche se non so se Mathics supporta la codifica a byte singolo per i file di origine come Mathematica).
Martin Ender,

Il primo funziona bene ( penso di sapere cosa fa), ma la matematica non sembra supportare ±nel secondo. Funzionerebbe con f, ma al costo di due byte.
Dennis,

Un altro allo stesso byte Count: Round[#!/E]+1-Sign@#&. Valori iniziali fastidiosi ...!
Greg Martin

3

Rubino, 27 byte

f=->n{n<1?1:n*f[n-1]+~0**n}

~0**nè più corto di (-1)**n!


3

CJam (10 byte)

1qi{~*)}/z

Demo online .

Questo utilizza la ricorrenza !n = n !(n-1) + (-1)^n, da cui ho derivato n! / ee poi scoperto che era già in OEIS.

Dissezione

Il ciclo calcola (-1)^n !n, quindi alla fine dobbiamo prendere il valore assoluto:

1     e# Push !0 to the stack
qi{   e# Read an integer n and loop from 0 to n-1
  ~   e#   Bitwise not takes i to -(i+1), so we can effectively loop from 1 to n
  *   e#   Multiply
  )   e#   Increment
}/
z     e# Take the absolute value

2

05AB1E , 8 byte

΃N*®Nm+

Provalo online!

Spiegazione

Î         # initialize stack with 0 and input
 ƒ        # for N in range [0 ... input]:
  N*      # multiply top of stack with N
    ®Nm   # push (-1)^N
       +  # add

2

MATLAB, 33 byte

@(n)(-1)^n*hypergeom([1 -n],[],1)

Funzione anonima che utilizza la formula nella sezione 3 di Derangements e applicazioni di Mehdi Hassani.

Esempio di utilizzo:

>> @(n)(-1)^n*hypergeom([1 -n],[],1)
ans = 
    @(n)(-1)^n*hypergeom([1,-n],[],1)
>> ans(6)
ans =
   265

2

JavaScript (ES6), 26 byte

f=n=>!n||n*f(n-1)-(~n%2|1)

Utilizza la relazione di ricorrenza dalla risposta di @ Laikoni. In ES7 è possibile salvare un byte utilizzando +(-1)**ninvece di -(~n%2|1).


2

PostScript, 81 76 69 byte

Ecco le implementazioni di entrambe le formule.

n * f (n-1) + (- 1) ^ n

/ f {dup 0 eq {pop 1} {dup dup 1 sub f mul exch 2 mod 2 mul 1 sub sub} ifelse} def

/f{dup 0 eq{pop 1}{dup dup 1 sub f mul -1 3 2 roll exp add}ifelse}def

Quella versione genera un float. Se è necessario generare un numero intero:

/f{dup 0 eq{pop 1}{dup dup 1 sub f mul -1 3 2 roll exp cvi add}ifelse}def

che pesa 73 byte.

L'altra formula è un po 'più lunga: 81 byte.

(N-1) * (f (n-1) + f (n-2))

/f{dup 1 le{1 exch sub}{1 sub dup f exch dup 1 sub f 3 -1 roll add mul}ifelse}def

Queste funzioni ottengono il loro argomento dallo stack e lasciano il risultato nello stack.

È possibile testare le funzioni, in un file o in un prompt PostScript interattivo (ad es. GhostScript) con

0 1 12{/i exch def [i i f] ==}for

produzione

[0 1]
[1 0.0]
[2 1.0]
[3 2.0]
[4 9.0]
[5 44.0]
[6 265.0]
[7 1854.0]
[8 14833.0]
[9 133496.0]
[10 1334961.0]
[11 14684570.0]
[12 176214848.0]

Ecco un file PostScript completo che esegue il rendering dell'output sullo schermo o su una pagina della stampante. (I commenti in PostScript iniziano con %).

%!PS-Adobe-3.0

% (n-1)*(f(n-1)+f(n-2))
% /f{dup 1 le{1 exch sub}{1 sub dup f exch dup 1 sub f 3 -1 roll add mul}ifelse}def

% n*f(n-1)+(-1)^n
/f{dup 0 eq{pop 1}{dup dup 1 sub f mul -1 3 2 roll exp add}ifelse}def

% 0 1 12{/i exch def [i i f] ==}for

/FS 16 def              %font size
/LM 5 def               %left margin
/numst 12 string def    %numeric string buffer

/Newline{currentpoint exch pop FS sub LM exch moveto}def
/Courier findfont FS scalefont setfont
LM 700 moveto

(Subfactorials) Newline
0 1 12{
    dup numst cvs show (: ) show f numst cvs show Newline
}for
showpage
quit

1
-1 3 2 roll expè un po 'più breve di exch 2 mod 2 mul 1 sub.
Peter Taylor,

@PeterTaylor Così è! :) Mi sono dimenticato di exp: oops: Tuttavia, restituisce un float e penso di dover emettere un numero intero per conformarmi alla domanda.
PM 2Ring

1
Penso che tu possa ancora lanciarmi in un cvie salvare. (Nota: non testato, ma dalla lettura del documento penso che dovrebbe funzionare).
Peter Taylor,

@PeterTaylor Sì, cvifunziona ed è ancora più breve della mia versione precedente.
PM 2Ring

1

PHP, 69 byte

function f($i){return$i>1?$i*f($i-1)+(-1)**$i:1-$i;}echo f($argv[1]);

usare in questo modo a(n) = n*a(n-1) + (-1)^n


1
Hai solo bisogno di dare la funzione, non il programma completo, quindi puoi eliminare gli ultimi 17 caratteri. C'è un ulteriore risparmio in quanto l'input non ha un case speciale 1. Penso che i due risparmi lo portino a 47 byte.
Peter Taylor,

1

PHP, 50 44

for(;$i++<$argn;)$n=++$n*$i-$i%2*2;echo$n+1;

Corri con echo <n> | php -nR '<code>

Il bello a(n) = n*a(n-1) + (-1)^nè che dipende solo dal valore precedente. Ciò consente di implementarlo in modo iterativo anziché ricorsivo. Ciò consente di risparmiare una dichiarazione di funzione lunga .

-6 byte di @Titus. Grazie !


-1 byte: $i++<$argv[1]. -2 byte: for(;$i++<$argv[1];)$n=++$n*$i-$i%2*2;echo$n+1;. (-3 byte con -Re $argn.)
Tito

@Titus qualcuno si è annoiato? : D ti dispiacerebbe darmi un esempio per -Re $argn?
Christoph

1
Non annoiato, ma desideroso di golf. Vedi php.net/manual/de/features.commandline.options.php: echo <input> | php -nR '<code>'. esempio: codegolf.stackexchange.com/a/113046
Tito

1
@Titus ho capito bene? ;-)
Christoph

0

Mathematica, 40 byte

±0=1;±1=0;±n_:=(n-1)(±(n-1)+±(n-2))

Che arriva a 31 byte con la codifica ISO 8859-1 predefinita.


0

C, 34 byte

a(n){return n?n*a(n-1)-n%2*2+1:1;}

Spiegazione:

a(n){                            } define a function called a of n
     return                     ;  make the function evaluate to...
            n?                :1   set the base case of 1 when n is 0
              n*a(n-1)             first half of the formula on the page
                      -n%2*2+1     (-1)**n

0

R, 47 byte

n=scan();`if`(!n,1,floor(gamma(n+1)/exp(1)+.5))

Utilizza la stessa formula della risposta di Mego .

Metodo alternativo, 52 byte utilizzando la PerMallowslibreria

n=scan();`if`(!n,1,PerMallows::count.perms(n,n,'h'))

0

In realtà , 18 byte

;!@ur⌠;!@0Dⁿ/⌡MΣ*≈

Provalo online!

Spiegazione:

;!@ur⌠;!@0Dⁿ/⌡MΣ*≈
;                   duplicate input
 !                  n!
  @ur               range(0, n+1) (yields [0, n])
     ⌠;!@0Dⁿ/⌡M     for each i in range:
      ;               duplicate i
       !              i!
        @0Dⁿ          (-1)**i
            /         (-1)**i/i!
               Σ    sum
                *   multiply sum by n!
                 ≈  floor into int

Una versione a 12 byte che funzionerebbe se effettivamente avesse più precisione:

;!╠@/1½+L@Y+

Provalo online!

A differenza di tutte le altre risposte (a partire dalla pubblicazione), questa soluzione non utilizza né la formula ricorsiva né la formula di somma. Invece, utilizza la seguente formula:

formula di derangement

Questa formula è relativamente facile da implementare in Realmente:

!╠@/1½+L
!         n!
 ╠        e
  @/      divide n! by e
    1½+   add 0.5
       L  floor

Ora, l'unico problema è che la formula vale solo per il positivo n. Se si tenta di utilizzare n = 0, la formula restituisce in modo errato 0. Ciò è facilmente risolvibile, tuttavia: applicando la negazione booleana all'input e aggiungendola all'output della formula, viene fornito l'output corretto per tutti i valori non negativi n. Pertanto, il programma con quella correzione è:

;!╠@/1½+L@Y+
;             duplicate input
 !            n!
  ╠           e
   @/         divide n! by e
     1½+      add 0.5
        L     floor
         @Y   boolean negate the other copy of the input (1 if n == 0 else 0)
           +  add

Continua a dare risposte negative per me ...
Leaky Nun

@LeakyNun Questo è a causa dei limiti di precisione. Per input di grandi dimensioni (circa n = 100), (-1)**n/n!non può essere rappresentato con float IEEE 754 a precisione doppia. È accettabile secondo la sfida.
Mego

Anche per n=4...
Leaky Nun

@LeakyNun Oh. Non so perché stavo usando la divisione pavimentata. Risolvendolo ora.
Mego



0

Alice , 20 18 byte

1/o
k\i@/&wq*eqE+]

Provalo online!

Spiegazione

Questo utilizza la ricorsione da Laikoni di risposta , ! N = n! (N-1) + (-1) n . Simile alla risposta di Funciton, sto usando il caso base ! (- 1) = 1 . Abbiamo messo quello 1 in pila con 1.. Poi questo...

.../o
...\i@/...

... è solo il solito framework I / O decimale. Il codice principale è in realtà

&wq*eqE+]k

Suddiviso:

&w    Push the current IP address N times to the return address stack, which
      effectively begins a loop which will be executed N+1 times.
  q     Push the position of the tape head, which we're abusing as the
        iterator variable n.
  *     Multiply !(n-1) by n.
  e     Push -1.
  q     Retrieve n again.
  E     Raise -1 to the nth power.
  +     Add it to n*!(n-1).
  ]     Move the tape head to the right.
k     Jump back to the w, as long as there is still a copy of the return
      address on the return address stack. Otherwise, do nothing and exit
      the loop.
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.