ฟังก์ชันแฟกทอเรียลของรูบี้


89

ฉันจะบ้า: ฟังก์ชัน Ruby สำหรับแฟกทอเรียลอยู่ที่ไหน? ไม่ฉันไม่ต้องการการใช้งานบทช่วยสอนฉันแค่ต้องการฟังก์ชันจากไลบรารี มันไม่ได้อยู่ในคณิตศาสตร์!

ฉันเริ่มสงสัยว่ามันเป็นฟังก์ชันไลบรารี่มาตรฐานหรือไม่?


63
ฉันชอบ6.downto(1).inject(:*)
mckeed

43
@mckeed: หรือ(1..6).inject(:*)ที่รวบรัดกว่านี้หน่อย
Sep2k

8
ทำไมคุณถึงคาดหวังว่าจะมีหนึ่ง?
ประธาน James K. Polk

4
ฉันสงสัยว่าห้องสมุดคณิตศาสตร์และวิทยาศาสตร์ของ Ruby มีสถานะเป็นอย่างไร
Andrew Grimm

5
เพียงหมายเหตุเกี่ยวกับตัวอย่างที่ให้มาโดยใช้การฉีด ล้มเหลวสำหรับกรณีที่(1..num).inject(:*) ให้คำตอบที่ถูกต้องสำหรับกรณี 0 และส่งกลับสำหรับพารามิเตอร์เชิงลบ num == 0(1..(num.zero? ? 1 : num)).inject(:*)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 .. เอง). ลด (: *); สิ้นสุด
Tarmo

9
@allen - อย่าโทษภาษาถ้าคุณไม่เข้าใจ หมายความว่าให้นำช่วง 1 มาเป็นตัวเองจากนั้นลบองค์ประกอบแรก (1) ออกจากมัน (นั่นคือสิ่งที่ลดความหมายในการเขียนโปรแกรมเชิงฟังก์ชัน) จากนั้นลบองค์ประกอบแรกของสิ่งที่เหลือ (2) แล้วคูณ (: *) เข้าด้วยกัน ตอนนี้ลบองค์ประกอบแรกออกจากสิ่งที่เหลือ (3) แล้วคูณด้วยผลรวมที่ทำงาน ทำต่อไปจนกว่าจะไม่มีอะไรเหลือ (เช่นคุณได้จัดการช่วงทั้งหมดแล้ว) หากการลดล้มเหลว (เนื่องจากอาร์เรย์ว่างเปล่าในกรณีของ 0!) ให้ส่งคืน 1 ต่อไป
SDJMcHattie

นอกจากนี้คุณยังสามารถจัดการกับกรณีที่ศูนย์โดยการระบุค่าเริ่มต้นใน:reduce (1..self).reduce(1,:*)
Mark Thomas

3
จริงๆแล้วคุณสามารถใช้ได้(2..self).reduce(1,:*)ถ้าประสิทธิภาพระดับจุลภาคเป็นของคุณ :)
Mark Thomas


14

คุณยังสามารถใช้Math.gammaฟังก์ชันที่เดือดเป็นแฟกทอเรียลสำหรับพารามิเตอร์จำนวนเต็ม


3
จากเอกสาร: "โปรดทราบว่า gamma (n) เหมือนกับ fact (n-1) สำหรับจำนวนเต็ม n> 0 อย่างไรก็ตาม gamma (n) ส่งคืนค่า float และสามารถเป็นค่าประมาณได้" หากคำนึงถึงสิ่งนั้นก็ใช้ได้ แต่โซลูชันการลดลงดูเหมือนจะตรงไปตรงมามากขึ้น
Michael Kohl

ขอบคุณสำหรับสิ่งนี้! ลำไส้ของฉันบอกว่าจะใช้กับไลบรารีมาตรฐานโดยใช้การลดขนาดที่เขียนเองทุกครั้งที่ทำได้ การทำโปรไฟล์อาจแนะนำเป็นอย่างอื่น
David J.

2
หมายเหตุ: เป็น O (1) และแม่นยำสำหรับ0..22: MRI Ruby จะทำการค้นหาค่าเหล่านั้นจริง ๆ (ดูstatic const double fact_table[]ในแหล่งที่มา ) นอกเหนือจากนั้นเป็นค่าประมาณ ตัวอย่างเช่น 23! ต้องการแมนทิสซา 56 บิตซึ่งเป็นไปไม่ได้ที่จะแสดงอย่างแม่นยำโดยใช้ IEEE 754 double ซึ่งมีตั๊กแตนตำข้าว 53 บิต
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

4

เพียงแค่เรียกใช้ฟังก์ชันนี้

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

ตัวอย่าง

factorial(3)
factorial(11)

3

การใช้Math.gamma.floorเป็นวิธีง่ายๆในการสร้างการประมาณจากนั้นปัดเศษกลับลงมาเป็นผลลัพธ์จำนวนเต็มที่ถูกต้อง ควรใช้กับจำนวนเต็มทั้งหมดรวมการตรวจสอบอินพุตหากจำเป็น


6
การแก้ไข: หลังจากn = 22สิ้นสุดการให้คำตอบที่แน่นอนและสร้างการประมาณ
Ayarch

2

ด้วยความเคารพอย่างสูงต่อทุกคนที่เข้าร่วมและสละเวลาเพื่อช่วยเหลือเราฉันต้องการแบ่งปันผลการเปรียบเทียบของโซลูชันที่ระบุไว้ที่นี่ พารามส์:

การทำซ้ำ = 1,000

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

คุณอาจพบว่าคำขอคุณลักษณะ Ruby มีประโยชน์ มันมีขี้ปะติ๋วแพทช์ที่มีการสาธิตสคริปต์ทุบตี ความแตกต่างของความเร็วระหว่างวงรอบไร้เดียงสาและวิธีแก้ปัญหาที่นำเสนอในชุดงานสามารถเท่ากับ 100x (ร้อยเท่า) เขียนด้วยทับทิมแท้ทั้งหมด


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

และอีกวิธีหนึ่ง (=

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

เหตุใดไลบรารีมาตรฐานจึงต้องใช้วิธีแฟกทอเรียลในเมื่อมีตัววนซ้ำในตัวเพื่อจุดประสงค์ที่แน่นอนนี้ เรียกว่า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.