Função fatorial Ruby


87

Estou ficando louco: Onde está a função Ruby para fatorial? Não, não preciso de implementações de tutorial, só quero a função da biblioteca. Não está na matemática!

Estou começando a duvidar, é uma função de biblioteca padrão?


63
Eu faço como6.downto(1).inject(:*)
— mckeed

42
@mckeed: Ou (1..6).inject(:*)que é um pouco mais sucinto.
— sepp2k

8
por que você esperaria que houvesse um?
— Presidente James K. Polk,

4
Eu me pergunto qual é o status das bibliotecas de matemática e ciências para Ruby.
— Andrew Grimm

5
Apenas uma nota sobre os exemplos fornecidos usando injetar. (1..num).inject(:*)falha para o caso em que num == 0. (1..(num.zero? ? 1 : num)).inject(:*)fornece a resposta correta para o caso 0 e retorna nilpara os parâmetros negativos.
— Yogh

Respostas:




77

Não está na biblioteca padrão, mas você pode estender a classe Integer.

class Integer
  def factorial_recursive
    self <= 1 ? 1 : self * (self - 1).factorial
  end
  def factorial_iterative
    f = 1; for i in 1..self; f *= i; end; f
  end
  alias :factorial :factorial_iterative
end

NB Fatorial iterativo é a melhor escolha por razões óbvias de desempenho.


8
Ele disse explicitamente que não quer uma implementação.
— sepp2k

115
Ele não pode; mas as pessoas que procuram por "fatorial Ruby" podem.
— Pierre-Antoine LaFayette

1
rosettacode.org/wiki/Factorial#Ruby está simplesmente errado. Não há caso para 0
— Douglas G. Allen

A versão recursiva é realmente mais lenta? Pode depender se Ruby faz otimização recursiva de cauda.
— Lex Lindsey

23

Descaradamente copiado de http://rosettacode.org/wiki/Factorial#Ruby , meu favorito pessoal é

class Integer
  def fact
    (1..self).reduce(:*) || 1
  end
end

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Essa implementação também é a mais rápida entre as variantes listadas no Rosetta Code.

atualização # 1

Adicionado || 1para lidar com o caso zero.

atualização # 2

Com agradecimento e apreço a Mark Thomas , aqui está uma versão um pouco mais eficiente, elegante e obscura:

class Integer
  def fact
    (2..self).reduce(1,:*)
  end
end

1
Que diabos isso significa?! sim, é rápido, mas é muito incomum
— niccolo m.

3
também é incorreto para 0! - deve ser algo como: if self <= 1; 1; outro; (1..se) .reduzir (: *); fim
— Tarmo

8
@allen - Não culpe a linguagem se você não consegue entendê-la. Significa simplesmente pegar o intervalo 1 para self e, em seguida, remover o primeiro elemento (1) dele (ou seja, isso é o que significa reduzir na programação funcional). Em seguida, remova o primeiro elemento do que resta (2) e multiplique (: *) todos juntos. Agora remova o primeiro elemento do que resta (3) e multiplique-o pelo total corrente. Continue até que não haja mais nada (ou seja, você manipulou toda a gama). Se a redução falhar (porque o array está vazio no caso de 0!), Então apenas retorne 1 de qualquer maneira.
— SDJMcHattie

Você também pode lidar com o caso de zero especificando o valor inicial em reduce: (1..self).reduce(1,:*).
— Mark Thomas

3
Na verdade, você pode usar (2..self).reduce(1,:*), se micro-eficiência for o seu lugar :)
— Mark Thomas

14

Você também pode usar a Math.gammafunção que se resume a fatorial para parâmetros inteiros.


3
Dos documentos: "Observe que gamma (n) é igual a fact (n-1) para o inteiro n> 0. No entanto, gamma (n) retorna float e pode ser uma aproximação". Se levarmos isso em consideração, funciona, mas a solução de redução parece muito mais direta.
— Michael Kohl

Obrigado por isso! Meu instinto diz para usar a biblioteca padrão em vez de um redução personalizada sempre que possível. A criação de perfil pode sugerir o contrário.
— David J.

2
Nota: É O (1) e preciso para 0..22: MRI Ruby realmente executa uma pesquisa para esses valores (veja static const double fact_table[]na fonte ). Além disso, é uma aproximação. 23 !, por exemplo, requer uma mantissa de 56 bits que é impossível representar precisamente usando o duplo IEEE 754 que tem mantissas de 53 bits.
— FNY


13
class Integer
  def !
    (1..self).inject(:*)
  end
end

exemplos

!3  # => 6
!4  # => 24

O que há de errado class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
— Aleksei Matiushkin

@mudasobwa Eu gosto, refatorei para simplificar.
— jasonleonhard

4
Eu respeitosamente sugeriria que substituir um método de instância incorporado em todos os objetos Ruby para retornar um valor verdadeiro, ao invés de um falso, pode não fazer você muitos amigos.
— MatzFan

Pode ser muito perigoso fazer com que o operador de negação se torne outra coisa quando aacontece de estar Integerno caso de !a... fazer isso pode causar a existência de um bug que é muito difícil de dizer. Se aacontecer de ser um grande número, como 357264543então o processador está entrando em um grande loop e as pessoas podem se perguntar por que o programa de repente fica lento
— polaridade

Essa resposta foi mais apenas uma coisa legal de compartilhar do que um exemplo prático de usar.
— jasonleonhard


6

Acabei de escrever o meu próprio:

def fact(n)
  if n<= 1
    1
  else
    n * fact( n - 1 )
  end
end

Além disso, você pode definir um fatorial de queda:

def fall_fact(n,k)
  if k <= 0
    1
  else
    n*fall_fact(n - 1, k - 1)
  end
end

4

Basta chamar esta função

def factorial(n=0)
  (1..n).inject(:*)
end

exemplos

factorial(3)
factorial(11)

3

Usar Math.gamma.flooré uma maneira fácil de produzir uma aproximação e arredondar de volta para o resultado inteiro correto. Deve funcionar para todos os inteiros, inclua uma verificação de entrada se necessário.


6
Correção: Depois n = 22que deixa de dar uma resposta exata e produz aproximações.
— Ayarch

2

Com muito respeito a todos que participaram e despenderam seu tempo nos ajudando, gostaria de compartilhar meus benchmarks das soluções listadas aqui. Params:

iterações = 1000

n = 6

                                     user     system      total        real
Math.gamma(n+1)                   0.000383   0.000106   0.000489 (  0.000487)
(1..n).inject(:*) || 1            0.003986   0.000000   0.003986 (  0.003987)
(1..n).reduce(1, :*)              0.003926   0.000000   0.003926 (  0.004023)
1.upto(n) {|x| factorial *= x }   0.003748   0.011734   0.015482 (  0.022795)

Para n = 10

  user     system      total        real
0.000378   0.000102   0.000480 (  0.000477)
0.004469   0.000007   0.004476 (  0.004491)
0.004532   0.000024   0.004556 (  0.005119)
0.027720   0.011211   0.038931 (  0.058309)

1
É importante notar que o mais rápido Math.gamma(n+1)também é apenas aproximado para n> 22, portanto, pode não ser adequado para todos os casos de uso.
— Neil Slater de

1

Apenas outra maneira de fazer isso, embora realmente não seja necessário.

class Factorial
   attr_reader :num
   def initialize(num)
      @num = num
   end

   def find_factorial
      (1..num).inject(:*) || 1
   end
end

number = Factorial.new(8).find_factorial
puts number


1

Aqui está minha versão parece ser clara para mim, embora não seja tão limpa.

def factorial(num)
    step = 0
    (num - 1).times do (step += 1 ;num *= step) end
    return num
end

Esta foi a minha linha de teste irb que mostrou cada etapa.

num = 8;step = 0;(num - 1).times do (step += 1 ;num *= step; puts num) end;num

0
class Integer
  def factorial
    return self < 0 ? false : self==0 ? 1 : self.downto(1).inject(:*)
    #Not sure what other libraries say, but my understanding is that factorial of 
    #anything less than 0 does not exist.
  end
end

0

E ainda outra forma (=

def factorial(number)
  number = number.to_i
  number_range = (number).downto(1).to_a
  factorial = number_range.inject(:*)
  puts "The factorial of #{number} is #{factorial}"
end
factorial(#number)

0

Só mais uma maneira de fazer isso:

# fact(n) => Computes the Factorial of "n" = n!

def fact(n) (1..n).inject(1) {|r,i| r*i }end

fact(6) => 720

0

Por que a biblioteca padrão exigiria um método fatorial, quando há um iterador embutido para esse propósito exato? É chamado upto.

Não, você não precisa usar recursão, como todas essas outras respostas mostram.

def fact(n)
  n == 0 ? 1 : n * fact(n - 1)
end  

Em vez disso, o iterador integrado upto pode ser usado para calcular fatoriais:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
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.