กำลังมองหาการใช้งานฟังก์ชันแฟกทอเรียลใน JavaScript อย่างรวดเร็ว มีข้อเสนอแนะ?
กำลังมองหาการใช้งานฟังก์ชันแฟกทอเรียลใน JavaScript อย่างรวดเร็ว มีข้อเสนอแนะ?
คำตอบ:
คุณสามารถค้นหา (1 ... 100)! บน Wolfram | Alphaเพื่อคำนวณลำดับแฟกทอเรียลล่วงหน้า
100 หมายเลขแรก ได้แก่ :
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
หากคุณยังต้องการคำนวณค่าด้วยตัวเองคุณสามารถใช้การบันทึก :
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;
}
ฉันคิดว่ามันจะมีประโยชน์ในการเพิ่มตัวอย่างการทำงานของฟังก์ชันแฟกทอเรียลซ้ำแบบขี้เกียจ ที่ใช้ตัวเลขจำนวนมากเพื่อให้ได้ผลลัพธ์ที่แน่นอนด้วยการบันทึกและแคชเป็นการเปรียบเทียบ
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);
ฉันคิดว่าคุณจะใช้การปิดบางประเภทเพื่อ จำกัด การเปิดเผยชื่อตัวแปร
function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }
ดูคำตอบของฉันซึ่งใช้BigInt
บิวด์อินล่าสุดมากกว่าไลบรารีของบุคคลที่สาม
คุณควรใช้ห่วง
นี่คือสองเวอร์ชันที่เปรียบเทียบโดยการคำนวณแฟกทอเรียลของ 100 เป็นเวลา 10.000 ครั้ง
เรียกซ้ำ
function rFact(num)
{
if (num === 0)
{ return 1; }
else
{ return num * rFact( num - 1 ); }
}
ซ้ำ
function sFact(num)
{
var rval=1;
for (var i = 2; i <= num; i++)
rval = rval * i;
return rval;
}
อยู่ที่: http://jsfiddle.net/xMpTv/
ผลลัพธ์ของฉันแสดง:
- วนซ้ำ ~ 150 มิลลิวินาที
- วนซ้ำ ~ 5 มิลลิวินาที ..
rval = rval * i;
เขียนrval *= i;
ฉันยังคิดว่าคำตอบของ Margus คือคำตอบที่ดีที่สุด อย่างไรก็ตามหากคุณต้องการคำนวณแฟกทอเรียลของตัวเลขภายในช่วง 0 ถึง 1 (เช่นฟังก์ชันแกมมา) เช่นกันคุณจะไม่สามารถใช้แนวทางนั้นได้เนื่องจากตารางการค้นหาจะต้องมีค่าไม่สิ้นสุด
อย่างไรก็ตามคุณสามารถประมาณค่าของแฟกทอเรียลได้และค่อนข้างเร็วเร็วกว่าการเรียกตัวเองซ้ำ ๆ หรือวนซ้ำเป็นอย่างน้อย (โดยเฉพาะอย่างยิ่งเมื่อค่าเริ่มใหญ่ขึ้น)
วิธีการประมาณที่ดีคือวิธีหนึ่งของ Lanczos
นี่คือการใช้งานใน JavaScript (ย้ายมาจากเครื่องคิดเลขที่ฉันเขียนเมื่อหลายเดือนก่อน):
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;
}
ตอนนี้คุณสามารถทำสิ่งดีๆเช่นfactorial(0.41)
ฯลฯ อย่างไรก็ตามความแม่นยำอาจลดลงเล็กน้อยเพราะมันเป็นการประมาณผลลัพธ์
var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;
ผมขอแนะนำให้เปลี่ยนส่วนด้านล่างสำหรับวงไป สิ่งนี้ช่วยให้คุณคำนวณแฟกทอเรียลได้สูงถึง 169! แทนที่จะเป็นเพียง 140!. นี่ค่อนข้างใกล้เคียงกับแฟกทอเรียลที่แทนค่าได้สูงสุดโดยใช้Number
ประเภทข้อมูลซึ่งก็คือ 170!
ตารางการค้นหาเป็นวิธีที่ชัดเจนหากคุณกำลังทำงานกับตัวเลขธรรมชาติ ในการคำนวณแฟกทอเรียลแบบเรียลไทม์คุณสามารถเร่งความเร็วด้วยแคชบันทึกตัวเลขที่คุณคำนวณไว้ก่อนหน้านี้ สิ่งที่ต้องการ:
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;
})();
คุณสามารถคำนวณค่าบางค่าล่วงหน้าเพื่อเร่งความเร็วให้มากยิ่งขึ้น
นี่คือทางออกของฉัน:
function fac(n){
return(n<2)?1:fac(n-1)*n;
}
เป็นวิธีที่ง่ายที่สุด(อักขระ / บรรทัดน้อย) ที่ฉันพบมีเพียงฟังก์ชันที่มีโค้ดบรรทัดเดียว
แก้ไข:
หากคุณต้องการบันทึกอักขระบางตัวคุณสามารถใช้ฟังก์ชัน Arrow (21 ไบต์) :
f=n=>(n<2)?1:f(n-1)*n
f=n=>n?f(n-1)*n:1
...
ฟังก์ชั่นการเรียกซ้ำที่สั้นและง่าย (คุณสามารถทำได้โดยใช้ลูปด้วย แต่ฉันไม่คิดว่าจะทำให้ประสิทธิภาพแตกต่างกัน):
function factorial (n){
if (n==0 || n==1){
return 1;
}
return factorial(n-1)*n;
}
สำหรับ n ที่มีขนาดใหญ่มากคุณสามารถใช้การประมาณค่าสเตอร์ลิง - แต่จะให้ค่าโดยประมาณเท่านั้น
แก้ไข:ความคิดเห็นว่าทำไมฉันถึงได้รับการโหวตลดลงคงจะดี ...
แก้ไข 2:นี่จะเป็นวิญญาณโดยใช้ลูป (ซึ่งจะเป็นทางเลือกที่ดีกว่า):
function factorial (n){
j = 1;
for(i=1;i<=n;i++){
j = j*i;
}
return j;
}
ผมคิดว่าทางออกที่ดีที่สุดที่จะใช้ค่าที่เก็บไว้เป็น Margus กล่าวถึงและใช้Stirlings ประมาณค่าขนาดใหญ่ (สันนิษฐานว่าคุณจะต้องมีจริงๆอย่างรวดเร็วและไม่จำเป็นต้องเป็นที่แน่นอนในตัวเลขใหญ่ดังกล่าว)
ดูเถิดเครื่องช่วยจำซึ่งใช้ฟังก์ชันอาร์กิวเมนต์เดียวและบันทึกมัน ปรากฎว่าเร็วกว่าโซลูชันของ @ xPheRe เล็กน้อยรวมถึงขีด จำกัด ของขนาดแคชและการตรวจสอบที่เกี่ยวข้องเนื่องจากฉันใช้การลัดวงจรและอื่น ๆ
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 ประมาณ 25 เท่าเมื่อเทียบกับเวอร์ชันเรียกซ้ำและเร็วกว่า xPheRe's 10%
ฉันคิดว่าเวอร์ชันที่ใช้ลูปนี้อาจเป็นฟังก์ชันแฟกทอเรียลที่เร็วที่สุด
function factorial(n, r = 1) {
while (n > 0) r *= n--;
return r;
}
// Default parameters `r = 1`,
// was introduced in ES6
และนี่คือเหตุผลของฉัน:
for
ลูปและwhile
ลูปมีประสิทธิภาพใกล้เคียงกัน แต่for
ลูปที่ไม่มีนิพจน์เริ่มต้นและนิพจน์สุดท้ายจะดูแปลก น่าจะดีกว่าที่จะเขียนfor(; n > 0;)
เป็นwhile(n > 0)
n
และr
ใช้ดังนั้นในทางทฤษฎีพารามิเตอร์ที่น้อยลงหมายถึงใช้เวลาในการจัดสรรหน่วยความจำน้อยลงn
เป็นศูนย์หรือไม่ - ฉันเคยได้ยินทฤษฎีมาว่าคอมพิวเตอร์ตรวจสอบเลขฐานสอง (0 และ 1) ได้ดีกว่าการตรวจสอบจำนวนเต็มอื่น ๆฉันมาเจอกระทู้นี้ แรงบันดาลใจจากการมีส่วนร่วมทั้งหมดที่นี่ฉันได้สร้างเวอร์ชันของตัวเองซึ่งมีคุณสมบัติสองอย่างที่ฉันไม่เคยเห็นมาก่อน: 1) ตรวจสอบเพื่อให้แน่ใจว่าอาร์กิวเมนต์เป็นจำนวนเต็มที่ไม่เป็นลบ 2) การสร้างหน่วยจากแคชและ ฟังก์ชั่นที่จะทำให้เป็นบิตของโค้ดในตัวเอง เพื่อความสนุกฉันพยายามทำให้มันกระชับที่สุด บางคนอาจพบว่าสง่างามบางคนอาจคิดว่ามันคลุมเครืออย่างมาก อย่างไรก็ตามนี่คือ:
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];
คุณสามารถเติมแคชล่วงหน้าหรืออนุญาตให้เติมเมื่อมีการโทร แต่องค์ประกอบเริ่มต้น (สำหรับ fact (0) ต้องมีอยู่มิฉะนั้นจะแตก
สนุก :)
นี่คือทางออกเดียว:
function factorial(number) {
total = 1
while (number > 0) {
total *= number
number = number - 1
}
return total
}
การใช้ ES6 คุณสามารถทำได้ทั้งเร็วและสั้น:
const factorial = n => [...Array(n + 1).keys()].slice(1).reduce((acc, cur) => acc * cur, 1)
รหัสในการคำนวณแฟกทอเรียลขึ้นอยู่กับความต้องการของคุณ
สำหรับจุดที่ 1 และ 4 มักมีประโยชน์มากกว่าที่จะมีฟังก์ชันในการประเมินบันทึกของแฟกทอเรียลโดยตรงแทนที่จะมีฟังก์ชันในการประเมินแฟกทอเรียล
นี่คือบล็อกโพสต์ที่กล่าวถึงปัญหาเหล่านี้ นี่คือรหัส C #บางส่วนสำหรับการคำนวณแฟกทอเรียลบันทึกที่จะพอร์ตไปยัง JavaScript เล็กน้อย แต่อาจไม่ดีที่สุดสำหรับความต้องการของคุณทั้งนี้ขึ้นอยู่กับคำตอบของคำถามด้านบน
นี่คือเวอร์ชันที่ใช้ลูปขนาดกะทัดรัด
function factorial( _n )
{
var _p = 1 ;
while( _n > 0 ) { _p *= _n-- ; }
return _p ;
}
หรือคุณอาจแทนที่วัตถุคณิตศาสตร์ (เวอร์ชันเรียกซ้ำ):
Math.factorial = function( _x ) { return _x <= 1 ? 1 : _x * Math.factorial( --_x ) ; }
หรือเข้าร่วมทั้งสองแนวทาง ...
การใช้ประโยชน์จากความจริงที่ว่าNumber.MAX_VALUE < 171!
เราสามารถใช้ตารางการค้นหาที่สมบูรณ์ซึ่งประกอบด้วยองค์ประกอบอาร์เรย์ขนาดกะทัดรัดเพียง 171 ชิ้นโดยใช้หน่วยความจำน้อยกว่า 1.4 กิโลไบต์
ฟังก์ชันการค้นหาที่รวดเร็วพร้อมความซับซ้อนของรันไทม์O (1)และค่าโสหุ้ยการเข้าถึงอาร์เรย์ขั้นต่ำจะมีลักษณะดังนี้:
// 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
สิ่งนี้แม่นยำและเร็วที่สุดเท่าที่จะทำได้โดยใช้Number
ประเภทข้อมูล การคำนวณตารางการค้นหาใน Javascript - เป็นบางคำตอบอื่น ๆ แนะนำ - n! > Number.MAX_SAFE_INTEGER
จะช่วยลดความแม่นยำเมื่อ
การบีบอัดตารางรันไทม์ผ่าน gzip จะลดขนาดบนดิสก์จากประมาณ 3.6 เป็น 1.8 กิโลไบต์
คำตอบหนึ่งบรรทัด:
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
BigInt
เพื่อความปลอดภัยโซลูชันใช้
BigInt
คุณลักษณะ ES 2018 + / 2019
นี่เป็นตัวอย่างที่ใช้BigInt
งานได้เนื่องจากคำตอบจำนวนมากในที่นี้ทั้งหมดหนีพ้นขอบเขตที่ปลอดภัยของNumber
(MDN) เกือบจะทันที ไม่ใช่วิธีที่เร็วที่สุด แต่ง่ายและชัดเจนกว่าสำหรับการปรับแต่งการเพิ่มประสิทธิภาพอื่น ๆ (เช่นแคชของตัวเลข 100 ตัวแรก)
function factorial(nat) {
let p = BigInt(1)
let i = BigInt(nat)
while (1 < i--) p *= i
return p
}
// 9.332621544394415e+157
Number(factorial(100))
// "933262154439441526816992388562667004907159682643816214685929638952175999
// 932299156089414639761565182862536979208272237582511852109168640000000000
// 00000000000000"
String(factorial(100))
// 9332621544394415268169923885626670049071596826438162146859296389521759999
// 3229915608941463976156518286253697920827223758251185210916864000000000000
// 000000000000n
factorial(100)
n
ในตอนท้ายของเช่นตัวอักษรตัวเลข1303n
แสดงให้เห็นว่ามันเป็นBigInt
ประเภทBigInt
ด้วยNumber
เว้นแต่คุณจะบังคับพวกเขาอย่างชัดเจนและการทำเช่นนั้นอาจทำให้สูญเสียความแม่นยำใช้คุณสมบัติ ES6 สามารถเขียนโค้ดในบรรทัดเดียวและไม่ต้องเรียกซ้ำ :
var factorial=(n)=>Array.from({length: n},(v, k) => k+1).reduce((a, b) => a*b, 1)
เพื่อความสมบูรณ์นี่คือเวอร์ชันเรียกซ้ำที่อนุญาตให้เพิ่มประสิทธิภาพการโทรหาง ฉันไม่แน่ใจว่าการเพิ่มประสิทธิภาพการโทรหางจะดำเนินการใน JavaScript หรือไม่
function rFact(n, acc)
{
if (n == 0 || n == 1) return acc;
else return rFact(n-1, acc*n);
}
เรียกมันว่า:
rFact(x, 1);
นี่เป็นวิธีแก้ปัญหาแบบวนซ้ำที่ใช้พื้นที่สแต็กน้อยลงและบันทึกค่าที่คำนวณไว้ก่อนหน้านี้ด้วยวิธีการบันทึกด้วยตนเอง:
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
โปรดทราบว่าฉันกำลังเพิ่มสิ่งนี้ให้กับวัตถุคณิตศาสตร์ซึ่งเป็นตัวอักษรของวัตถุดังนั้นจึงไม่มีต้นแบบ แทนที่จะผูกสิ่งเหล่านี้เข้ากับฟังก์ชันโดยตรง
Math.factorial(100); Math.factorial(500);
จะคำนวณการคูณ 1..100 สองครั้ง
ฉันเชื่อว่าต่อไปนี้เป็นโค้ดที่ยั่งยืนและมีประสิทธิภาพที่สุดจากความคิดเห็นด้านบน คุณสามารถใช้สิ่งนี้ในสถาปัตยกรรม js แอปพลิเคชันส่วนกลางของคุณ ... และไม่ต้องกังวลกับการเขียนมันในหลายเนมสเปซ (เนื่องจากเป็นงานที่อาจไม่ต้องการการเพิ่มมากนัก) ฉันได้รวมชื่อเมธอดไว้ 2 ชื่อ (ขึ้นอยู่กับความชอบ) แต่ทั้งสองสามารถใช้เป็นเพียงการอ้างอิง
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
แทนที่จะใช้วิธีอื่นคุณจะได้ตัวเลข 4 หลักที่แม่นยำสำหรับ n >> 20
// 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
นี่เป็นการแคชค่า 100 ค่าแรกทันทีและไม่นำตัวแปรภายนอกเข้ามาในขอบเขตของแคชโดยจัดเก็บค่าเป็นคุณสมบัติของอ็อบเจ็กต์ฟังก์ชันซึ่งหมายความว่าหากคุณรู้ว่าfactorial(n)
มีการคำนวณแล้วคุณสามารถ เรียกง่ายๆว่าfactorial[n]
ซึ่งมีประสิทธิภาพมากกว่าเล็กน้อย การเรียกใช้ค่า 100 ค่าแรกเหล่านี้จะใช้เวลาย่อยในระดับมิลลิวินาทีในเบราว์เซอร์สมัยใหม่
21! > Number.MAX_SAFE_INTEGER
ดังนั้นจึงไม่สามารถแสดงเป็นทศนิยม64 บิตได้อย่างปลอดภัย
นี่คือการนำไปใช้ซึ่งคำนวณทั้งแฟกทอเรียลบวกและลบ มันง่ายและรวดเร็ว
var factorial = function(n) {
return n > 1
? n * factorial(n - 1)
: n < 0
? n * factorial(n + 1)
: 1;
}
นี่คือสิ่งที่ฉันสร้างขึ้นเองอย่าใช้ตัวเลขที่สูงกว่า 170 หรือต่ำกว่า 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;
}
i
และทำการNumber
แปลงมากเกินไปและให้ผลลัพธ์ที่ไม่ถูกต้องสำหรับ 0! (ตามที่คุณระบุ แต่ทำไม?)
นี่คือรหัสของฉัน
function factorial(num){
var result = num;
for(i=num;i>=2;i--){
result = result * (i-1);
}
return result;
}
factorial(0)
. นอกจากนี้การเริ่มต้นการคูณของคุณด้วย n * (n-1) * (n-2) * ... * 1 แทนการปัดเศษอื่น ๆ คุณจะหลวมได้ถึง 4 หลักด้วยความแม่นยำสำหรับ n >> 20 @prime: และเป็นตัวแทนที่ดีที่สุดกับ170! > Number.MAX_VALUE
Infinity
ลูปที่แคชควรเร็วที่สุด (อย่างน้อยเมื่อเรียกหลายครั้ง)
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;
}
})();
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n)
}
จัดทำโดยhttp://javascript.info/tutorial/number-mathเป็นวิธีง่ายๆในการประเมินว่าวัตถุเป็นจำนวนเต็มที่เหมาะสมสำหรับการคำนวณหรือไม่
var factorials=[[1,2,6],3];
ชุดแฟกทอเรียลแบบบันทึกที่ต้องการการคำนวณซ้ำซ้อนอาจประมวลผลด้วย "คูณด้วย 1" หรือเป็นตัวเลขหนึ่งหลักที่เป็นสมการธรรมดาที่ไม่คุ้มค่ากับการประมวลผลแบบสด
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;
});
หลังจากตรวจสอบการป้อนข้อมูลจากสมาชิกคนอื่น ๆ (ไม่รวมคำแนะนำบันทึกแม้ว่าฉันจะนำไปใช้ในภายหลัง) ฉันก็ไปต่อและรวบรวมสคริปต์ที่ค่อนข้างง่าย ฉันเริ่มต้นด้วยตัวอย่าง JavaScript OOP ที่ไม่ได้รับการศึกษาและสร้างคลาสเล็ก ๆ เพื่อจัดการแฟกทอเรียล จากนั้นฉันก็ใช้ Memoization เวอร์ชันที่แนะนำข้างต้น ฉันยังใช้แฟกทอเรียลไลเซชั่นชวเลข แต่ฉันได้ทำการปรับค่าผิดพลาดเล็กน้อย ฉันเปลี่ยน "n <2" เป็น "n <3" "n <2" จะยังคงประมวลผล n = 2 ซึ่งจะเป็นการสิ้นเปลืองเพราะคุณจะทำซ้ำสำหรับ 2 * 1 = 2; นี่เป็นการสูญเปล่าในความคิดของฉัน ฉันเปลี่ยนเป็น "n <3"; เพราะถ้า n เป็น 1 หรือ 2 มันก็จะคืนค่า n ถ้าเป็น 3 ขึ้นไปมันจะประเมินตามปกติ แน่นอนว่าเป็นไปตามกฎฉันวางฟังก์ชันของฉันตามลำดับการดำเนินการที่สันนิษฐานจากมากไปหาน้อย ฉันได้เพิ่มตัวเลือกบูล (จริง | เท็จ) เพื่อให้สามารถแก้ไขได้อย่างรวดเร็วระหว่างการดำเนินการบันทึกและการดำเนินการตามปกติ (คุณไม่มีทางรู้ว่าเมื่อใดที่คุณต้องการสลับไปมาบนหน้าของคุณโดยไม่จำเป็นต้องเปลี่ยน "สไตล์") ดังที่ฉันได้กล่าวไว้ก่อนหน้านี้ ตัวแปรแฟกทอเรียลที่บันทึกไว้ถูกตั้งค่าด้วยตำแหน่งเริ่มต้น 3 ตำแหน่งโดยใช้อักขระ 4 ตัวและลดการคำนวณที่สิ้นเปลืองให้น้อยที่สุด ทุกอย่างที่ผ่านมาจากการทำซ้ำครั้งที่สามคุณกำลังจัดการกับเลขสองหลักบวก ฉันคิดว่าคุณอยู่ที่ไหน stickler เพียงพอเกี่ยวกับเรื่องนี้คุณจะเรียกใช้บนตารางแฟกทอเรียล (ตามที่ใช้งาน) ใช้อักขระ 4 ตัวและลดการคำนวณที่สิ้นเปลืองให้น้อยที่สุด ทุกอย่างที่ผ่านมาจากการทำซ้ำครั้งที่สามคุณกำลังจัดการกับเลขสองหลักบวก ฉันคิดว่าคุณอยู่ที่ไหน stickler เพียงพอเกี่ยวกับเรื่องนี้คุณจะเรียกใช้บนตารางแฟกทอเรียล (ตามที่ใช้งาน) ใช้อักขระ 4 ตัวและลดการคำนวณที่สิ้นเปลืองให้น้อยที่สุด ทุกอย่างที่ผ่านมาจากการทำซ้ำครั้งที่สามคุณกำลังจัดการกับเลขสองหลักบวก ฉันคิดว่าคุณอยู่ที่ไหน stickler เพียงพอเกี่ยวกับเรื่องนี้คุณจะเรียกใช้บนตารางแฟกทอเรียล (ตามที่ใช้งาน)
หลังจากนี้ฉันวางแผนอะไรไว้บ้าง? พื้นที่เก็บข้อมูลในเครื่องและ | เซสชันเพื่อให้สามารถแคชเคสของการทำซ้ำที่จำเป็นโดยพื้นฐานแล้วจะจัดการปัญหา "ตาราง" ที่พูดข้างต้น นอกจากนี้ยังช่วยประหยัดฐานข้อมูลและพื้นที่ฝั่งเซิร์ฟเวอร์ได้อย่างมหาศาล อย่างไรก็ตามหากคุณใช้ localStorage คุณจะต้องดูดพื้นที่บนคอมพิวเตอร์ของผู้ใช้ของคุณเป็นหลักเพียงเพื่อจัดเก็บรายการตัวเลขและทำให้หน้าจอของพวกเขาดูเร็วขึ้นอย่างไรก็ตามในระยะเวลาอันยาวนานด้วยความต้องการอันยิ่งใหญ่สิ่งนี้จะช้า ฉันกำลังคิดว่า sessionStorage (การล้างหลังจาก Tab ออก) จะเป็นเส้นทางที่ดีกว่ามาก อาจรวมสิ่งนี้เข้ากับเซิร์ฟเวอร์ที่ปรับสมดุลด้วยตนเอง / แคชที่ขึ้นกับท้องถิ่นได้หรือไม่ ผู้ใช้ A ต้องการ X ซ้ำ ผู้ใช้ B ต้องการการทำซ้ำ Y X + Y / 2 = จำนวนที่ต้องการแคชในเครื่อง จากนั้นเพียงตรวจจับและดำเนินการกับเวลาในการโหลดและเกณฑ์มาตรฐานเวลาดำเนินการสำหรับผู้ใช้ทุกคนจนกว่าจะปรับตัวเองเพื่อเพิ่มประสิทธิภาพให้กับไซต์ ขอบคุณ!
แก้ไข 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);
});
การแก้ไขนี้ใช้คำแนะนำ Stack อื่นและอนุญาตให้ฉันเรียกฟังก์ชันนี้ว่าแฟกทอเรียล (จริง) (5) ซึ่งเป็นหนึ่งในเป้าหมายของฉันที่กำหนดไว้ : 3 ฉันยังลบการกำหนดที่ไม่จำเป็นบางอย่างออกและชวเลขชื่อตัวแปรที่ไม่ใช่สาธารณะ
undefined
เป็น 0! ES6 อนุญาตให้แทนที่isNumeric
ด้วยNumber.isInteger
. บรรทัดที่เหมือนfactorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);
ไม่สามารถอ่านได้ทั้งหมด
n === 0
? Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
function computeFactorialOfN(n) {
var output=1;
for(i=1; i<=n; i++){
output*=i;
} return output;
}
computeFactorialOfN(5);