JavaScript'teki en hızlı faktöriyel işlev nedir? [kapalı]


98

JavaScript'te faktöriyel işlevin gerçekten hızlı bir uygulamasını arıyorsunuz . Herhangi bir önerin var mı?


8
Olası argüman aralığı nedir?
Nikita Rybak

5
Faktörleri önceden hesaplamayı ve değerleri bir arama tablosunda saklamayı düşündünüz mü?
Waleed Amjad

2
Böyle bir işlevin uygulaması nedir? Başka bir deyişle, onu ne için kullanacaksınız?
Pointy

@Nikita Rybak, yalnızca 1 belge (n). Eğer (n> 170) e = Infinity
Ken

@ Sivri, yine başka bir matematik hesap makinesi hizmeti.
Ken

Yanıtlar:


114

(1 ... 100) için arama yapabilirsiniz ! Faktöriyel sırayı önceden hesaplamak için Wolfram | Alpha üzerine tıklayın .

İlk 100 numara:

1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000, 6402373705728000, 121645100408832000, 2432902008176640000, 51090942171709440000, 1124000727777607680000, 25852016738884976640000, 620448401733239439360000, 15511210043330985984000000, 403291461126605635584000000, 10888869450418352160768000000, 304888344611713860501504000000, 8841761993739701954543616000000, 265252859812191058636308480000000, 8222838654177922817725562880000000, 263130836933693530167218012160000000, 8683317618811886495518194401280000000, 295232799039604140847618609643520000000, 10333147966386144929666651337523200000000, 371993326789901217467999448150835200000000, 13763753091226345046315979581580902400000000, 523022617466601111760007224100074291200000000, 20397882081197443358640281739902897356800000000, 815915283247897734345611269596115894272000000000, 33452526613163807108170062053440751665152000000000, 1405006117752879898543142606244511569936384000000000, 60415263063373835637355132068513997507264512000000000, 2658271574788448768043625811014615890319638528000000000, 119622220865480194561963161495657715064383733760000000000, 5502622159812088949850305428800254892961651752960000000000, 258623241511168180642964355153611979969197632389120000000000, 12413915592536072670862289047373375038521486354677760000000000, 608281864034267560872252163321295376887552831379210240000000000, 30414093201713378043612608166064768844377641568960512000000000000, 1551118753287382280224243016469303211063259720016986112000000000000, 80658175170943878571660636856403766975289505440883277824000000000000, 4274883284060025564298013753389399649690343788366813724672000000000000, 230843697339241380472092742683027581083278564571807941132288000000000000, 12696403353658275925965100847566516959580321051449436762275840000000000000, 710998587804863451854045647463724949736497978881168458687447040000000000000, 40526919504877216755680601905432322134980384796226602145184481280000000000000, 2350561331282878571829474910515074683828862318181142924420699914240000000000000, 138683118545689835737939019720389406345902876772687432540821294940160000000000000, 8320987112741390144276341183223364380754172606361245952449277696409600000000000000, 507580213877224798800856812176625227226004528988036003099405939480985600000000000000, 31469973260387937525653122354950764088012280797258232192163168247821107200000000000000, 1982608315404440064116146708361898137544773690227268628106279599612729753600000000000000, 126886932185884164103433389335161480802865516174545192198801894375214704230400000000000000, 8247650592082470666723170306785496252186258551345437492922123134388955774976000000000000000, 544344939077443064003729240247842752644293064388798874532860126869671081148416000000000000000, 36471110918188685288249859096605464427167635314049524593701628500267962436943872000000000000000, 2480035542436830599600990418569171581047399201355367672371710738018221445712183296000000000000000, 171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000, 11978571669969891796072783721689098736458938142546425857555362864628009582789845319680000000000000000, 850478588567862317521167644239926010288584608120796235886430763388588680378079017697280000000000000000, 61234458376886086861524070385274672740778091784697328983823014963978384987221689274204160000000000000000, 4470115461512684340891257138125051110076800700282905015819080092370422104067183317016903680000000000000000, 330788544151938641225953028221253782145683251820934971170611926835411235700971565459250872320000000000000000, 24809140811395398091946477116594033660926243886570122837795894512655842677572867409443815424000000000000000000, 1885494701666050254987932260861146558230394535379329335672487982961844043495537923117729972224000000000000000000, 145183092028285869634070784086308284983740379224208358846781574688061991349156420080065207861248000000000000000000, 11324281178206297831457521158732046228731749579488251990048962825668835325234200766245086213177344000000000000000000, 894618213078297528685144171539831652069808216779571907213868063227837990693501860533361810841010176000000000000000000, 71569457046263802294811533723186532165584657342365752577109445058227039255480148842668944867280814080000000000000000000, 5797126020747367985879734231578109105412357244731625958745865049716390179693892056256184534249745940480000000000000000000, 475364333701284174842138206989404946643813294067993328617160934076743994734899148613007131808479167119360000000000000000000, 39455239697206586511897471180120610571436503407643446275224357528369751562996629334879591940103770870906880000000000000000000, 3314240134565353266999387579130131288000666286242049487118846032383059131291716864129885722968716753156177920000000000000000000, 281710411438055027694947944226061159480056634330574206405101912752560026159795933451040286452340924018275123200000000000000000000, 24227095383672732381765523203441259715284870552429381750838764496720162249742450276789464634901319465571660595200000000000000000000, 2107757298379527717213600518699389595229783738061356212322972511214654115727593174080683423236414793504734471782400000000000000000000, 185482642257398439114796845645546284380220968949399346684421580986889562184028199319100141244804501828416633516851200000000000000000000, 16507955160908461081216919262453619309839666236496541854913520707833171034378509739399912570787600662729080382999756800000000000000000000, 1485715964481761497309522733620825737885569961284688766942216863704985393094065876545992131370884059645617234469978112000000000000000000000, 135200152767840296255166568759495142147586866476906677791741734597153670771559994765685283954750449427751168336768008192000000000000000000000, 12438414054641307255475324325873553077577991715875414356840239582938137710983519518443046123837041347353107486982656753664000000000000000000000, 1156772507081641574759205162306240436214753229576413535186142281213246807121467315215203289516844845303838996289387078090752000000000000000000000, 108736615665674308027365285256786601004186803580182872307497374434045199869417927630229109214583415458560865651202385340530688000000000000000000000, 10329978488239059262599702099394727095397746340117372869212250571234293987594703124871765375385424468563282236864226607350415360000000000000000000000, 991677934870949689209571401541893801158183648651267795444376054838492222809091499987689476037000748982075094738965754305639874560000000000000000000000, 96192759682482119853328425949563698712343813919172976158104477319333745612481875498805879175589072651261284189679678167647067832320000000000000000000000, 9426890448883247745626185743057242473809693764078951663494238777294707070023223798882976159207729119823605850588608460429412647567360000000000000000000000, 933262154439441526816992388562667004907159682643816214685929638952175999932299156089414639761565182862536979208272237582511852109168640000000000000000000000, 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Hala Degerler kendini hesaplamak istiyorsanız, kullanabileceğiniz Memoization :

var f = [];
function factorial (n) {
  if (n == 0 || n == 1)
    return 1;
  if (f[n] > 0)
    return f[n];
  return f[n] = factorial(n-1) * n;
}

Düzenleme: 21.08.2014

2.Çözüm

Bunun bir çalışma örnek eklemek kullanışlı olacağını düşündük tembel iteratif faktöriyel fonksiyonunun kullandığı bu büyük sayılar elde etmek tam ile sonuca memoization ve cache karşılaştırma

var f = [new BigNumber("1"), new BigNumber("1")];
var i = 2;
function factorial(n)
{
  if (typeof f[n] != 'undefined')
    return f[n];
  var result = f[i-1];
  for (; i <= n; i++)
      f[i] = result = result.multiply(i.toString());
  return result;
}
var cache = 100;
// Due to memoization, following line will cache first 100 elements.
factorial(cache);

Değişken isim görünürlüğünü sınırlamak için bir tür kapatma kullanacağınızı varsayıyorum .

Referans : BigNumber Sandbox : JsFiddle


6402373705728000'i geçen değerler kesilecektir, bu nedenle bu yaklaşımı kullanacaksanız, yukarıda belirtilen tabloyu kullanmadan önce üsse dönüştürdüğünüzden emin olun.
David Scott Kirby

1
@DavidScottKirby Javascript, bu sayıları otomatik olarak en yakın 64 bitlik kayan gösterimine dönüştürür. Kodda tam hassas sayılara sahip olmamanın gerçek yararı, dosya boyutunun azaltılmasıdır.
le_m

İkinci çözümünüz, üçüncü taraf kitaplıktan ziyade daha yeni yerleşik olanı kullanan yanıtıfunction factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; } da görmek için basitleştirilebilir . BigInt
Patrick Roberts

100. sayı 158 karakter uzunluğunda
Barbu Barbu

99

Bir döngü kullanmalısınız.

İşte 100 faktöriyelini 10.000 kez hesaplayarak karşılaştırılan iki versiyon.

Özyinelemeli

function rFact(num)
{
    if (num === 0)
      { return 1; }
    else
      { return num * rFact( num - 1 ); }
}

Yinelemeli

function sFact(num)
{
    var rval=1;
    for (var i = 2; i <= num; i++)
        rval = rval * i;
    return rval;
}

Canlı: http://jsfiddle.net/xMpTv/

Sonuçlarım şunu gösteriyor:
- Yinelemeli ~ 150 milisaniye
- Yinelemeli ~ 5 milisaniye ..


+1 Harika cevap! Bununla birlikte, daha büyük sayılar için faktoriyelleri hesaplamak için birden fazla çağrı olduğunda not alma makul olabilir.
Tadeck

@Tadeck, teşekkürler. Nitekim, bu durumda not alma çok yararlıdır ve bu nedenle Margus cevabı doğru cevap olarak seçilmiştir :)
Gabriele Petrioli

Özyinelemenin 1 satırlık versiyonu: function factorial (num) {return (num == 1)? num: num * arguments.callee (num-1); }
jbyrd

2
@HWTech, yöntemleri asla çağırmıyorsunuz. Testiniz, iki yöntemi tanımlama hızlarını karşılaştırıyor .. yürütme sürelerini değil .. Bu daha iyi bir test (sadece 15'in faktöriyelini deniyor)
Gabriele Petrioli

4
rval = rval * i;Senin yerine yazabilirsinrval *= i;
Ryan

30

Hala Margus'un cevabının en iyisi olduğunu düşünüyorum. Bununla birlikte, 0 ile 1 aralığındaki sayıların faktöriyellerini de (yani gama işlevi) hesaplamak istiyorsanız, bu yaklaşımı kullanamazsınız çünkü arama tablosunun sonsuz değerler içermesi gerekecektir.

Bununla birlikte, faktöriyellerin değerlerini yaklaşık olarak tahmin edebilirsiniz ve oldukça hızlıdır, kendini yinelemeli olarak çağırmaktan veya en azından döngüye sokmaktan daha hızlıdır (özellikle değerler büyümeye başladığında).

İyi bir yaklaşım yöntemi Lanczos'unkidir

İşte JavaScript'te bir uygulama (aylar önce yazdığım bir hesap makinesinden alınmıştır):

function factorial(op) {
 // Lanczos Approximation of the Gamma Function
 // As described in Numerical Recipes in C (2nd ed. Cambridge University Press, 1992)
 var z = op + 1;
 var p = [1.000000000190015, 76.18009172947146, -86.50532032941677, 24.01409824083091, -1.231739572450155, 1.208650973866179E-3, -5.395239384953E-6];

 var d1 = Math.sqrt(2 * Math.PI) / z;
 var d2 = p[0];

 for (var i = 1; i <= 6; ++i)
  d2 += p[i] / (z + i);

 var d3 = Math.pow((z + 5.5), (z + 0.5));
 var d4 = Math.exp(-(z + 5.5));

 d = d1 * d2 * d3 * d4;

 return d;
}

Artık factorial(0.41), vb. Gibi harika şeyler yapabilirsiniz , ancak doğruluk biraz düşük olabilir, sonuçta bu, sonucun bir tahminidir.


oldukça ilginç bir yaklaşım, teşekkürler.
Ken

Bana bir ton zaman kazandırdı, çok teşekkürler :)
nicolaskruchten

For döngüsü altındaki kısmı olarak değiştirmenizi tavsiye ederim var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;. Bu, 169'a kadar faktöriyelleri hesaplamanıza olanak tanır! şu anda sadece 140 yerine !. Bu, Number170 olan veri türünü kullanan maksimum temsil edilebilir faktöryel değere oldukça yakındır .
le_m

18

Doğal sayılarla çalışıyorsanız, arama tablosu en bariz yoldur. Herhangi bir faktöriyel gerçek zamanlı olarak hesaplamak için, daha önce hesapladığınız sayıları kaydederek bir önbellekle hızlandırabilirsiniz. Gibi bir şey:

factorial = (function() {
    var cache = {},
        fn = function(n) {
            if (n === 0) {
                return 1;
            } else if (cache[n]) {
                return cache[n];
            }
            return cache[n] = n * fn(n -1);
        };
    return fn;
})();

Daha da hızlandırmak için bazı değerleri önceden hesaplayabilirsiniz.


3
Bu yanıta dayalı herhangi bir işlev için (ayrıca biraz daha hızlı :)), ayrıca önbellek boyutunda bir sınır da dahil olmak üzere bir otomatik hatırlatıcı oluşturdum. stackoverflow.com/a/10031674/36537
Phil H

16

İşte benim çözümüm:

function fac(n){
    return(n<2)?1:fac(n-1)*n;
}

Bulduğum en basit yol (daha az karakter / satır) , yalnızca bir kod satırına sahip bir işlev.


Düzenleme:
Bazı karakterleri gerçekten kaydetmek istiyorsanız, bir Ok Fonksiyonu (21 bayt) ile gidebilirsiniz :

f=n=>(n<2)?1:f(n-1)*n

7
f=n=>n?f(n-1)*n:1
Şununla

ne yazık ki görmek güzel ve form olarak kısa olsa bile, bunu yapmanın en yavaş yolu bu.
Zibri

13

ES6 ile sadece bir satır

const factorial = n => !(n > 1) ? 1 : factorial(n - 1) * n;


factorial = n => n <= 1 ? 1 : factorial(n - 1) * n
Naramsim

10

kısa ve kolay özyinelemeli işlev (bunu bir döngü ile de yapabilirsiniz, ancak bunun performansta herhangi bir fark yaratacağını düşünmüyorum):

function factorial (n){
  if (n==0 || n==1){
    return 1;
  }
  return factorial(n-1)*n;
} 

çok büyük bir n için, stirlings yaklaşımını kullanabilirsiniz - ancak bu size yalnızca yaklaşık bir değer verecektir.

DÜZENLEME: Bunun için neden olumsuz oy aldığıma dair bir yorum güzel olurdu ...

DÜZENLEME2: Bu, bir döngü kullanan özüm olacaktır (bu daha iyi bir seçim olacaktır):

function factorial (n){
  j = 1;
  for(i=1;i<=n;i++){
    j = j*i;
  }
  return j;
}

Bence en iyi çözüm, Margus'un bahsettiği gibi önbelleğe alınmış değerleri kullanmak ve daha büyük değerler için stirlings yaklaşımını kullanmak olacaktır ( gerçekten hızlı olmanız ve böylesine büyük sayılarda bu kadar kesin olmanız gerekmediğini varsayıyoruz ).


4
Kuyruk çağrısı optimizasyonu olmayan dillerde (yani en yaygın kullanılan diller), bunu yapmanın kolay olduğu yerlerde yinelemeli olmayan bir uygulama kullanmak daha iyidir, ancak bunun etrafında yollar vardır: paulbarry.com/articles/2009/08/30 / tail-call-optimization
Daniel Earwicker

Bu gerçekten de kesinlikle o kadar hızlı değil, çünkü uygulanmış olsaydı TCO'yu bile kullanmazdı. Ama bu basit ve ben buna olumsuz oy vermem. Kesinlikle en hızlı değil.
haylem

Yinelemeli çağrı kuyruk konumunda olmadığından, bu işlev için kuyruk çağrısı optimizasyonu mümkün değildir.
Fred Foo

3
@Josh, (olumsuz seçmen değil ) en hızlısı, oldukça farkla
döngüdür

7

Bakın, herhangi bir tek argüman işlevini alan ve onu ezberleyen memoizer. Önbellek boyutu sınırı ve ilgili denetim de dahil olmak üzere @ xPheRe'nin çözümünden marjinal olarak daha hızlı olduğu ortaya çıkıyor , çünkü kısa devreyi kullanıyorum vb.

function memoize(func, max) {
    max = max || 5000;
    return (function() {
        var cache = {};
        var remaining = max;
        function fn(n) {
            return (cache[n] || (remaining-- >0 ? (cache[n]=func(n)) : func(n)));
        }
        return fn;
    }());
}

function fact(n) {
    return n<2 ? 1: n*fact(n-1);
}

// construct memoized version
var memfact = memoize(fact,170);

// xPheRe's solution
var factorial = (function() {
    var cache = {},
        fn = function(n) {
            if (n === 0) {
                return 1;
            } else if (cache[n]) {
                return cache[n];
            }
            return cache[n] = n * fn(n -1);
        };
    return fn;
}());

Chrome'daki makinemde yinelemeli sürümden yaklaşık 25 kat daha hızlı ve xPheRe'den% 10 daha hızlı.


6

En hızlı faktöryel fonksiyon

Bu döngü tabanlı sürümün en hızlı faktöriyel işlev olabileceğini düşünüyorum.

function factorial(n, r = 1) {
  while (n > 0) r *= n--;
  return r;
}

// Default parameters `r = 1`,
//   was introduced in ES6

Ve işte benim gerekçem:

  • Özyinelemeli işlevler, belleğe alma ile bile, bir işlev çağrısının ek yüküne sahiptir (temelde işlevleri yığına itmek) ve bu, bir döngü kullanmaktan daha az performanslıdır.
  • forDöngüler ve whiledöngüler benzer performansa sahipken , forbaşlatma ifadesi ve son ifade olmayan bir döngü tuhaf görünür; muhtemelen daha iyi yazmaya for(; n > 0;)olarakwhile(n > 0)
  • Sadece iki parametre nver böylece teori daha az parametreler yardımıyla kullanılır, daha az zaman bellek tahsis geçirdi
  • nSıfır olup olmadığını kontrol eden azaltılmış bir döngü kullanır - Bilgisayarların ikili sayıları (0 ve 1) kontrol etmede diğer tam sayıları kontrol etmekten daha iyi olduğuna dair teoriler duydum

5

Bu yazıya rastladım. Buradaki tüm katkılardan esinlenerek, daha önce tartışmadığım iki özelliğe sahip kendi sürümümü oluşturdum: 1) Argümanın negatif olmayan bir tamsayı olduğundan emin olmak için bir kontrol 2) Önbellekten bir birim yapmak ve kendi içinde bir kod biti yapma işlevi. Eğlenmek için olabildiğince kompakt yapmaya çalıştım. Bazıları bunu zarif bulabilir, bazıları ise son derece belirsiz olduğunu düşünebilir. Her neyse, işte burada:

var fact;
(fact = function(n){
    if ((n = parseInt(n)) < 0 || isNaN(n)) throw "Must be non-negative number";
    var cache = fact.cache, i = cache.length - 1;
    while (i < n) cache.push(cache[i++] * i);
    return cache[n];
}).cache = [1];

Önbelleği önceden doldurabilir veya aramalar geçtikçe doldurulmasına izin verebilirsiniz. Ancak ilk öğe (gerçekte (0) mevcut olmalı yoksa kırılacaktır.

Zevk almak :)


4

ES6 kullanmak çok basit

const factorial = n => n ? (n * factorial(n-1)) : 1;

Burada bir örnek görün


4

İşte bir çözüm:

function factorial(number) {
  total = 1
  while (number > 0) {
    total *= number
    number = number - 1
  }
  return total
}

4

ES6 kullanarak bunu hem hızlı hem de kısa sürede gerçekleştirebilirsiniz:

const factorial = n => [...Array(n + 1).keys()].slice(1).reduce((acc, cur) => acc * cur, 1)

3

Faktöriyel hesaplama kodu gereksinimlerinize bağlıdır.

  1. Taşma konusunda endişeli misiniz?
  2. Hangi girdi aralığına sahip olacaksınız?
  3. Boyutu veya zamanı küçültmek sizin için daha mı önemli?
  4. Faktöriyel ile ne yapacaksın?

1. ve 4. noktalarla ilgili olarak, faktöriyelin kendisini değerlendirmek için bir fonksiyona sahip olmaktansa, faktöriyelin günlüğünü doğrudan değerlendirecek bir fonksiyona sahip olmak genellikle daha faydalıdır .

İşte bu sorunları tartışan bir blog yazısı . İşte JavaScript'e taşımak için önemsiz olan günlük faktöryel hesaplama için bazı C # kodu . Ancak yukarıdaki sorulara verdiğiniz cevaplara bağlı olarak ihtiyaçlarınız için en iyisi olmayabilir.


Numaralı liste muhtemelen yorumlarda yer almalıdır. Geriye kalan tek şey iki bağlantıdır ve yalnızca bağlantı yanıtlarının kullanılması önerilmez.
Barett

3

Bu kompakt döngü tabanlı bir versiyondur

function factorial( _n )
{
    var _p = 1 ;
    while( _n > 0 ) { _p *= _n-- ; }
    return _p ;
}

Veya Math nesnesini geçersiz kılabilirsiniz (özyinelemeli sürüm):

Math.factorial = function( _x )  { return _x <= 1 ? 1 : _x * Math.factorial( --_x ) ; }

Veya her iki yaklaşıma da katılın ...


1
Yukarıdaki kodun içinde düzelttim. Teşekkür ederim!
Sandro Rosa

3

Tam bir arama tablosuNumber.MAX_VALUE < 171! kullanabileceğimiz gerçeğinden yararlanarak bellek 1.4 kilobayt daha az kaplıyor sadece 171 kompakt dizi öğelerinin oluşan.

Çalışma zamanı karmaşıklığı O (1) ve minimum dizi erişimi ek yüküne sahip hızlı bir arama işlevi daha sonra aşağıdaki gibi görünecektir:

// Lookup table for n! for 0 <= n <= 170:
const factorials = [1,1,2,6,24,120,720,5040,40320,362880,3628800,39916800,479001600,6227020800,87178291200,1307674368e3,20922789888e3,355687428096e3,6402373705728e3,121645100408832e3,243290200817664e4,5109094217170944e4,1.1240007277776077e21,2.585201673888498e22,6.204484017332394e23,1.5511210043330986e25,4.0329146112660565e26,1.0888869450418352e28,3.0488834461171387e29,8.841761993739702e30,2.6525285981219107e32,8.222838654177922e33,2.631308369336935e35,8.683317618811886e36,2.9523279903960416e38,1.0333147966386145e40,3.7199332678990125e41,1.3763753091226346e43,5.230226174666011e44,2.0397882081197444e46,8.159152832478977e47,3.345252661316381e49,1.40500611775288e51,6.041526306337383e52,2.658271574788449e54,1.1962222086548019e56,5.502622159812089e57,2.5862324151116818e59,1.2413915592536073e61,6.082818640342675e62,3.0414093201713376e64,1.5511187532873822e66,8.065817517094388e67,4.2748832840600255e69,2.308436973392414e71,1.2696403353658276e73,7.109985878048635e74,4.0526919504877214e76,2.3505613312828785e78,1.3868311854568984e80,8.32098711274139e81,5.075802138772248e83,3.146997326038794e85,1.98260831540444e87,1.2688693218588417e89,8.247650592082472e90,5.443449390774431e92,3.647111091818868e94,2.4800355424368305e96,1.711224524281413e98,1.1978571669969892e100,8.504785885678623e101,6.1234458376886085e103,4.4701154615126844e105,3.307885441519386e107,2.48091408113954e109,1.8854947016660504e111,1.4518309202828587e113,1.1324281178206297e115,8.946182130782976e116,7.156945704626381e118,5.797126020747368e120,4.753643337012842e122,3.945523969720659e124,3.314240134565353e126,2.81710411438055e128,2.4227095383672734e130,2.107757298379528e132,1.8548264225739844e134,1.650795516090846e136,1.4857159644817615e138,1.352001527678403e140,1.2438414054641308e142,1.1567725070816416e144,1.087366156656743e146,1.032997848823906e148,9.916779348709496e149,9.619275968248212e151,9.426890448883248e153,9.332621544394415e155,9.332621544394415e157,9.42594775983836e159,9.614466715035127e161,9.90290071648618e163,1.0299016745145628e166,1.081396758240291e168,1.1462805637347084e170,1.226520203196138e172,1.324641819451829e174,1.4438595832024937e176,1.588245541522743e178,1.7629525510902446e180,1.974506857221074e182,2.2311927486598138e184,2.5435597334721877e186,2.925093693493016e188,3.393108684451898e190,3.969937160808721e192,4.684525849754291e194,5.574585761207606e196,6.689502913449127e198,8.094298525273444e200,9.875044200833601e202,1.214630436702533e205,1.506141741511141e207,1.882677176888926e209,2.372173242880047e211,3.0126600184576594e213,3.856204823625804e215,4.974504222477287e217,6.466855489220474e219,8.47158069087882e221,1.1182486511960043e224,1.4872707060906857e226,1.9929427461615188e228,2.6904727073180504e230,3.659042881952549e232,5.012888748274992e234,6.917786472619489e236,9.615723196941089e238,1.3462012475717526e241,1.898143759076171e243,2.695364137888163e245,3.854370717180073e247,5.5502938327393044e249,8.047926057471992e251,1.1749972043909107e254,1.727245890454639e256,2.5563239178728654e258,3.80892263763057e260,5.713383956445855e262,8.62720977423324e264,1.3113358856834524e267,2.0063439050956823e269,3.0897696138473508e271,4.789142901463394e273,7.471062926282894e275,1.1729568794264145e278,1.853271869493735e280,2.9467022724950384e282,4.7147236359920616e284,7.590705053947219e286,1.2296942187394494e289,2.0044015765453026e291,3.287218585534296e293,5.423910666131589e295,9.003691705778438e297,1.503616514864999e300,2.5260757449731984e302,4.269068009004705e304,7.257415615307999e306];

// Lookup function:
function factorial(n) {
  return factorials[n] || (n > 170 ? Infinity : NaN);
}

// Test cases:
console.log(factorial(NaN));       // NaN
console.log(factorial(-Infinity)); // NaN
console.log(factorial(-1));        // NaN
console.log(factorial(0));         // 1
console.log(factorial(170));       // 7.257415615307999e+306 < Number.MAX_VALUE
console.log(factorial(171));       // Infinity > Number.MAX_VALUE
console.log(factorial(Infinity));  // Infinity

Bu, Numberveri türünü kullanırken aldığı kadar kesin ve hızlıdır . Arama tablosunu Javascript'te hesaplamak - diğer bazı yanıtların önerdiği gibi - ne zaman hassasiyeti azaltacaktır n! > Number.MAX_SAFE_INTEGER.

Çalışma zamanı tablosunu gzip aracılığıyla sıkıştırmak, diskteki boyutunu yaklaşık 3.6'dan 1.8 kilobayt'a düşürür.


3

Tek satır cevap:

const factorial = (num, accumulator) => num <= 1 ? accumulator || 1 : factorial(--num, num * (accumulator || num + 1));

factorial(5); // 120
factorial(10); // 3628800
factorial(3); // 6
factorial(7); // 5040
// et cetera


3

BigIntGüvenlik için yinelemeli faktöryel

Çözüm BigInt, ES 2018 + / 2019 özelliğini kullanır.

Bu, çalışan örnek kullanımlarıdır BigInt, çünkü buradaki birçok yanıtın tümü Number(MDN) güvenli sınırından neredeyse hemen kaçar. En hızlı değil, ancak basit ve dolayısıyla diğer optimizasyonları uyarlamak için daha net (ilk 100 sayının önbelleği gibi).

function factorial(nat) {
   let p = BigInt(1)
   let i = BigInt(nat)

   while (1 < i--) p *= i

   return p
}

Örnek Kullanım

// 9.332621544394415e+157
Number(factorial(100))

// "933262154439441526816992388562667004907159682643816214685929638952175999
//  932299156089414639761565182862536979208272237582511852109168640000000000
//  00000000000000"
String(factorial(100))

// 9332621544394415268169923885626670049071596826438162146859296389521759999
// 3229915608941463976156518286253697920827223758251185210916864000000000000
// 000000000000n
factorial(100)
  • nBir sayısal hazır benzerlerinin ucunda 1303nbir olduğunu belirmektedir BigInttürü.
  • Eğer karıştırmak gerektiğini unutmayın BigIntile Numberbunları açıkça zorlamak sürece ve bunu yaparken doğruluğunda kaybına neden olabilir.

3

ES6 özelliklerini kullanarak, TEK satıra ve özyineleme olmadan kod yazabilir :

var factorial=(n)=>Array.from({length: n},(v, k) => k+1).reduce((a, b) => a*b, 1)


2

Tamlık için, işte kuyruk arama optimizasyonuna izin verecek özyinelemeli bir sürüm. Yine de kuyruk arama optimizasyonlarının JavaScript'te yapılıp yapılmadığından emin değilim ..

function rFact(n, acc)
{
    if (n == 0 || n == 1) return acc; 
    else return rFact(n-1, acc*n); 
}

Onu aramak için:

rFact(x, 1);

ES6 toplam sahip olma maliyetini destekliyor, ancak afaik olarak bu özellik henüz herhangi bir büyük motorda varsayılan olarak etkin değil
le_m

2

Bu, daha az yığın alanı kullanan ve önceden hesaplanmış değerleri kendi kendine ezberleyerek kaydeden yinelemeli bir çözümdür:

Math.factorial = function(n){
    if(this.factorials[n]){ // memoized
        return this.factorials[n];
    }
    var total=1;
    for(var i=n; i>0; i--){
        total*=i;
    }
    this.factorials[n] = total; // save
    return total;
};
Math.factorials={}; // store

Ayrıca bunu bir nesne değişmezi olan Math nesnesine eklediğime dikkat edin, böylece prototip yoktur. Aksine, bunları doğrudan işleve bağlamak.


Bu, alt problemler Math.factorial(100); Math.factorial(500);için hafızaya alma işleminden tam olarak yararlanmıyor - örneğin, 1..100 çarpımını iki kez hesaplayacak.
Barett

2

Aşağıdakilerin, yukarıdaki yorumlardan elde edilen en sürdürülebilir ve verimli kod parçası olduğuna inanıyorum. Bunu küresel uygulama js mimarinizde kullanabilirsiniz ... ve birden çok ad alanında yazma konusunda endişelenmeyin (çünkü bu, muhtemelen çok fazla artırmaya ihtiyaç duymayan bir görevdir). 2 yöntem adı ekledim (tercihe göre), ancak her ikisi de sadece referans oldukları için kullanılabilir.

Math.factorial = Math.fact = function(n) {
    if (isNaN(n)||n<0) return undefined;
    var f = 1; while (n > 1) {
        f *= n--;
    } return f;
};

n * (n-1) * (n-2) * ... * 1
Çarpmaya

2
// if you don't want to update the Math object, use `var factorial = ...`
Math.factorial = (function() {
    var f = function(n) {
        if (n < 1) {return 1;}  // no real error checking, could add type-check
        return (f[n] > 0) ? f[n] : f[n] = n * f(n -1);
    }
    for (i = 0; i < 101; i++) {f(i);} // precalculate some values
    return f;
}());

factorial(6); // 720, initially cached
factorial[6]; // 720, same thing, slightly faster access, 
              // but fails above current cache limit of 100
factorial(100); // 9.33262154439441e+157, called, but pulled from cache
factorial(142); // 2.6953641378881614e+245, called
factorial[141]; // 1.89814375907617e+243, now cached

Bu, ilk 100 değerin anında önbelleğe alınmasını sağlar ve önbellek kapsamına harici bir değişken getirmez, değerleri işlev nesnesinin kendisinin özellikleri olarak saklar, yani factorial(n)zaten hesaplanmış olduğunu biliyorsanız , basitçe factorial[n], biraz daha verimli olan olarak adlandırın . Bu ilk 100 değeri çalıştırmak, modern tarayıcılarda bir milisaniyenin altında bir zaman alacaktır.


Bunu 21'den sonra anladım! sayılar güvenilir değil.
Otomatik Sünger

@AutoSponge Bunun nedeni 21! > Number.MAX_SAFE_INTEGER, 64 bitlik kayan nokta olarak güvenli bir şekilde temsil edilememesidir.
le_m


2

İşte kendim yaptığım bir tane, 170'in üzerinde veya 2'nin altındaki sayıları kullanma.

function factorial(x){
 if((!(isNaN(Number(x)))) && (Number(x)<=170) && (Number(x)>=2)){
  x=Number(x);for(i=x-(1);i>=1;--i){
   x*=i;
  }
 }return x;
}

Çarpmaya tam tersi yerine n * (n-1) * (n-2) * ... * 1 ile başlayarak, n >> 20 için hassasiyette 4 haneye kadar kaybedersiniz. Ayrıca, istenmeyen bir küresel değişken ive çok fazla Numberdönüşüm gerçekleştiriyor ve 0 için yanlış sonuçlar veriyor! (belirttiğiniz gibi, ama neden?).
le_m

2

İşte kodum

function factorial(num){
    var result = num;
    for(i=num;i>=2;i--){
        result = result * (i-1);
    }
    return result;
}

1
Eğer (n> 170) e = Sonsuz. Ve kodunuz çok büyük bir sayı oluşturacaktır. herhangi bir taşma olmayacak mı?
prime

İçin yanlış sonuç factorial(0). Ayrıca, çarpmaya n * (n-1) * (n-2) * ... * 1 ile başladığınızda, n >> 20 için hassasiyette 4 haneye kadar kaybedersiniz. @Prime: 170! > Number.MAX_VALUEve en iyi şekilde temsil edilir Infinity.
le_m

2

Önbelleğe alınan döngü en hızlı olmalıdır (en azından birden çok kez çağrıldığında)

var factorial = (function() {
  var x =[];

  return function (num) {
    if (x[num] >0) return x[num];
    var rval=1;
    for (var i = 2; i <= num; i++) {
        rval = rval * i;
        x[i] = rval;
    }
    return rval;
  }
})();

2
function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n)
}

Tarafından sunulmuştur http://javascript.info/tutorial/number-math bir amacı hesaplanması için uygun bir tamsayıdır olmadığını değerlendirmek için basit bir yöntemdir.

var factorials=[[1,2,6],3];

Gereksiz hesaplamalar gerektiren, "1 ile çarp" ile işlenebilir veya canlı olarak işlemeye değmeyen basit bir denklem olan tek basamaklı bir basit Ezberlenmiş faktöriye seti.

var factorial = (function(memo,n) {
    this.memomize = (function(n) {
        var ni=n-1;
        if(factorials[1]<n) {
            factorials[0][ni]=0;
            for(var factorial_index=factorials[1]-1;factorials[1]<n;factorial_index++) {
                factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);
                factorials[1]++;
            }
        }
    });
    this.factorialize = (function(n) {
        return (n<3)?n:(factorialize(n-1)*n);
    });
    if(isNumeric(n)) {
        if(memo===true) {
            this.memomize(n);
            return factorials[0][n-1];
        }
        return this.factorialize(n);
    }
    return factorials;
});

Diğer üyelerden gelen girdileri inceledikten sonra (Günlük tavsiyesi hariç, ancak bunu daha sonra uygulayabilirim) devam ettim ve oldukça basit bir senaryoyu bir araya getirdim. Basit bir eğitimsiz JavaScript OOP örneğiyle başladım ve faktöriyelleri işlemek için küçük bir sınıf oluşturdum. Daha sonra yukarıda önerilen Memoization sürümümü uyguladım. Ayrıca kısaltmayı uyguladım, ancak küçük bir hata düzeltmesi yaptım; "N <2" yi "n <3" olarak değiştirdim. "n <2" yine de israf olan n = 2'yi işler, çünkü 2 * 1 = 2 için yineleme yapardınız; bu bence bir israf. "N <3" olarak değiştirdim; çünkü n 1 veya 2 ise, basitçe n döndürür, 3 veya daha fazla ise normal olarak değerlendirilir. Elbette kurallar uygulandığında, işlevlerimi azalan varsayılan yürütme sırasına yerleştirdim. Memo'lu ve normal yürütme arasında hızlı değişiklik yapılmasına izin vermek için bool (true | false) seçeneğini ekledim (Sayfanızda "stili" değiştirmeye gerek kalmadan ne zaman geçiş yapmak istediğinizi asla bilemezsiniz) Daha önce de söylediğim gibi Memoized factorials değişkeni, 3 başlangıç ​​konumu ile ayarlanır, 4 karakter alır ve israflı hesaplamaları en aza indirir. Üçüncü yinelemeyi geçen her şey, çift basamaklı matematik artı ile uğraşıyorsunuz. Bu konuda yeterince titizseniz, faktöriyel bir tabloda (uygulandığı gibi) koşarsınız. 4 karakter alarak gereksiz hesaplamaları en aza indirir. Üçüncü yinelemeyi geçen her şey, çift basamaklı matematik artı ile uğraşıyorsunuz. Bu konuda yeterince titizseniz, faktöriyel bir tabloda (uygulandığı gibi) koşarsınız. 4 karakter alarak boşa giden hesaplamaları en aza indirir. Üçüncü yinelemeyi geçen her şey, çift basamaklı matematik artı ile uğraşıyorsunuz. Bu konuda yeterince titizseniz, faktöriyel bir tabloda (uygulandığı gibi) koşarsınız.

Bundan sonra ne planladım? Yerel ve | oturum depolaması, temelde yukarıda bahsedilen "tablo" sorununu ele alarak, gerekli yinelemelerin vaka bazında önbelleğe alınmasına izin verir. Bu aynı zamanda veritabanı ve sunucu tarafı alanından da büyük ölçüde tasarruf sağlayacaktır. Bununla birlikte, localStorage ile giderseniz, basitçe bir sayı listesi depolamak ve ekranlarının daha hızlı GÖRÜNMESİNİ sağlamak için, temelde kullanıcılarınızın bilgisayarındaki alanı emiyor olacaksınız, ancak çok büyük bir ihtiyaç olduğunda uzun bir süre sonra bu yavaş olacaktır. Bence sessionStorage (Sekme ayrıldıktan sonra temizleme) çok daha iyi bir yol olacaktır. Muhtemelen bunu kendi kendini dengeleyen bir sunucu / yerel bağımlı önbellek ile birleştirmek? A kullanıcısının X yinelemeye ihtiyacı var. B kullanıcısının Y yinelemesine ihtiyacı var. X + Y / 2 = Yerel olarak önbelleğe alınması gereken miktar. Ardından, sitenin optimizasyonuna kendini ayarlayana kadar her kullanıcı için yükleme süresi ve yürütme zamanı karşılaştırmaları canlı olarak tespit edin ve oynayın. Teşekkürler!

Düzenleme 3:

var f=[1,2,6];
var fc=3;
var factorial = (function(memo) {
    this.memomize = (function(n) {
        var ni=n-1;
        if(fc<n) {
            for(var fi=fc-1;fc<n;fi++) {
                f[fc]=f[fi]*(fc+1);
                fc++;
            }
        }
        return f[ni];
    });

    this.factorialize = (function(n) {
        return (n<3)?n:(factorialize(n-1)*n);
    });

    this.fractal = (function (functio) {
        return function(n) {
            if(isNumeric(n)) {
                return functio(n);
            }
            return NaN;
        }
    });

    if(memo===true) {
        return this.fractal(memomize);
    }
    return this.fractal(factorialize);
});

Bu düzenleme, başka bir Yığın önerisi uygular ve işlevi faktöriyel (doğru) (5) olarak adlandırmama izin verir, ki bu da hedeflerimden biridir. : 3 Ayrıca bazı gereksiz atamaları kaldırdım ve bazı genel olmayan değişken isimlerini kısalttım.


İade undefined0 !. ES6 değiştirmesini sağlar isNumericile Number.isInteger. Gibi satırlar factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);tamamen okunamaz.
le_m

2

İşte daha yeni javascript işlevlerini doldurma , eşleme , azaltma ve yapıcı (ve şişman ok sözdizimi) kullananlardan biri :

Math.factorial = n => n === 0 ? 1 : Array(n).fill(null).map((e,i)=>i+1).reduce((p,c)=>p*c)

Düzenleme: n === 0 işlemek için güncellendi


2
Bu gerçekten çirkin, okunamayan bir kod satırı.
jungledev

1
Bu harika bir fikir. Uzunluğu iki kez geçmek yerine, neden tüm mantığı azaltma işlevine dönüştürüp uç durumu işlemek için başlangıç ​​değerini kullanmıyorsunuz n === 0? Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
AlexSashaRegan

2
function computeFactorialOfN(n) {
  var output=1;
  for(i=1; i<=n; i++){
    output*=i;
  } return output;
}
computeFactorialOfN(5);

2
StackOverflow'a hoş geldiniz ve yardımınız için teşekkürler. Bazı açıklamalar ekleyerek cevabınızı daha da iyi hale getirmek isteyebilirsiniz.
Elias MP
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.