Tìm kiếm cách triển khai thực sự nhanh chóng của hàm giai thừa trong JavaScript. Bất kỳ gợi ý?
Tìm kiếm cách triển khai thực sự nhanh chóng của hàm giai thừa trong JavaScript. Bất kỳ gợi ý?
Câu trả lời:
Bạn có thể tìm kiếm (1 ... 100)! trên Wolfram | Alpha để tính toán trước chuỗi giai thừa.
100 số đầu tiên là:
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
Nếu bạn vẫn muốn tự mình tính toán các giá trị, bạn có thể sử dụng 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;
}
Tôi nghĩ sẽ hữu ích nếu thêm một ví dụ hoạt động của hàm giai thừa lặp đi lặp lại lười biếng sử dụng các số lớn để có kết quả chính xác với ghi nhớ và bộ nhớ cache để so sánh
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);
Tôi giả sử bạn sẽ sử dụng một số loại bao đóng để hạn chế khả năng hiển thị tên biến.
function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }
Cũng xem câu trả lời của tôi sử dụng nội trang mới BigInt
hơn thay vì thư viện của bên thứ ba.
Bạn nên sử dụng một vòng lặp.
Đây là hai phiên bản được chuẩn hóa bằng cách tính giai thừa của 100 cho 10.000 lần.
Đệ quy
function rFact(num)
{
if (num === 0)
{ return 1; }
else
{ return num * rFact( num - 1 ); }
}
Lặp đi lặp lại
function sFact(num)
{
var rval=1;
for (var i = 2; i <= num; i++)
rval = rval * i;
return rval;
}
Trực tiếp tại: http://jsfiddle.net/xMpTv/
Kết quả của tôi cho thấy:
- Đệ quy ~ 150 mili giây
- Lặp lại ~ 5 mili giây ..
rval = rval * i;
bạn có thể viếtrval *= i;
Tôi vẫn nghĩ câu trả lời của Margus là câu trả lời hay nhất. Tuy nhiên, nếu bạn muốn tính theo giai thừa của các số trong phạm vi từ 0 đến 1 (tức là hàm gamma), thì bạn không thể sử dụng cách tiếp cận đó vì bảng tra cứu sẽ phải chứa các giá trị vô hạn.
Tuy nhiên, bạn có thể tính gần đúng giá trị của các giai thừa và nó khá nhanh, nhanh hơn so với gọi đệ quy chính nó hoặc lặp lại nó ít nhất (đặc biệt là khi các giá trị bắt đầu lớn hơn).
Một phương pháp xấp xỉ tốt là một của Lanczos
Đây là một triển khai trong JavaScript (được chuyển từ máy tính tôi đã viết vài tháng trước):
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;
}
Giờ đây, bạn có thể làm những thứ thú vị như factorial(0.41)
, v.v. tuy nhiên, độ chính xác có thể hơi sai lệch, xét cho cùng, đó là kết quả gần đúng.
var d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;
. Điều này cho phép bạn tính các giai thừa lên đến 169! thay vì hiện tại chỉ có 140 !. Đây là khá gần với giai thừa có thể biểu diễn tối đa bằng cách sử dụng Number
kiểu dữ liệu, là 170 !.
Bảng tra cứu là cách rõ ràng để thực hiện, nếu bạn đang làm việc với các số tự nhiên. Để tính toán bất kỳ giai thừa nào trong thời gian thực, bạn có thể tăng tốc độ bằng bộ nhớ cache, lưu các số bạn đã tính toán trước đó. Cái gì đó như:
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;
})();
Bạn có thể tính toán trước một số giá trị để tăng tốc hơn nữa.
Đây là giải pháp của tôi:
function fac(n){
return(n<2)?1:fac(n-1)*n;
}
Đó là cách đơn giản nhất (ít ký tự / dòng) mà tôi đã tìm thấy, chỉ một hàm với một dòng mã.
Chỉnh sửa:
Nếu bạn thực sự muốn lưu một số ký tự, bạn có thể sử dụng Hàm mũi tên (21 byte) :
f=n=>(n<2)?1:f(n-1)*n
f=n=>n?f(n-1)*n:1
...
hàm đệ quy ngắn và dễ dàng (bạn cũng có thể làm điều đó với một vòng lặp, nhưng tôi không nghĩ rằng điều đó sẽ tạo ra bất kỳ sự khác biệt nào về hiệu suất):
function factorial (n){
if (n==0 || n==1){
return 1;
}
return factorial(n-1)*n;
}
đối với một n rất lớn, bạn có thể sử dụng xấp xỉ stirlings - nhưng điều đó sẽ chỉ cung cấp cho bạn một giá trị gần đúng.
BIÊN TẬP: một nhận xét về lý do tại sao tôi nhận được một phản đối cho điều này sẽ rất tuyệt ...
EDIT2: đây sẽ là kỹ thuật sử dụng một vòng lặp (sẽ là lựa chọn tốt hơn):
function factorial (n){
j = 1;
for(i=1;i<=n;i++){
j = j*i;
}
return j;
}
Tôi nghĩ rằng giải pháp tốt nhất là nên sử dụng các giá trị lưu trữ, như Margus đề cập và sử dụng Stirlings xấp xỉ cho các giá trị lớn hơn (giả định bạn phải thực sự nhanh chóng và không phải là chính xác về số lượng lớn như vậy).
Kìa, trình ghi nhớ, nhận bất kỳ hàm đối số đơn nào và ghi nhớ nó. Hóa ra là nhanh hơn một chút so với giải pháp của @ xPheRe , bao gồm cả giới hạn về kích thước bộ nhớ cache và kiểm tra liên quan, vì tôi sử dụng shortcuiting, v.v.
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;
}());
Trên máy của tôi trong Chrome nhanh hơn khoảng 25 lần so với phiên bản đệ quy và nhanh hơn 10% so với xPheRe.
Tôi nghĩ rằng phiên bản dựa trên vòng lặp này có thể là hàm giai thừa nhanh nhất.
function factorial(n, r = 1) {
while (n > 0) r *= n--;
return r;
}
// Default parameters `r = 1`,
// was introduced in ES6
Và đây là lý do của tôi:
for
các vòng lặp và while
vòng lặp có hiệu suất tương tự nhau, một for
vòng lặp không có biểu thức khởi tạo và biểu thức cuối cùng trông kỳ lạ; có lẽ tốt hơn nên viết for(; n > 0;)
nhưwhile(n > 0)
n
và r
được sử dụng, vì vậy về lý thuyết ít tham số hơn có nghĩa là dành ít thời gian hơn cho việc phân bổ bộ nhớn
có bằng không - tôi đã nghe lý thuyết rằng máy tính kiểm tra các số nhị phân (0 và 1) tốt hơn chúng kiểm tra các số nguyên khácTôi đã xem qua bài đăng này. Lấy cảm hứng từ tất cả các đóng góp ở đây, tôi đã nghĩ ra phiên bản của riêng mình, có hai tính năng mà tôi chưa từng thảo luận trước đây: 1) Kiểm tra để đảm bảo đối số là số nguyên không âm 2) Tạo một đơn vị khỏi bộ nhớ cache và chức năng biến nó thành một bit mã độc lập. Để cho vui, tôi đã cố gắng làm cho nó nhỏ gọn nhất có thể. Một số có thể thấy rằng nó thanh lịch, những người khác có thể nghĩ rằng nó tối nghĩa. Dù sao, đây là:
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];
Bạn có thể điền trước bộ nhớ cache hoặc cho phép nó được lấp đầy khi các cuộc gọi diễn ra. Nhưng phần tử ban đầu (thực tế là (0) phải có mặt nếu không nó sẽ bị hỏng.
Thưởng thức :)
Nó rất đơn giản bằng cách sử dụng ES6
const factorial = n => n ? (n * factorial(n-1)) : 1;
Xem ví dụ ở đây
Đây là một giải pháp:
function factorial(number) {
total = 1
while (number > 0) {
total *= number
number = number - 1
}
return total
}
Sử dụng ES6, bạn có thể đạt được nó cả nhanh và ngắn:
const factorial = n => [...Array(n + 1).keys()].slice(1).reduce((acc, cur) => acc * cur, 1)
Mã để tính giai thừa phụ thuộc vào yêu cầu của bạn.
Về điểm 1 và 4, thường hữu ích hơn nếu có một chức năng đánh giá nhật ký của giai thừa trực tiếp hơn là có một chức năng để đánh giá chính giai thừa.
Đây là một bài đăng trên blog thảo luận về những vấn đề này. Đây là một số mã C # để tính toán giai thừa nhật ký mà sẽ rất nhỏ để chuyển sang JavaScript. Nhưng nó có thể không tốt nhất cho nhu cầu của bạn tùy thuộc vào câu trả lời của bạn cho các câu hỏi trên.
Đây là phiên bản dựa trên vòng lặp nhỏ gọn
function factorial( _n )
{
var _p = 1 ;
while( _n > 0 ) { _p *= _n-- ; }
return _p ;
}
Hoặc bạn có thể ghi đè đối tượng Math (phiên bản đệ quy):
Math.factorial = function( _x ) { return _x <= 1 ? 1 : _x * Math.factorial( --_x ) ; }
Hoặc tham gia cả hai cách tiếp cận ...
Khai thác thực tế rằng Number.MAX_VALUE < 171!
, chúng ta có thể chỉ cần sử dụng một bảng tra cứu hoàn chỉnh chỉ bao gồm 171 phần tử mảng nhỏ gọn chiếm ít hơn 1,4 kilobyte bộ nhớ.
Một hàm tra cứu nhanh với độ phức tạp thời gian chạy O (1) và tổng chi phí truy cập mảng tối thiểu khi đó sẽ trông như sau:
// 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
Điều này chính xác và nhanh nhất khi sử dụng Number
kiểu dữ liệu. Tính toán bảng tra cứu trong Javascript - như một số câu trả lời khác đề xuất - sẽ làm giảm độ chính xác khi n! > Number.MAX_SAFE_INTEGER
.
Nén bảng thời gian chạy qua gzip làm giảm kích thước của nó trên đĩa từ khoảng 3,6 xuống 1,8 kilobyte.
Câu trả lời một dòng:
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
để an toànGiải pháp sử dụng
BigInt
, một tính năng của ES 2018 + / 2019.
Đây là ví dụ hoạt động được sử dụng BigInt
, vì nhiều câu trả lời ở đây đều thoát khỏi ranh giới an toàn của Number
(MDN) gần như ngay lập tức. Nó không phải là nhanh nhất nhưng nó đơn giản và do đó rõ ràng hơn để điều chỉnh các tối ưu hóa khác (như bộ nhớ cache của 100 số đầu tiên).
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
cuối một ký tự số như 1303n
cho biết đó là một BigInt
loại.BigInt
với Number
trừ khi bạn ép buộc chúng một cách rõ ràng và làm như vậy có thể làm mất độ chính xác.Sử dụng các tính năng của ES6, có thể viết mã trên MỘT dòng & không cần đệ quy :
var factorial=(n)=>Array.from({length: n},(v, k) => k+1).reduce((a, b) => a*b, 1)
Chỉ để hoàn thiện, đây là một phiên bản đệ quy sẽ cho phép tối ưu hóa cuộc gọi đuôi. Tuy nhiên, tôi không chắc liệu tối ưu hóa cuộc gọi đuôi có được thực hiện trong JavaScript hay không ..
function rFact(n, acc)
{
if (n == 0 || n == 1) return acc;
else return rFact(n-1, acc*n);
}
Để gọi nó:
rFact(x, 1);
Đây là một giải pháp lặp lại sử dụng ít không gian ngăn xếp hơn và lưu các giá trị đã tính toán trước đó theo cách tự ghi nhớ:
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
Cũng lưu ý rằng tôi đang thêm cái này vào đối tượng Math, nó là một đối tượng theo nghĩa đen nên không có nguyên mẫu. Thay vì chỉ ràng buộc chúng với hàm một cách trực tiếp.
Math.factorial(100); Math.factorial(500);
sẽ tính toán 1..100 phép nhân hai lần.
Tôi tin rằng phần sau là đoạn mã bền vững và hiệu quả nhất từ các nhận xét ở trên. Bạn có thể sử dụng nó trong kiến trúc js ứng dụng toàn cầu của mình ... và đừng lo lắng về việc viết nó trong nhiều không gian tên (vì nó là một nhiệm vụ có thể không cần bổ sung nhiều). Tôi đã bao gồm 2 tên phương thức (dựa trên tùy chọn) nhưng cả hai đều có thể được sử dụng vì chúng chỉ là tham chiếu.
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
thay cho chiều ngược lại, bạn mất lên đến 4 chữ số trong độ chính xác cho 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
Điều này thực hiện lưu trữ nhanh 100 giá trị đầu tiên và không đưa một biến bên ngoài vào phạm vi cho bộ nhớ cache, lưu trữ các giá trị dưới dạng thuộc tính của chính đối tượng hàm, có nghĩa là nếu bạn biết factorial(n)
đã được tính toán, bạn có thể chỉ đơn giản là gọi nó là factorial[n]
, hiệu quả hơn một chút. Việc chạy 100 giá trị đầu tiên này sẽ mất thời gian dưới mili giây trong các trình duyệt hiện đại.
21! > Number.MAX_SAFE_INTEGER
, do đó, không thể được biểu diễn một cách an toàn dưới dạng một phao 64-bit.
Đây là một triển khai tính toán cả thừa số dương và âm. Nó nhanh chóng và đơn giản.
var factorial = function(n) {
return n > 1
? n * factorial(n - 1)
: n < 0
? n * factorial(n + 1)
: 1;
}
Đây là số do tôi tự làm, không sử dụng các số trên 170 hoặc dưới 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
và thực hiện quá nhiều Number
chuyển đổi và đưa ra kết quả không chính xác cho 0! (như bạn đã nêu, nhưng tại sao?).
Đây là mã của tôi
function factorial(num){
var result = num;
for(i=num;i>=2;i--){
result = result * (i-1);
}
return result;
}
factorial(0)
. Ngoài ra, bằng cách bắt đầu phép nhân của bạn với n * (n-1) * (n-2) * ... * 1 thay vì làm theo cách khác, bạn có độ chính xác lên đến 4 chữ số cho n >> 20. @prime: 170! > Number.MAX_VALUE
và được thể hiện tốt nhất với Infinity
.
function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n)
}
Được cung cấp bởi http://javascript.info/tutorial/number-math như một cách đơn giản để đánh giá xem một đối tượng có phải là một số nguyên thích hợp để tính toán hay không.
var factorials=[[1,2,6],3];
Một tập hợp các thừa số được ghi nhớ đơn giản yêu cầu các phép tính dư thừa, có thể được xử lý bằng "nhân với 1" hoặc là một chữ số là một phương trình đơn giản không đáng để xử lý trực tiếp.
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;
});
Sau khi xem xét đầu vào từ các thành viên khác (không bao gồm lời khuyên Log, mặc dù tôi có thể thực hiện điều đó sau này), tôi đã tiếp tục và tập hợp một tập lệnh khá đơn giản. Tôi bắt đầu với một ví dụ OOP JavaScript đơn giản chưa được đào tạo và xây dựng một lớp nhỏ để xử lý các giai thừa. Sau đó, tôi đã triển khai phiên bản Ghi nhớ được đề xuất ở trên. Tôi cũng đã triển khai Giai thừa viết tắt tuy nhiên tôi đã thực hiện một điều chỉnh lỗi nhỏ; Tôi đã thay đổi "n <2" thành "n <3". "n <2" sẽ vẫn xử lý n = 2, điều này sẽ lãng phí, bởi vì bạn sẽ lặp lại cho một 2 * 1 = 2; đây là một sự lãng phí theo quan điểm của tôi. Tôi đã thay đổi nó thành "n <3"; bởi vì nếu n là 1 hoặc 2 nó sẽ trả về n đơn giản, nếu nó là 3 hoặc nhiều hơn nó sẽ đánh giá bình thường. Tất nhiên khi các quy tắc áp dụng, tôi đã đặt các hàm của mình theo thứ tự giảm dần của việc thực thi giả định. Tôi đã thêm vào tùy chọn bool (true | false) để cho phép thay đổi nhanh chóng giữa thực thi đã ghi nhớ và thực thi bình thường (Bạn không bao giờ biết khi nào bạn muốn hoán đổi trên trang của mình mà không cần thay đổi "kiểu") Như tôi đã nói trước đây biến thừa số được ghi nhớ được đặt với 3 vị trí bắt đầu, lấy 4 ký tự và giảm thiểu các phép tính lãng phí. Mọi thứ sau lần lặp thứ ba, bạn đang xử lý phép toán cộng hai chữ số. Tôi nghĩ nếu bạn có đủ một stickler về nó, bạn sẽ chạy trên một bảng giai thừa (như được triển khai). lấy 4 ký tự và giảm thiểu các phép tính lãng phí. Mọi thứ sau lần lặp thứ ba, bạn đang xử lý phép toán cộng hai chữ số. Tôi nghĩ nếu bạn có đủ một stickler về nó, bạn sẽ chạy trên một bảng giai thừa (như được triển khai). lấy 4 ký tự và giảm thiểu các phép tính lãng phí. Mọi thứ sau lần lặp thứ ba, bạn đang xử lý phép toán cộng hai chữ số. Tôi nghĩ nếu bạn có đủ một stickler về nó, bạn sẽ chạy trên một bảng giai thừa (như được triển khai).
Tôi đã lên kế hoạch gì sau đây? lưu trữ phiên cục bộ & | để cho phép lưu trữ từng trường hợp trong bộ nhớ cache của các lần lặp cần thiết, về cơ bản xử lý vấn đề "bảng" đã nói ở trên. Điều này cũng sẽ tiết kiệm hàng loạt cơ sở dữ liệu và không gian phía máy chủ. Tuy nhiên, nếu bạn sử dụng localStorage, về cơ bản, bạn sẽ hút không gian trên máy tính của người dùng chỉ để lưu trữ danh sách các số và làm cho màn hình của họ XEM nhanh hơn, tuy nhiên trong một khoảng thời gian dài với nhu cầu lớn thì điều này sẽ chậm lại. Tôi đang nghĩ rằng sessionStorage (xóa sau khi Tab rời đi) sẽ là một lộ trình tốt hơn nhiều. Có thể kết hợp điều này với một máy chủ tự cân bằng / bộ nhớ cache phụ thuộc cục bộ không? Người dùng A cần X lần lặp. Người dùng B cần lặp lại Y. X + Y / 2 = Số lượng cần thiết được lưu trong bộ nhớ cache cục bộ. Sau đó, chỉ cần phát hiện và xử lý các điểm chuẩn thời gian tải và thời gian thực thi trực tiếp cho mọi người dùng cho đến khi nó tự điều chỉnh để tối ưu hóa cho chính trang web. Cảm ơn!
Chỉnh sửa 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);
});
Chỉnh sửa này thực hiện một đề xuất Stack khác và cho phép tôi gọi hàm là giai thừa (true) (5), là một trong những mục tiêu của tôi đã đề ra. : 3 Tôi cũng đã loại bỏ một số chỉ định không cần thiết và viết tắt một số tên biến không công khai.
undefined
cho 0 !. ES6 cho phép thay thế isNumeric
bằng Number.isInteger
. Những dòng như thế factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);
là hoàn toàn không thể đọc được.
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);