Функціональна функція рубіну


88

Я божеволію: де функція Ruby для факторіалу? Ні, мені не потрібні реалізації підручників, я просто хочу функцію з бібліотеки. Це не в математиці!

Я починаю сумніватися, чи є це стандартною функцією бібліотеки?


63
Я роблю це як6.downto(1).inject(:*)
mckeed

43
@mckeed: Або (1..6).inject(:*)що є трохи більш лаконічним.
sepp2k

8
чому ви очікуєте, що він буде такий?
Президент Джеймс К. Полк,

4
Цікаво, яким є статус математичних та природничих бібліотек для Рубі.
Ендрю Грімм,

5
Просто примітка щодо наведених прикладів за допомогою ін’єкції. (1..num).inject(:*)не вдається у випадку, коли num == 0. (1..(num.zero? ? 1 : num)).inject(:*)дає правильну відповідь для випадку 0 і повертається nilдля негативних параметрів.
Йог

Відповіді:




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 Ітеративний факторіал - кращий вибір із очевидних причин ефективності.


8
Він прямо сказав, що не хоче реалізації.
sepp2k

117
Він може цього не робити; але люди, які шукають ТО "Ruby factorial", можуть.
Pierre-Antoine LaFayette

1
rosettacode.org/wiki/Factorial#Ruby просто помиляється. Немає справи для 0
Дуглас Г. Аллен

Рекурсивна версія насправді повільніша? Це може залежати від того, чи Ruby робить рекурсивну оптимізацію.
Лекс Ліндсі,

24

Безсоромно перекладено з http://rosettacode.org/wiki/Factorial#Ruby , мій особистий фаворит -

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

>> 400.fact
=> 64034522846623895262347970319503005850702583026002959458684445942802397169186831436278478647463264676294350575035856810848298162883517435228961988646802997937341654150838162426461942352307046244325015114448670890662773914918117331955996440709549671345290477020322434911210797593280795101545372667251627877890009349763765710326350331533965349868386831339352024373788157786791506311858702618270169819740062983025308591298346162272304558339520759611505302236086810433297255194852674432232438669948422404232599805551610635942376961399231917134063858996537970147827206606320217379472010321356624613809077942304597360699567595836096158715129913822286578579549361617654480453222007825818400848436415591229454275384803558374518022675900061399560145595206127211192918105032491008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Ця реалізація також виявляється найшвидшою серед варіантів, перелічених у Розетському кодексі.

оновлення №1

Додано || 1для обробки нульового регістру.

оновлення No2

З подякою та вдячністю Марку Томасу , ось версія, яка є трохи ефективнішою, елегантнішою та неяснішою:

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

1
що, біса, це означає ?! так, це швидко, але це дуже непривітно, хоча
niccolo m.

3
це також неправильно для 0! - має бути приблизно таким: if self <= 1; 1; ще; (1..себе) .зменшити (: *); кінець
Тармо

8
@allen - Не звинувачуй мову, якщо ти не можеш її зрозуміти. Це просто означає, прийміть діапазон 1 до себе, а потім видаліть з нього перший елемент (1) (тобто це те, що означає функціональне програмування). Потім видаліть перший елемент із того, що залишилося (2), і помножте (: *) їх разом. Тепер вийміть перший елемент із того, що залишилось (3), і помножте його на загальну суму. Продовжуйте рухатися, поки нічого не залишиться (тобто ви обробили весь асортимент). Якщо зменшення не вдається (оскільки масив порожній у випадку 0!), Тоді все одно просто поверніть 1.
SDJMcHattie

Ви також можете звертатися з нульовим випадком, вказавши початкове значення в reduce: (1..self).reduce(1,:*).
Mark Thomas

3
Насправді ви можете використовувати (2..self).reduce(1,:*), якщо ваша мікроефективність
Марк Томас


14

Ви також можете використовувати Math.gamma функцію, яка зводиться до факторіалу для цілочисельних параметрів.


3
З документації: "Зверніть увагу, що гама (n) така ж, як факт (n-1) для цілого числа n> 0. Однак гамма (n) повертає плаваюче значення і може бути наближенням". Якщо взяти це до уваги, це працює, але рішення щодо зменшення здається набагато простішим.
Michael Kohl

Дякую за це! У моїй кишці сказано використовувати, коли це можливо, у напрямку до стандартної бібліотеки, замість написаного на замовлення. Профілювання може свідчити про інше.
Девід Дж.,

2
Примітка: Це O (1) і точно для 0..22: МРТ Ruby насправді виконує пошук цих значень (див. static const double fact_table[]У джерелі ). Крім цього, це наближення. 23 !, наприклад, потрібна 56-бітна мантиса, яку неможливо точно представити за допомогою подвійника IEEE 754, який має 53-бітові мантиси.
fny

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

приклади

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

В чому проблема class Integer ; def ! ; (1..self).inject(:*) ; end ; end?
Олексій Матюшкін

@mudasobwa Мені це подобається, я змінив його для простоти.
jasonleonhard

4
Я б з повагою припустив, що перевизначення методу екземпляра, включеного у всі об’єкти Ruby, щоб повернути неправдиве значення, а не хибне, може не зробити вас багато друзями.
MatzFan

Можливо, дійсно небезпечно змусити оператор заперечення перетворитися на щось інше, коли aце трапляється Integerу випадку !a... це може призвести до існування помилки, про яку дуже важко сказати. Якщо aтрапляється велике число, наприклад, 357264543тоді процесор переходить у великий цикл, і люди можуть дивуватися, чому програма раптом стає повільною
нонополярність

Ця відповідь була скоріше класною подією, а не практичним прикладом для використання.
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

З високою повагою до всіх, хто брав участь і витратив свій час, щоб допомогти нам, я хотів би поділитися своїми орієнтирами перелічених тут рішень. Параметри:

ітерацій = 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, тому може бути непридатним для всіх випадків використання.
Ніл Слейтер,

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 . Він містить нетривіальний патч, який включає демонстраційний скрипт Bash . Різниця швидкості між наївним циклом та рішенням, представленим у пакеті, може бути буквально 100-кратною (стократною). Написано все на чистому Ruby.


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  

Навпаки, вбудований ітератор upto може бути використаний для обчислення факторіалів:

factorial = 1
1.upto(10) {|x| factorial *= x }
factorial
 => 3628800
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.