Apa fungsi faktorial tercepat dalam JavaScript? [Tutup]


98

Mencari implementasi yang sangat cepat dari fungsi faktorial dalam JavaScript. Ada saran?


8
Berapa kisaran argumen yang mungkin?
Nikita Rybak

5
Sudahkah Anda mempertimbangkan untuk menghitung sebelumnya faktorial dan menyimpan nilai dalam tabel pencarian?
Waleed Amjad

2
Apa penerapan fungsi seperti itu? Dengan kata lain, untuk apa Anda akan menggunakannya?
Pointy

@Nikita Rybak, cuma 1 agrument (n). Jika (n> 170) e = Infinity
Ken

@ Pointy, layanan kalkulator matematika lainnya.
Ken

Jawaban:


113

Anda dapat mencari (1 ... 100)! di Wolfram | Alpha untuk menghitung sebelumnya urutan faktorial.

100 angka pertama adalah:

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

Jika Anda masih ingin menghitung nilainya sendiri, Anda dapat menggunakan 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;
}

Edit: 21.08.2014

Solusi 2

Saya pikir akan berguna untuk menambahkan contoh kerja fungsi faktorial iteratif malas yang menggunakan angka besar untuk mendapatkan hasil yang tepat dengan memoization dan cache sebagai perbandingan

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);

Saya berasumsi Anda akan menggunakan semacam closure untuk membatasi visibilitas nama variabel.

Ref : BigNumber Sandbox : JsFiddle


Nilai setelah 6402373705728000 akan dipotong jadi jika Anda akan menggunakan pendekatan ini, pastikan untuk mengubahnya menjadi eksponensial sebelum menggunakan tabel yang disebutkan di atas.
David Scott Kirby

1
@DavidScottKirby Javascript secara otomatis mengonversi angka-angka ini ke representasi float 64-bit terdekat. Manfaat nyata dari tidak adanya angka presisi penuh dalam kode adalah mengurangi ukuran file.
le_m

Solusi kedua Anda dapat disederhanakan menjadi function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }Juga lihat jawaban saya yang menggunakan bawaan yang BigIntlebih baru daripada perpustakaan pihak ketiga.
Patrick Roberts

jadi, nomor 100 memiliki panjang 158 karakter
Barbu Barbu

99

Anda harus menggunakan loop.

Berikut adalah dua versi yang diukur dengan menghitung faktorial 100 untuk 10.000 kali.

Rekursif

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

Iteratif

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

Langsung di: http://jsfiddle.net/xMpTv/

Hasil saya menunjukkan:
- Rekursif ~ 150 milidetik
- Iteratif ~ 5 milidetik ..


+1 Jawaban bagus! Meskipun memoisasi mungkin masuk akal ketika ada beberapa panggilan untuk menghitung faktorial untuk nomor yang lebih besar.
Tadeck

@Tadeck, terima kasih. Memang memoisasi sangat berguna dalam kasus ini dan itulah mengapa jawaban Margus dipilih sebagai yang benar :)
Gabriele Petrioli

Versi rekursif 1 baris: function factorial (num) {return (num == 1)? num: num * arguments.callee (num-1); }
jbyrd

2
@HWTech, Anda tidak pernah memanggil metode tersebut. Pengujian Anda membandingkan kecepatan dalam menentukan kedua metode .. bukan waktu yang mereka
butuhkan

4
Alih-alih rval = rval * i;Anda bisa menulisrval *= i;
Ryan

29

Saya masih berpikir jawaban Margus adalah yang terbaik. Namun jika Anda ingin menghitung faktorial angka dalam rentang 0 hingga 1 (yaitu fungsi gamma) juga, Anda tidak dapat menggunakan pendekatan itu karena tabel pemeta harus berisi nilai tak hingga.

Namun, Anda dapat memperkirakan nilai faktorial, dan ini cukup cepat, lebih cepat daripada memanggil dirinya sendiri secara rekursif atau setidaknya mengulanginya (terutama ketika nilai mulai menjadi lebih besar).

Metode perkiraan yang baik adalah metode Lanczos

Berikut adalah implementasi dalam JavaScript (porting dari kalkulator yang saya tulis bulan lalu):

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;
}

Anda sekarang dapat melakukan hal-hal keren seperti factorial(0.41), dll. Namun, akurasinya mungkin sedikit salah, bagaimanapun juga, ini adalah perkiraan hasilnya.


pendekatan yang cukup menarik, terima kasih.
Ken

Baru saja menghemat banyak waktu, terima kasih banyak :)
nicolaskruchten

Saya sarankan untuk mengubah bagian di bawah for-loop menjadi var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;. Ini memungkinkan Anda untuk menghitung faktorial hingga 169! bukannya saat ini hanya 140 !. Ini cukup dekat dengan faktorial terwakili maksimum yang menggunakan Numbertipe data, yaitu 170 !.
le_m

18

Tabel pemeta adalah cara yang tepat untuk digunakan, jika Anda menggunakan bilangan asli. Untuk menghitung faktorial apa pun secara real-time, Anda dapat mempercepatnya dengan cache, menyimpan angka-angka yang telah Anda hitung sebelumnya. Sesuatu seperti:

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;
})();

Anda dapat menghitung sebelumnya beberapa nilai untuk lebih mempercepatnya.


3
Saya telah membuat sebuah auto-memoizer untuk setiap fungsi berdasarkan jawaban ini (juga sedikit lebih cepat :)), juga termasuk batasan ukuran cache. stackoverflow.com/a/10031674/36537
Phil H

16

Inilah solusi saya:

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

Ini adalah cara paling sederhana (lebih sedikit karakter / baris) yang saya temukan, hanya fungsi dengan satu baris kode.


Sunting:
Jika Anda benar-benar ingin menyimpan beberapa karakter, Anda dapat menggunakan Fungsi Panah (21 byte) :

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

7
Menghemat lebih banyak lagi dengan f=n=>n?f(n-1)*n:1...
le_m

sayangnya bahkan jika itu bagus untuk dilihat dan pendek dalam bentuk, ini adalah cara paling lambat untuk melakukannya.
Zibri

12

Hanya Satu baris dengan ES6

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


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

10

fungsi rekursif yang singkat dan mudah (Anda juga dapat melakukannya dengan loop, tetapi menurut saya itu tidak akan membuat perbedaan dalam performa):

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

untuk n yang sangat besar, Anda dapat menggunakan perkiraan putaran - tetapi itu hanya akan memberi Anda nilai perkiraan.

EDIT: komentar tentang mengapa saya mendapatkan suara negatif untuk ini akan menyenangkan ...

EDIT2: ini akan menjadi solusi jiwa menggunakan loop (yang akan menjadi pilihan yang lebih baik):

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

Saya pikir solusi terbaik adalah dengan menggunakan nilai-nilai cache, sebagai Margus disebutkan dan menggunakan Stirlings pendekatan untuk nilai-nilai yang lebih besar (diasumsikan Anda harus benar-benar cepat dan tidak harus yang sebenarnya di nomor besar seperti).


4
Dalam bahasa tanpa pengoptimalan panggilan ekor (yaitu bahasa yang paling banyak digunakan), lebih baik menggunakan implementasi non-rekursif yang mudah dilakukan, meskipun ada beberapa cara untuk mengatasinya: paulbarry.com/articles/2009/08/30 / tail-call-optimization
Daniel Earwicker

itu memang tidak secepat itu, karena TCO bahkan tidak akan digunakan, jika diterapkan. Tapi itu sederhana dan saya tidak akan meremehkannya. Pasti ini bukan yang tercepat.
haylem

Pengoptimalan panggilan ekor bahkan tidak memungkinkan untuk fungsi ini, karena panggilan rekursif tidak dalam posisi ekor.
Fred Foo

3
@Josh, ( bukan downvoter ) tercepat adalah loop dengan selisih yang cukup ..
Gabriele Petrioli

7

Lihatlah, memoizer, yang mengambil fungsi argumen tunggal dan mem-memoasinya. Ternyata sedikit lebih cepat dari solusi @ xPheRe , termasuk batas ukuran cache dan pemeriksaan terkait, karena saya menggunakan shortcircuiting dan sebagainya.

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;
}());

Kira-kira 25x lebih cepat pada mesin saya di Chrome daripada versi rekursif, dan 10% lebih cepat dari xPheRe.


6

Fungsi faktorial tercepat

Menurut saya, versi berbasis loop ini mungkin merupakan fungsi faktorial tercepat.

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

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

Dan inilah alasan saya:

  • Fungsi rekursif, bahkan dengan memoization, memiliki overhead panggilan fungsi (pada dasarnya mendorong fungsi ke stack) yang kinerjanya kurang dari menggunakan loop
  • Sementara forloop dan whileloop memiliki kinerja yang serupa, forloop tanpa ekspresi inisialisasi dan ekspresi akhir terlihat aneh; mungkin lebih baik untuk menulis for(; n > 0;)sebagaiwhile(n > 0)
  • Hanya dua parameter ndan rdigunakan, jadi dalam teori lebih sedikit parameter berarti lebih sedikit waktu yang dihabiskan untuk mengalokasikan memori
  • Menggunakan decremented loop yang memeriksa apakah nnol - Saya pernah mendengar teori bahwa komputer lebih baik dalam memeriksa bilangan biner (0 dan 1) daripada memeriksa bilangan bulat lainnya

5

Saya menemukan posting ini. Terinspirasi oleh semua kontribusi di sini saya datang dengan versi saya sendiri, yang memiliki dua fitur yang belum pernah saya lihat sebelumnya: 1) Pemeriksaan untuk memastikan argumennya adalah bilangan bulat non-negatif 2) Membuat unit dari cache dan fungsi untuk membuatnya menjadi satu bit kode mandiri. Untuk bersenang-senang, saya mencoba membuatnya sekompak mungkin. Beberapa orang mungkin menganggapnya elegan, yang lain mungkin menganggapnya sangat tidak jelas. Bagaimanapun, ini dia:

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];

Anda dapat mengisi cache terlebih dahulu, atau membiarkannya diisi saat panggilan lewat. Tetapi elemen awal (untuk fakta (0) harus ada atau akan rusak.

Nikmati :)



4

Inilah salah satu solusinya:

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

4

Menggunakan ES6, Anda dapat mencapainya dengan cepat dan singkat:

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

3

Kode untuk menghitung faktorial tergantung pada kebutuhan Anda.

  1. Apakah Anda khawatir tentang overflow?
  2. Rentang masukan apa yang akan Anda miliki?
  3. Apakah lebih penting bagi Anda untuk meminimalkan ukuran atau waktu?
  4. Apa yang akan Anda lakukan dengan faktorial?

Mengenai poin 1 dan 4, seringkali lebih berguna memiliki fungsi untuk mengevaluasi log faktorial secara langsung daripada memiliki fungsi untuk mengevaluasi faktorial itu sendiri.

Berikut adalah entri blog yang membahas masalah ini. Berikut adalah beberapa kode C # untuk menghitung faktorial log yang akan sepele untuk port ke JavaScript. Tetapi ini mungkin bukan yang terbaik untuk kebutuhan Anda tergantung pada jawaban Anda atas pertanyaan di atas.


Daftar bernomor mungkin harus ada di komentar. Yang tersisa hanyalah dua tautan, dan jawaban hanya tautan tidak disarankan.
Barett

3

Ini adalah versi berbasis loop kompak

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

Atau Anda mungkin menimpa objek Matematika (versi rekursif):

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

Atau bergabunglah dengan kedua pendekatan ...


1
Saya memperbaikinya di dalam kode di atas. Terima kasih!
Sandro Rosa

3

Memanfaatkan fakta bahwa Number.MAX_VALUE < 171!, kita cukup menggunakan tabel pencarian lengkap yang hanya terdiri dari 171 elemen array ringkas yang memakan kurang dari 1,4 kilobyte memori.

Fungsi pencarian cepat dengan kompleksitas waktu proses O (1) dan overhead akses array minimal akan terlihat sebagai berikut:

// 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

Ini tepat dan secepat itu menggunakan Numbertipe data. Menghitung tabel pemeta dalam Javascript - seperti yang disarankan beberapa jawaban lain - akan mengurangi presisi saat n! > Number.MAX_SAFE_INTEGER.

Mengompresi tabel runtime melalui gzip mengurangi ukurannya pada disk dari sekitar 3,6 menjadi 1,8 kilobyte.


3

Jawaban satu baris:

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

Faktorial berulang dengan BigInt untuk keamanan

Solusi menggunakan BigInt, fitur ES 2018 + / 2019.

Ini adalah contoh penggunaan yang berfungsi BigInt, karena banyak jawaban di sini semuanya lepas dari batas aman Number(MDN) hampir seketika. Ini bukan yang tercepat tetapi sederhana dan dengan demikian lebih jelas untuk mengadaptasi pengoptimalan lain (seperti cache dari 100 angka pertama).

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

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

   return p
}

Contoh Penggunaan

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

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

// 9332621544394415268169923885626670049071596826438162146859296389521759999
// 3229915608941463976156518286253697920827223758251185210916864000000000000
// 000000000000n
factorial(100)
  • Di nakhir literal numerik seperti 1303nmenunjukkan itu aBigInt tipe.
  • Ingatlah bahwa Anda tidak boleh bergaul BigIntdengan Numberkecuali Anda secara eksplisit memaksanya, dan hal itu dapat menyebabkan hilangnya akurasi.

3

Menggunakan fitur ES6, dapat menulis kode pada SATU baris & tanpa rekursi :

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


2

Hanya untuk kelengkapan, berikut adalah versi rekursif yang akan memungkinkan pengoptimalan panggilan ekor. Saya tidak yakin apakah pengoptimalan panggilan ekor dilakukan dalam JavaScript ..

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

Untuk menyebutnya:

rFact(x, 1);

ES6 mendukung TCO, tetapi afaik fitur ini belum aktif secara default di mesin utama mana pun
le_m

2

Ini adalah solusi berulang yang menggunakan lebih sedikit ruang tumpukan dan menyimpan nilai yang dihitung sebelumnya dengan cara memo sendiri:

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

Juga perhatikan bahwa saya menambahkan ini ke objek Matematika yang merupakan literal objek sehingga tidak ada prototipe. Alih-alih hanya mengikat ini ke fungsi secara langsung.


Ini tidak benar-benar memanfaatkan memoization untuk subproblem - misalnya, Math.factorial(100); Math.factorial(500);akan menghitung perkalian 1..100 dua kali.
Barett

2

Saya percaya berikut ini adalah bagian kode yang paling berkelanjutan dan efisien dari komentar di atas. Anda dapat menggunakan ini dalam arsitektur js aplikasi global Anda ... dan, tidak perlu khawatir tentang menulisnya di banyak ruang nama (karena ini adalah tugas yang mungkin tidak memerlukan banyak penambahan). Saya telah menyertakan 2 nama metode (berdasarkan preferensi) tetapi keduanya dapat digunakan karena mereka hanya referensi.

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

Dengan memulai perkalian Anda dengan n * (n-1) * (n-2) * ... * 1alih - alih sebaliknya, Anda kehilangan hingga 4 digit dengan presisi untuk n >> 20.
le_m

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

Ini melakukan caching dari 100 nilai pertama dengan cepat, dan tidak memasukkan variabel eksternal ke dalam ruang lingkup untuk cache, menyimpan nilai sebagai properti dari objek fungsi itu sendiri, yang berarti bahwa jika Anda tahu factorial(n)telah dihitung, Anda dapat cukup menyebutnya sebagai factorial[n], yang sedikit lebih efisien. Menjalankan 100 nilai pertama ini akan membutuhkan waktu sub-milidetik di browser modern.


Saya menemukan itu setelah 21! angkanya tidak dapat diandalkan.
AutoSponge

@AutoSponge Itu karena 21! > Number.MAX_SAFE_INTEGER, karenanya tidak dapat dengan aman direpresentasikan sebagai float 64-bit.
le_m


2

Ini yang saya buat sendiri, jangan gunakan angka di atas 170 atau di bawah 2.

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;
}

Dengan memulai perkalian Anda dengan n * (n-1) * (n-2) * ... * 1 alih-alih sebaliknya, Anda kehilangan hingga 4 digit dengan presisi untuk n >> 20. Juga, membuat yang tidak diinginkan variabel global idan melakukan terlalu banyak Numberkonversi dan memberikan hasil yang salah untuk 0! (seperti yang Anda nyatakan, tapi mengapa?).
le_m

2

Ini kode saya

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

1
Jika (n> 170) e = Tak terhingga. Dan kode Anda akan menghasilkan sejumlah besar. tidak akan ada luapan?
Perdana

Hasil salah untuk factorial(0). Juga, dengan memulai perkalian Anda dengan n * (n-1) * (n-2) * ... * 1 alih-alih sebaliknya, Anda kehilangan hingga 4 digit dengan presisi untuk n >> 20. @prime: 170! > Number.MAX_VALUEdan paling baik diwakili dengan Infinity.
le_m

2

Loop yang di-cache harus tercepat (setidaknya ketika dipanggil beberapa kali)

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)
}

Disediakan oleh http://javascript.info/tutorial/number-math sebagai cara sederhana untuk mengevaluasi apakah suatu objek adalah bilangan bulat yang tepat untuk kalkulasi.

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

Sekumpulan sederhana faktorial Memo yang memerlukan kalkulasi berlebihan, dapat diproses dengan "dikalikan dengan 1", atau merupakan satu digit yang merupakan persamaan sederhana yang tidak layak diproses secara langsung.

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;
});

Setelah meninjau masukan dari anggota lain (tidak termasuk saran Log, meskipun saya dapat menerapkannya nanti) saya melanjutkan dan membuat skrip yang cukup sederhana. Saya mulai dengan contoh JavaScript OOP sederhana yang tidak berpendidikan dan membuat kelas kecil untuk menangani faktorial. Saya kemudian menerapkan Memoisasi versi saya yang disarankan di atas. Saya juga menerapkan Factorialization steno namun saya membuat sedikit kesalahan penyesuaian; Saya mengubah "n <2" menjadi "n <3". "n <2" masih akan memproses n = 2 yang akan menjadi sia-sia, karena Anda akan mengulang untuk 2 * 1 = 2; ini sia-sia menurut saya. Saya mengubahnya menjadi "n <3"; karena jika n adalah 1 atau 2 itu hanya akan mengembalikan n, jika 3 atau lebih itu akan mengevaluasi secara normal. Tentu saja karena aturan berlaku, saya menempatkan fungsi saya dalam urutan menurun dari eksekusi yang diasumsikan. Saya menambahkan opsi bool (true | false) untuk memungkinkan perubahan cepat antara eksekusi memo'ed dan normal (Anda tidak pernah tahu kapan Anda ingin menukar halaman Anda tanpa perlu mengubah "gaya") Seperti yang saya katakan sebelumnya variabel faktorial memoized diatur dengan 3 posisi awal, mengambil 4 karakter, dan meminimalkan perhitungan yang sia-sia. Semua yang melewati iterasi ketiga, Anda menangani matematika dua digit plus. Saya pikir jika Anda di mana cukup ngotot tentang hal itu Anda akan menjalankan pada tabel faktorial (seperti yang diterapkan). mengambil 4 karakter, dan meminimalkan perhitungan yang boros. Semua yang melewati iterasi ketiga, Anda menangani matematika dua digit plus. Saya pikir jika Anda di mana cukup ngotot tentang hal itu Anda akan menjalankan pada tabel faktorial (seperti yang diterapkan). mengambil 4 karakter, dan meminimalkan perhitungan yang boros. Semua yang melewati iterasi ketiga, Anda menangani matematika dua digit plus. Saya pikir jika Anda di mana cukup ngotot tentang hal itu Anda akan menjalankan pada tabel faktorial (seperti yang diterapkan).

Apa yang telah saya rencanakan setelah ini? local & | session storage untuk memungkinkan cache kasus per kasus dari iterasi yang diperlukan, pada dasarnya menangani masalah "tabel" yang diucapkan di atas. Ini juga akan menghemat ruang database dan server secara besar-besaran. Namun, jika Anda menggunakan localStorage Anda pada dasarnya akan menghabiskan ruang di komputer pengguna Anda hanya untuk menyimpan daftar nomor dan membuat layar mereka TERLIHAT lebih cepat, namun dalam jangka waktu yang lama dengan kebutuhan yang sangat besar ini akan menjadi lambat. Saya berpikir sessionStorage (membersihkan setelah Tab pergi) akan menjadi rute yang jauh lebih baik. Mungkin menggabungkan ini dengan server self balancing / cache dependen lokal? Pengguna A membutuhkan pengulangan X. Pengguna B membutuhkan iterasi Y. X + Y / 2 = Jumlah yang dibutuhkan dalam cache lokal. Kemudian, cukup deteksi dan ubah dengan waktu muat dan waktu eksekusi langsung untuk setiap pengguna sampai menyesuaikan diri dengan pengoptimalan untuk situs itu sendiri. Terima kasih!

Edit 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);
});

Hasil edit ini menerapkan saran Tumpukan lainnya dan memungkinkan saya memanggil fungsi sebagai faktorial (benar) (5), yang merupakan salah satu tujuan saya yang ditetapkan. : 3 Saya juga menghapus beberapa penugasan yang tidak perlu, dan menyingkat beberapa nama variabel non-publik.


Kembali undefineduntuk 0 !. ES6 memungkinkan untuk diganti isNumericdengan Number.isInteger. Garis seperti factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);sama sekali tidak bisa dibaca.
le_m

2

Ini adalah salah satu yang menggunakan fungsi javascript yang lebih baru mengisi , memetakan , mengurangi dan konstruktor (dan sintaks panah lemak):

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

Edit: diperbarui untuk menangani n === 0


2
Itu adalah salah satu baris kode yang sangat jelek dan tidak terbaca.
jungledev

1
Itu ide yang bagus. Daripada menelusuri panjangnya dua kali, mengapa tidak mengonversi semua logika ke fungsi pengurangan dan menggunakan nilai awalnya untuk menangani kasus tepi 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
Selamat datang di StackOverflow dan terima kasih atas bantuan Anda. Anda mungkin ingin membuat jawaban Anda lebih baik dengan menambahkan beberapa penjelasan.
Elias MP
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.