루비 계승 함수


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.