Combinatória demente: calcular o subfatorial

25

Os números subfatoriais ou rencontros ( A000166 ) são uma sequência de números semelhante aos números fatoriais que aparecem na combinatória de permutações. Em particular, o n º subfactorial ! N dá o número de desarranjos de um conjunto de n elementos. Um desarranjo é uma permutação na qual nenhum elemento permanece na mesma posição. O subfatorial pode ser definido através da seguinte relação de recorrência:

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

De fato, a mesma relação de recorrência vale para o fatorial, mas para o subfatorial partimos de:

!0 = 1
!1 = 0

(Para o fatorial, teríamos, é claro, 1! = 1. )

Sua tarefa é calcular ! N , dado n .

Regras

Como o fatorial, o subfatorial cresce muito rapidamente. Não há problema se o seu programa puder lidar apenas com entradas n , de forma que ! N possa ser representado pelo tipo de número nativo do seu idioma. Entretanto, em teoria , seu algoritmo deve trabalhar para n arbitrário . Isso significa que você pode assumir que os resultados integrais e o valor intermediário podem ser representados exatamente pelo seu idioma. Observe que isso exclui a constante e se ela é armazenada ou calculada com precisão finita.

O resultado precisa ser um número inteiro exato (em particular, você não pode aproximar o resultado com notação científica).

Você pode escrever um programa ou uma função e usar qualquer um dos métodos padrão de recebimento de entrada e saída.

Você pode usar qualquer linguagem de programação , mas observe que essas brechas são proibidas por padrão.

Isso é , então a resposta mais curta e válida - medida em bytes - vence.

Casos de teste

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
Martin Ender
fonte
Relacionado.
Martin Ender

Respostas:

19

Função , 336 bytes

A contagem de bytes assume a codificação UTF-16 com a BOM.

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

Isso define uma função fque pega um número inteiro e gera outro número inteiro a uma volta de 90 graus para a esquerda. Funciona para entradas arbitrariamente grandes.

Experimente online!

Considerando que este é o Funciton, é até razoavelmente rápido (n = 20 leva cerca de 14 segundos no TIO). A principal desaceleração vem da dupla recursão, pois não acho que o interpretador do Funciton memorize automaticamente as funções.

Infelizmente, algumas fontes monoespaçadas não monoespaçam corretamente as e / ou inserem pequenas lacunas entre as linhas. Aqui está uma captura de tela do código do TIO em toda a sua beleza:

insira a descrição da imagem aqui

Eu acho que pode ser possível jogar isso um pouco mais, por exemplo, alterando a condição de >0para <1e trocando os ramos do condicional, para que eu possa reutilizar o número literal, ou talvez usando uma fórmula completamente diferente, mas estou bem feliz com o quão compacto já é.

Explicação

Isso basicamente implementa a recursão dada no desafio, embora use o caso base ! (- 1) =! 0 = 1 . n-1 e n-2 são calculados com a função predecessora e o resultado intermediário n-1 é reutilizado em três locais. Não há muito mais, então vou passar rapidamente pelo fluxo de controle:

               ─┐
               ╓┴╖
               ║f║
               ╙─╜

Este é o cabeçalho da função que emite da função de entrada n longo da linha ligada. Isso atinge imediatamente a junção em T, que simplesmente duplica o valor.

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

A 0caixa é apenas um literal numérico. Uma junção de quatro direções calcula duas funções: o caminho que sai da base calcula 0 <n , que usaremos para determinar o caso base. O caminho que sai à esquerda calcula 0 << n (um deslocamento para a esquerda), mas descartamos esse valor com a construção Starkov .

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

Conduzimos isso para a condicional de três vias ?. Se o valor for falso, retornamos o resultado constante 1. A extremidade solta à direita do ?é a saída da função. Estou girando em torno de 180 graus aqui, para que a orientação relativa de entrada e saída de fseja mais conveniente no restante do programa.

Se a condição for verdadeira, o outro valor será usado. Vejamos o caminho que leva a esse ramo. (Observe que a avaliação do Funciton é realmente preguiçosa, de modo que esse ramo nunca será avaliado se não for necessário, o que possibilita a recursão em primeiro lugar.)

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

No outro ramo, primeiro calculamos n-1 e depois dividimos o caminho duas vezes para obter três cópias do valor (uma para o coeficiente de recorrência, uma para o primeiro subfatorial e a última para n-2 ).

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

Como eu disse, decrementamos uma cópia novamente com outra , depois alimentamos n-1 e n-2 recursivamente fe finalmente adicionamos os dois resultados no +.

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

Tudo o que resta é multiplicar n-1 por ! (N-1) +! (N-2) .

Martin Ender
fonte
13

Oásis , 5 bytes

Usa a fórmula dada por Martin. Código:

+n<*X

Versão dissecada:

+n<*

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

Explicação a(n) =:

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

Experimente online!

Adnan
fonte
Bom truque usando X:-) BTW, você já implementou isso ? Um destes dias, não será capaz de fugir com apenas mudando os valores iniciais
Luis Mendo
@LuisMendo Sim, eu fiz! É usado como um sinalizador de comando ( aqui está um link para a página de informações). Obrigado pela sugestão :).
Adnan
7

Geléia , 7 bytes

R=Œ!Ḅċ0

Essa abordagem constrói os desarranjos, por isso é bastante lenta.

Experimente online!

Como funciona

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.
Dennis
fonte
7

Braquilog (2), 11 bytes

⟦₁{p:?\≠ᵐ}ᶜ

Experimente online!

Explicação

Isso é basicamente apenas uma tradução direta da especificação do inglês para o Brachylog (e, portanto, tem a vantagem de poder ser facilmente modificada para lidar com pequenas alterações na especificação, como encontrar o número de perturbações de uma lista específica).

⟦₁{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

fonte
5

Idiomas com soluções integradas

Seguindo a sugestão do xnor, esta é uma resposta CW na qual soluções triviais baseadas em um único built-in para calcular o subfatorial ou gerar todos os desarranjos devem ser editadas.

Mathematica, 12 bytes

Subfactorial
Martin Ender
fonte
suspiro Mathematica ...
epicbob57
5

Python 3 , 35 32 bytes

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

Isso usa a relação de recorrência ! N = n! (N-1) + (-1) n da resposta Haskell de @ Laikoni , com caso base ! 0 = 1 .

Experimente online!

Dennis
fonte
Eu acho que você também pode usar a outra equação dada aqui , o que permitiria poupar dois bytes: f=lambda n:n<1or n*f(n-1)+(-1)**n.
Adnan
11
Três bytes com um pouco de reordenação. ;)
Dennis
11
A parte divertida dessa recorrência é que, se você voltar ao caso base n=-1, não importa em qual valor você usar. Isso pode ser útil para alguns idiomas (por exemplo, no Mathematica, você pode deixá-lo indefinido se salvar algum bytes).
Martin Ender
5

M , 9 bytes

o2!÷Øe+.Ḟ

Como você pode ver removendo o , M usa matemática simbólica, portanto não haverá problemas de precisão.

Experimente online! Não é a solução mais curta que foi publicada, mas é rápida .

Como funciona

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.
Dennis
fonte
5

MATL , 9 8 bytes

:tY@-!As

Da mesma forma que a resposta do @Dennis 'Jelly , isso realmente constrói as permutações e conta quantas delas são perturbações; então é lento.

Experimente 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
Luis Mendo
fonte
3

Matemática , 21 bytes

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

Sou muito novo nisso e não tenho ideia do que estou fazendo ...

Experimente online!

Dennis
fonte
11
Duas alternativas na mesma contagem de bytes: Round[(#/. 0->2)!/E]&e ±0=1;±n_:=Round[n!/E](embora eu não saiba se o Mathics suporta codificações de byte único para arquivos de origem, como o Mathematica).
Martin Ender
O primeiro funciona bem ( acho que sei o que ele faz), mas a matemática não parece apoiar ±no segundo. Funcionaria com f, mas ao custo de dois bytes.
217 Dennis
Outro, ao mesmo byte count: Round[#!/E]+1-Sign@#&. Valores iniciais irritantes ...!
Greg Martin
3

Ruby, 27 bytes

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

~0**né mais curto que (-1)**n!

m-chrzan
fonte
3

CJam (10 bytes)

1qi{~*)}/z

Demonstração online .

Isso usa a recorrência !n = n !(n-1) + (-1)^n, da qual derivamos n! / ee descobri que já estava no OEIS.

Dissecação

O loop calcula (-1)^n !n, então precisamos pegar o valor absoluto no final:

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
Peter Taylor
fonte
2

05AB1E , 8 bytes

΃N*®Nm+

Experimente online!

Explicação

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

MATLAB, 33 bytes

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

Função Anonympus que usa a fórmula na Seção 3 de Desarranjos e aplicações de Mehdi Hassani.

Exemplo de uso:

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

JavaScript (ES6), 26 bytes

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

Usa a relação de recorrência da resposta de @ Laikoni. No ES7, você pode salvar um byte usando em +(-1)**nvez de -(~n%2|1).

Neil
fonte
2

PostScript, 81 76 69 bytes

Aqui estão implementações de ambas as fórmulas.

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

Essa versão gera um float. Se for necessário gerar um número inteiro:

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

que pesa 73 bytes.

A outra fórmula é um pouco mais longa: 81 bytes.

(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

Essas funções obtêm seus argumentos da pilha e deixam o resultado na pilha.

Você pode testar as funções, em um arquivo ou em um prompt PostScript interativo (por exemplo, GhostScript) com

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

saída

[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]

Aqui está um arquivo PostScript completo que renderiza a saída na tela ou na página da impressora. (Os comentários no PostScript começam com %).

%!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
PM 2Ring
fonte
11
-1 3 2 roll expé um pouco mais curto que exch 2 mod 2 mul 1 sub.
Peter Taylor
@PeterTaylor Assim é! :) Eu esqueci exp: oops: No entanto, ele retorna um float e acho que preciso gerar um número inteiro para estar em conformidade com a pergunta.
PM 2Ring
11
Eu acho que você ainda pode jogar uma cvie economizar. (Nota: não testado, mas ao ler o documento, acho que deve funcionar).
Peter Taylor
@ PeterTaylor Sim, cvifunciona, e ainda é mais curto que a minha versão anterior.
PM 2Ring
1

PHP, 69 bytes

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

use desta maneira a(n) = n*a(n-1) + (-1)^n

Jörg Hülsermann
fonte
11
Você só precisa fornecer a função, não o programa completo, para poder descartar os últimos 17 caracteres. Há uma economia adicional por não entrada de caixa especial 1. Acho que as duas economias diminuem para 47 bytes.
Peter Taylor
1

PHP, 50 44

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

Correr com echo <n> | php -nR '<code>

A beleza de a(n) = n*a(n-1) + (-1)^né que depende apenas do valor anterior. Isso permite que ele seja implementado iterativamente em vez de recursivamente. Isso salva uma declaração de função longa.

-6 bytes por @Titus. Obrigado !

Christoph
fonte
-1 byte: $i++<$argv[1]. -2 bytes: for(;$i++<$argv[1];)$n=++$n*$i-$i%2*2;echo$n+1;. (-3 bytes com -Re $argn.)
Titus
@ Titus alguém ficou entediado? : D você se importaria de me dar um exemplo para -Re $argn?
Christoph
11
Não entediado, mas ansioso para jogar golfe. Veja php.net/manual/de/features.commandline.options.php: echo <input> | php -nR '<code>'. exemplo: codegolf.stackexchange.com/a/113046
Titus
11
@Titus eu acertei? ;-)
Christoph
0

Mathematica, 40 bytes

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

Que vem em 31 bytes sob a codificação ISO 8859-1 padrão.

A Simmons
fonte
0

C, 34 bytes

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

Explicação:

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
Bijan
fonte
0

R, 47 bytes

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

Usa a mesma fórmula da resposta do Mego .

Método alternativo, 52 bytes usando a PerMallowsbiblioteca

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

Na verdade , 18 bytes

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

Experimente online!

Explicação:

;!@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

Uma versão de 12 bytes que funcionaria se realmente tivesse mais precisão:

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

Experimente online!

Ao contrário de todas as outras respostas (no momento da postagem), esta solução não usa a fórmula recursiva nem a fórmula de soma. Em vez disso, ele usa a seguinte fórmula:

fórmula de perturbação

Essa fórmula é relativamente fácil de implementar em Na verdade:

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

Agora, o único problema é que a fórmula vale apenas para o positivo n. Se você tentar usar n = 0, a fórmula gera incorretamente 0. Isso é facilmente corrigido, no entanto: aplicando negação booleana à entrada e adicionando isso à saída da fórmula, a saída correta é fornecida para todos os não negativos n. Assim, o programa com essa correção é:

;!╠@/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
Mego
fonte
Continua dando respostas negativas para mim ...
Leaky Nun
@LeakyNun Isso é por causa dos limites de precisão. Para entradas grandes (em volta n = 100), (-1)**n/n!não pode ser representado com flutuadores IEEE 754 de precisão dupla. Isso é aceitável de acordo com o desafio.
Mego
Mesmo para n=4...
Leaky Nun
@LeakyNun Oh. Não sei por que estava usando a divisão do piso. Corrigindo agora.
Mego 24/04
16 bytes
Freira vazada
0

Empilhados , 30 bytes

[:[#-::#-f\f+*]$not,\2<# !]@:f

Experimente online!

Função recursiva simples. Usa o fato de que !n = not npara n < 2. Ligar como n f.

Conor O'Brien
fonte
0

Alice , 20 18 bytes

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

Experimente online!

Explicação

Isto usa a recursividade de Laikoni de resposta , ! N = n! (N-1) + (-1) n . Semelhante à resposta do Funciton, estou usando o caso base ! (- 1) = 1 . Colocamos 1 na pilha com 1.. Então isso...

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

... é apenas a estrutura de E / S decimal usual. O código principal é realmente

&wq*eqE+]k

Quebrado:

&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.
Martin Ender
fonte