루비 계승 함수


88

미쳐 버릴거야 : 팩토리얼을위한 루비 함수는 어디에 있나요? 아니요, 튜토리얼 구현이 필요하지 않으며 라이브러리의 함수 만 원합니다. 수학이 아닙니다!

나는 의심하기 시작했습니다. 표준 라이브러리 기능입니까?


63
나는 그것을 좋아한다6.downto(1).inject(:*)
mckeed 2010 년

43
@mckeed : 아니면 (1..6).inject(:*)좀 더 간결합니다.
sepp2k

8
왜 하나가있을 거라고 기대하겠습니까?
James K. Polk 회장

4
Ruby의 수학 및 과학 라이브러리의 상태가 궁금합니다.
Andrew Grimm

5
주입을 사용하여 제공된 예제에 대한 참고 사항입니다. (1..num).inject(:*)여기서 경우 실패 num == 0. (1..(num.zero? ? 1 : num)).inject(:*)0 케이스에 대한 정답을 제공하고 nil음수 매개 변수에 대해 리턴 합니다.
Yogh

답변:




77

표준 라이브러리에는 없지만 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 Iterative factorial은 명백한 성능상의 이유로 더 나은 선택입니다.


8
그는 구현을 원하지 않는다고 명시 적으로 말했습니다.
sepp2k

117
그는 그렇지 않을 수도 있습니다. 그러나 SO를 검색하는 사람들은 "Ruby factorial"을 찾을 수 있습니다.
Pierre-Antoine LaFayette

1
rosettacode.org/wiki/Factorial#Ruby 는 틀 렸습니다. 0에 대한 사례가 없습니다
Douglas G. Allen

재귀 버전이 실제로 더 느립니까? Ruby가 꼬리 재귀 최적화를 수행하는지 여부에 따라 달라질 수 있습니다.
Lex Lindsey

24

http://rosettacode.org/wiki/Factorial#Ruby 에서 뻔뻔스럽게 집어 넣은 제가 개인적으로 가장 좋아하는 것은

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

이 구현은 또한 Rosetta Code에 나열된 변형 중에서 가장 빠릅니다.

업데이트 # 1

|| 1제로 케이스를 처리하기 위해 추가되었습니다 .

업데이트 # 2

Mark Thomas 에게 감사와 감사를 표하며 , 좀 더 효율적이고 우아하며 모호한 버전이 있습니다.

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

1
도대체 그게 무슨 뜻이야?! 네, 빠르지 만 사용자 친화적이지 않습니다
niccolo m.

3
0에도 맞지 않습니다! -다음과 같아야합니다. if self <= 1; 1; 그밖에; (1..self) .reduce (: *); 종료
Tarmo

8
@allen-언어를 이해할 수 없다고 탓하지 마십시오. 이는 단순히 범위 1을 self로 취한 다음 첫 번째 요소 (1)를 제거하는 것입니다 (즉, 함수형 프로그래밍에서 의미를 줄이는 것입니다). 그런 다음 남은 것의 첫 번째 요소 (2)를 제거하고 함께 곱하십시오 (: *). 이제 남은 요소 (3)에서 첫 번째 요소를 제거하고 누계와 곱합니다. 아무것도 남지 않을 때까지 계속 진행하십시오 (즉, 전체 범위를 처리했습니다). 축소가 실패하면 (0의 경우 배열이 비어 있기 때문에) 어쨌든 1을 반환합니다.
SDJMcHattie

당신은 또한에 초기 값을 지정하여 제로 사건을 처리 할 수 있습니다 reduce: (1..self).reduce(1,:*).
Mark Thomas

3
(2..self).reduce(1,:*)마이크로 효율성이 당신의 일이라면 실제로 사용할 수 있습니다 :)
Mark Thomas


14

Math.gamma정수 매개 변수에 대해 계승으로 요약되는 함수를 사용할 수도 있습니다 .


3
문서에서 : "감마 (n)는 정수 n> 0에 대한 fact (n-1)과 동일합니다. 그러나 gamma (n)은 부동 소수점을 반환하고 근사치가 될 수 있습니다". 이를 고려하면 작동하지만 축소 솔루션은 훨씬 더 간단 해 보입니다.
Michael Kohl

감사합니다! 내 직감은 가능할 때마다 사용자 정의 작성 축소보다 표준 라이브러리로 사용하라고 말합니다. 프로파일 링은 달리 제안 할 수 있습니다.
David J.

2
주 : O의 대 (1) 및 정확한 0..22: MRI 루비 실제로 해당 값에 대한 조회 (참조 수행 static const double fact_table[]소스 ). 그 이상은 근사치입니다. 예를 들어, 23!은 53 비트 가수를 가진 IEEE 754 double을 사용하여 정확하게 표현할 수없는 56 비트 가수가 필요합니다.
fny

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

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

뭐가 잘못 class Integer ; def ! ; (1..self).inject(:*) ; end ; end됐나요?
Aleksei Matiushkin

@mudasobwa 나는 그것을 좋아한다, 나는 단순성을 위해 리팩토링했다.
jasonleonhard

4
모든 Ruby 객체에 통합 된 인스턴스 메서드 를 재정 의하여 거짓 값이 아닌 진실한 값을 반환하는 것은 많은 친구가되지 않을 수 있음을 정중하게 제안합니다 .
MatzFan

이 때 뭔가 다른되기 위해 부정 연산자를 만들기 위해 정말 위험 할 수있는 a것을 일어나는 Integer경우에 !a... 이야기하기가 매우 어렵다 존재하는 버그의 원인이 될 수 있습니다. 경우 a와 같은 큰 수를 될 일이 357264543다음 프로세서가 큰 루프로 진행되며, 프로그램 작성의 모든 갑자기 느려지 왜 사람들이 궁금해 할
nonopolarity가

이 답변은 실용적인 예가 아니라 공유하기에 더 멋진 것입니다.
jasonleonhard


6

나는 방금 내 자신을 썼다.

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

또한 하강 팩토리얼을 정의 할 수 있습니다.

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


3

사용 Math.gamma.floor은 근사치를 생성 한 다음 올바른 정수 결과로 다시 반올림하는 쉬운 방법입니다. 모든 정수에 대해 작동해야하며 필요한 경우 입력 확인을 포함합니다.


6
정정 : n = 22정확한 답을 제공하지 않고 근사치를 생성 한 후 .
Ayarch

2

우리를 돕기 위해 참여하고 시간을 보낸 모든 분들을 높이 평가하며 여기에 나열된 솔루션의 벤치 마크를 공유하고 싶습니다. 매개 변수 :

반복 = 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)

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
가장 빠른 Math.gamma(n+1)것은 n> 22에 대해서만 근사치이므로 모든 사용 사례에 적합하지 않을 수 있습니다.
Neil Slater

1

실제로 필요한 것은 아니지만이를 수행하는 또 다른 방법입니다.

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

여기에 내 버전이 깨끗하지는 않지만 분명한 것 같습니다.

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

이것은 각 단계를 보여주는 내 irb 테스트 라인이었습니다.

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

그리고 또 다른 방법 (=

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

한 가지 더 방법이 있습니다.

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

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

fact(6) => 720

0

이 정확한 목적을위한 내장 반복기가 있는데 표준 라이브러리에 팩토리얼 메서드가 필요한 이유는 무엇입니까? 그것은이라고upto 합니다.

이 다른 모든 답변이 보여주는 것처럼 재귀를 사용할 필요가 없습니다.

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

오히려 내장 반복기를 사용하여 계승을 계산할 수 있습니다.

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.