Hàm giai thừa của Ruby


88

Tôi đang phát điên: Hàm Ruby cho giai thừa ở đâu? Không, tôi không cần triển khai hướng dẫn, tôi chỉ muốn hàm từ thư viện. Nó không phải trong Toán học!

Tôi bắt đầu nghi ngờ, nó có phải là một chức năng thư viện chuẩn không?


63
Tôi làm điều đó như thế6.downto(1).inject(:*)
mckeed

43
@mckeed: Hay (1..6).inject(:*)ngắn gọn hơn một chút.
sepp2k

8
tại sao bạn lại mong đợi có một cái?
Tổng thống James K. Polk

4
Tôi tự hỏi tình trạng của các thư viện toán học và khoa học đối với Ruby là gì.
Andrew Grimm

5
Chỉ cần lưu ý về các ví dụ được cung cấp bằng cách sử dụng tiêm. (1..num).inject(:*)không thành công cho trường hợp nơi num == 0. (1..(num.zero? ? 1 : num)).inject(:*)đưa ra câu trả lời đúng cho trường hợp 0 ​​và trả về nilcho các tham số âm.
Yogh

Câu trả lời:




77

Nó không có trong thư viện chuẩn nhưng bạn có thể mở rộng lớp 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 Giai thừa lặp lại là một lựa chọn tốt hơn vì lý do hiệu suất rõ ràng.


8
Anh ấy nói rõ ràng rằng, anh ấy không muốn triển khai.
sepp2k

117
Anh ta có thể không; nhưng những người tìm kiếm SO cho "giai thừa Ruby" có thể.
Pierre-Antoine LaFayette

1
rosettacode.org/wiki/Factorial#Ruby đã sai. Không có một trường hợp số 0
Douglas G. Allen

Phiên bản đệ quy có thực sự chậm hơn không? Nó có thể phụ thuộc vào việc Ruby có thực hiện tối ưu hóa đệ quy đuôi hay không.
Lex Lindsey

24

Một cách đáng xấu hổ được đưa nôi từ http://rosettacode.org/wiki/Factorial#Ruby , sở thích cá nhân của tôi là

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Việc triển khai này cũng diễn ra nhanh nhất trong số các biến thể được liệt kê trong Mã Rosetta.

cập nhật # 1

Đã thêm || 1để xử lý trường hợp số không.

cập nhật # 2

Để cảm ơn và đánh giá cao Mark Thomas , đây là một phiên bản hiệu quả hơn, trang nhã và ít người biết đến hơn một chút:

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

1
điều đó là thế quái nào?! vâng, nó nhanh nhưng nó rất không thân thiện
niccolo m.

3
nó cũng không chính xác cho 0! - nên là một cái gì đó như: if self <= 1; 1; khác; (1..chính mình) .reduce (: *); kết thúc
Tarmo

8
@allen - Đừng đổ lỗi cho ngôn ngữ nếu bạn không thể hiểu nó. Nó chỉ đơn giản có nghĩa là, lấy phạm vi 1 là chính nó, sau đó loại bỏ phần tử đầu tiên (1) khỏi nó (tức là đó là ý nghĩa của giảm trong lập trình hàm). Sau đó, loại bỏ phần tử đầu tiên của phần còn lại (2) và nhân (: *) chúng với nhau. Bây giờ loại bỏ phần tử đầu tiên khỏi phần còn lại (3) và nhân phần tử đó với tổng số đang chạy. Tiếp tục cho đến khi không còn gì (tức là bạn đã xử lý toàn bộ phạm vi). Nếu giảm không thành công (vì mảng trống trong trường hợp 0!) Thì vẫn trả về 1.
SDJMcHattie 17/12/13

Bạn cũng có thể xử lý các trường hợp không bằng cách xác định giá trị ban đầu trong reduce: (1..self).reduce(1,:*).
Mark Thomas

3
Trên thực tế bạn có thể sử dụng (2..self).reduce(1,:*), nếu vi-hiệu quả là điều bạn :)
Mark Thomas


14

Bạn cũng có thể sử dụng Math.gammahàm tổng hợp thành giai thừa cho các tham số số nguyên.


3
Từ tài liệu: "Lưu ý rằng gamma (n) giống như fact (n-1) cho số nguyên n> 0. Tuy nhiên gamma (n) trả về float và có thể là một số gần đúng". Nếu người ta tính đến điều đó, nó hoạt động, nhưng giải pháp giảm thiểu có vẻ dễ dàng hơn nhiều.
Michael Kohl

Cám ơn vì cái này! Ruột của tôi nói rằng hãy sử dụng thư viện tiêu chuẩn trên một bản giảm thiểu được viết tùy chỉnh bất cứ khi nào có thể. Hồ sơ có thể gợi ý khác.
David J.

2
Lưu ý: Nó là O (1) và chính xác cho 0..22: MRI Ruby thực sự thực hiện tra cứu các giá trị đó (xem static const double fact_table[]trong nguồn ). Ngoài ra, nó là một con số gần đúng. 23 !, chẳng hạn, yêu cầu phần định trị 56-bit không thể biểu diễn chính xác bằng cách sử dụng đôi IEEE 754 có phần định trị 53-bit.
fny

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

ví dụ

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

Có gì sai với class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Aleksei Matiushkin

@mudasobwa Tôi thích nó, tôi đã cấu trúc lại cho đơn giản.
jasonleonhard

4
Tôi trân trọng đề nghị rằng việc ghi đè một phương thức thể hiện được kết hợp vào tất cả các đối tượng Ruby để trả về một giá trị trung thực, thay vì một giá trị giả có thể không khiến bạn có nhiều bạn.
MatzFan

Có thể thực sự nguy hiểm khi làm cho toán tử phủ định trở thành một thứ gì đó khác khi axảy ra Integertrong trường hợp !a... làm như vậy có thể gây ra một lỗi tồn tại rất khó nhận biết. Nếu axảy ra là một con số lớn như 357264543sau đó bộ vi xử lý là đi sâu vào một vòng lặp lớn và mọi người có thể tự hỏi tại sao chương trình tất cả của một đột ngột trở nên chậm chạp
nonopolarity

Câu trả lời này chỉ là một điều thú vị để chia sẻ hơn là một ví dụ thực tế để sử dụng.
jasonleonhard


6

Tôi chỉ viết của riêng tôi:

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

Ngoài ra, bạn có thể xác định một giai thừa giảm:

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

4

Chỉ cần gọi hàm này

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

ví dụ

factorial(3)
factorial(11)

3

Sử dụng Math.gamma.floorlà một cách dễ dàng để tạo ra một giá trị gần đúng và sau đó làm tròn nó trở lại kết quả số nguyên chính xác. Sẽ hoạt động với tất cả các Số nguyên, hãy bao gồm kiểm tra đầu vào nếu cần.


6
Chỉnh sửa: Sau khi n = 22nó ngừng đưa ra câu trả lời chính xác và tạo ra giá trị gần đúng.
Ayarch

2

Với sự tôn trọng cao đối với tất cả những người đã tham gia và dành thời gian của họ để giúp đỡ chúng tôi, tôi muốn chia sẻ điểm chuẩn của tôi về các giải pháp được liệt kê ở đây. Tham số:

số lần lặp = 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)

Đối với 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
Cần lưu ý rằng tốc độ nhanh nhất Math.gamma(n+1)cũng chỉ gần đúng với n> 22, vì vậy có thể không phù hợp với mọi trường hợp sử dụng.
Neil Slater

1

Chỉ là một cách khác để làm điều đó, mặc dù nó thực sự không cần thiết.

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

Bạn có thể sẽ thấy một yêu cầu tính năng Ruby hữu ích. Nó chứa một bản vá quan trọng bao gồm một tập lệnh Bash demo . Sự khác biệt về tốc độ giữa một vòng lặp ngây thơ và giải pháp được trình bày trong lô có thể là 100x (hàng trăm lần) theo đúng nghĩa đen. Được viết bằng Ruby nguyên chất.


1

Đây là phiên bản của tôi dường như rõ ràng với tôi mặc dù nó không được sạch sẽ.

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

Đây là dòng thử nghiệm irb của tôi hiển thị từng bước.

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

Và một cách khác (=

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

Chỉ cần một cách nữa để làm điều đó:

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

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

fact(6) => 720

0

Tại sao thư viện chuẩn lại yêu cầu phương pháp giai thừa, khi có một trình lặp tích hợp cho mục đích chính xác này? Nó được gọi là upto.

Không, bạn không cần phải sử dụng đệ quy, giống như tất cả các câu trả lời khác hiển thị.

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

Thay vào đó, trình vòng lặp tích hợp tối đa có thể được sử dụng để tính các giai thừa:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.