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

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) .


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!


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.

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

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

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!


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.

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

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!


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!


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

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

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

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).


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

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


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 !


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


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

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'))

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

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



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.
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.