web-dev-qa-db-ja.com

JavaScriptの最速の階乗関数とは何ですか?

JavaScriptのfactorial関数の非常に高速な実装を探しています。提案はありますか?

86
Ken

WolframAlphaで(1 ... 100)!を検索 を使用して、階乗シーケンスを事前計算できます。

最初の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

自分で値を計算したい場合は、 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;
} ​

編集:21.08.2014

解決策2

lazyiterativefactorial functionを使用してbig numbersを取得するのに役立つ例を追加すると便利だと思いましたexactmemoizationおよびcacheの比較結果

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

何らかの閉鎖 を使用して変数名の可視性を制限すると仮定します。

RefBigNumberSandboxJsFiddle

104
Margus

ループを使用する必要があります。

以下に、1万回の階乗を計算してベンチマークした2つのバージョンを示します。

再帰的

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/

私の結果は以下を示します:
-Recursive〜150ミリ秒
-Iterative〜5ミリ秒.

86

私はまだマーガスの答えが最高だと思います。ただし、0〜1の範囲内の数値の階乗(つまり、ガンマ関数)を計算する場合は、ルックアップテーブルに無限値を含める必要があるため、そのアプローチを使用できません。

ただし、can階乗の値を概算すると、かなり高速で、少なくとも自分自身を再帰的に呼び出したり、ループするよりも高速です(特に値が大きくなり始めた場合)。

良い近似方法は、ランチョスの方法です

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)などのクールな処理を実行できますが、精度は少しずれている可能性があります。結局のところ、これは結果の近似値です。

29
Waleed Amjad

ルックアップテーブルは、自然数で作業している場合の明らかな方法です。階乗をリアルタイムで計算するには、キャッシュでそれを高速化し、以前に計算した数値を保存します。何かのようなもの:

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

さらに高速化するために、いくつかの値を事前計算できます。

17
xPheRe

私の解決策は次のとおりです。

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

それは最も簡単な方法です(少ない文字/行)私が見つけたのは、1つのコード行を持つ関数のみです。


編集:
文字を保存したい場合は、 矢印関数(21バイト)を使用します。

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

短くて簡単な再帰関数(ループでも可能ですが、パフォーマンスに違いはないと思います):

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

nが非常に大きい場合、 スターリング近似 -を使用できますが、これは近似値のみを提供します。

EDIT:なぜこれに対して下票を得ているのかについてのコメントはニースだったでしょう...

EDIT2:これはループを使用した解決策になります(より良い選択です):

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

Margusが言及したように、キャッシュされた値を使用し、より大きな値に スターリング近似 を使用することが最善の解決策だと思います(あなたは本当に高速でなければならないと仮定しますそしてである必要はありません(= /// =)そのような大きな数値では正確です)。

8
oezi

注目すべきは、任意の単一引数関数を取り、それをメモ化するメモライザーです。 @xPheReの solution よりもわずかに高速であることが判明しました。これには、短絡などを使用しているため、キャッシュのサイズと関連するチェックの制限が含まれます。

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の10倍高速です。

7
Phil H

ES6でわずか1行

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


function print(value) {
  document.querySelector('.result').innerHTML = value;
}
.result {
  margin-left: 10px;
}
<input onkeyup="print(factorial(this.value))" type="number"/>

<span class="result">......</span>
6
Abdennour TOUMI

私はこの投稿に出会いました。ここでのすべての貢献に触発されて、私は自分のバージョンを思い付きました。これには、これまでに説明したことのない2つの機能があります:1)引数が負でない整数であることを確認するチェック2)キャッシュからユニットを作成し、 1つの自己完結型のコードにする関数。楽しみのために、できるだけコンパクトにしようとしました。ある人はエレガントであると感じるかもしれませんし、ある人はそれをひどく不明瞭だと思うかもしれません。とにかく、ここにあります:

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)が存在する必要があります。存在しない場合は破損します。

楽しい :)

5
Roland Bouman

ES6を使用すると、高速でも短時間でも実現できます。

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

最速の階乗関数

このループベースのバージョンは最速の階乗関数かもしれないと思います。

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

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

そして、ここに私の推論があります:

  • 再帰関数は、メモ化を使用しても、関数呼び出し(基本的にはスタックに関数をプッシュする)のオーバーヘッドがあり、ループを使用するよりもパフォーマンスが低下します。
  • forループとwhileループのパフォーマンスは似ていますが、initialization-expressionとfinal-expressionのないforループは奇妙に見えます。おそらくfor(; n > 0;)while(n > 0)と書く方が良いでしょう
  • nrの2つのパラメーターのみが使用されるため、理論的にはパラメーターが少ないほどメモリの割り当てにかかる時間が短くなります
  • nがゼロかどうかをチェックするデクリメントループを使用します-他の整数をチェックするよりもコンピューターが2進数(0と1)をチェックする方が良いという理論を聞いたことがあります
4
tfmontague

ES6を使用すると非常に簡単です

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

例を参照してください here

4
joseluiscc

1つの解決策を次に示します。

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

Number.MAX_VALUE < 171!という事実を利用して、1.4キロバイト未満のメモリを占有する171個のコンパクトな配列要素のみで構成される完全なルックアップテーブルを使用できます。

実行時の複雑さ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キロバイトに減少します。

3
le_m

1行の回答:

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
timgfx

階乗を計算するコードは、要件によって異なります。

  1. オーバーフローが心配ですか?
  2. どのような入力範囲がありますか?
  3. サイズや時間を最小限に抑えることが重要ですか?
  4. 階乗をどうしますか?

ポイント1と4については、階乗を評価する関数を持っているよりも、階乗のlogを直接評価する関数を持っている方がより便利なことがよくあります。自体。

これらの問題を説明する ブログ投稿 があります。ここにいくつかあります ログ階乗を計算するためのC#コード JavaScriptに移植するのは簡単です。ただし、上記の質問に対する回答によっては、ニーズに最適ではない場合があります。

3
John D. Cook

これはコンパクトなループベースのバージョンです

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

または、Mathオブジェクトをオーバーライドすることもできます(再帰バージョン):

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

または両方のアプローチに参加して......

3
Sandro Rosa
function isNumeric(n) {
    return !isNaN(parseFloat(n)) && isFinite(n)
}

http://javascript.info/tutorial/number-math により、オブジェクトが計算に適切な整数であるかどうかを評価する簡単な方法として提供されます。

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

冗長な計算を必要とするメモ化された階乗の単純なセットは、「1で乗算」で処理されるか、ライブで処理する価値のない単純な方程式である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の例から始め、階乗を処理するための小さなクラスを作成しました。その後、上記で提案したメモ化のバージョンを実装しました。速記の因数分解も実装しましたが、少し誤差を調整しました。 「n <2」を「n <3」に変更しました。 「n <2」は、n = 2を処理しますが、これは無駄になります。なぜなら、2 * 1 = 2を繰り返すためです。これは私の意見では無駄です。 「n <3」に変更しました。 nが1または2の場合は単純にnを返すため、3以上の場合は通常に評価されます。もちろん、ルールが適用されると、想定される実行の降順に関数を配置しました。 bool(true | false)オプションを追加して、メモした実行と通常の実行をすばやく変更できるようにしました(「スタイル」を変更せずにページをいつ入れ替えたいかわからないだけです)メモ化された階乗変数は3つの開始位置で設定され、4文字を使用し、無駄な計算を最小限に抑えます。 3回目の反復以降はすべて、2桁の数学プラスを処理しています。私はあなたがそれについて十分なスティックラーがあなたが階乗表(実行されているように)で実行するかどうかを考えます。

この後何を計画しましたか? local&| sessionストレージ。必要な反復のケースごとのキャッシュを可能にし、本質的に上記の「テーブル」の問題を処理します。これにより、データベースとサーバー側のスペースも大幅に節約できます。ただし、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の提案を実装し、関数をfactorial(true)(5)として呼び出すことを可能にします。これは、私の目標設定の1つでした。 :3また、いくつかの不必要な割り当てを削除し、いくつかの非パブリック変数名を短縮しました。

上記のコメントから、以下が最も持続可能で効率的なコードだと思います。これをグローバルアプリケーションの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;
};
2
Joe Johnson

安全のためにBigIntを使用した反復階乗

ソリューションは、ES 2018 +/2019機能である BigInt を使用します。

これは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)
  • 1303nのような数値リテラルの末尾のnは、BigInt型であることを示します。
  • 明示的に強制しない限り、BigIntNumberを混在させないでください。そうすると、精度が低下する可能性があります。
2
Meow

ここに私のコードがあります

function factorial(num){
    var result = num;
    for(i=num;i>=2;i--){
        result = result * (i-1);
    }
    return result;
}
2
cse031sust02
// 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個の値を実行すると、ミリ秒未満の時間がかかります。

2
Scott Sauyet

Wolfram MathWorld によると:

階乗n正の整数nas

n!=n(n-1)...2·1.

したがって、次の方法を使用して、数値の階乗を取得できます。

const factorial = n => +!n || n * factorial(--n);

factorial(4) // 4! = 4 * 3 * 2 * 1 = 24
2
Grant Miller

これは、正と負の両方の階乗を計算する実装です。速くて簡単です。

var factorial = function(n) {
  return n > 1
    ? n * factorial(n - 1)
    : n < 0
        ? n * factorial(n + 1)
        : 1;
}
2
Ilia Bykow
function computeFactorialOfN(n) {
  var output=1;
  for(i=1; i<=n; i++){
    output*=i;
  } return output;
}
computeFactorialOfN(5);
2

ここに私が自分で作ったものがありますが、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;
}
2
TheBestGuest

新しいJavaScript関数 fillmapreduce および constructor (および太い矢印の構文)を使用するものは次のとおりです。

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

編集:n === 0を処理するために更新

2
Ashley Coolman

完全を期すために、末尾呼び出しの最適化を可能にする再帰バージョンを以下に示します。ただし、末尾呼び出しの最適化がJavaScriptで実行されるかどうかはわかりません。

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

それを呼び出すには:

rFact(x, 1);
2
Robert Jeppesen

キャッシュされたループは最速でなければなりません(少なくとも複数回呼び出された場合)

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
Сухой27

これは、より少ないスタックスペースを使用し、以前に計算された値を自己メモ方式で保存する反復ソリューションです。

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オブジェクトにこれを追加しているため、プロトタイプがないことに注意してください。これらを関数に直接バインドするだけです。

2
b-h-
 used closure  for this with the helper (getFact) , I think this approach is neat hope this helps  


    factorial of n : using closures*/

    function getFact(num) {

        if (num > 1)
            return num * getFact(num - 1);
        else
            return 1;

    }


    function makeFact(fn) {
        return function(num) {
            return fn(num);
        }


    }

   makeFact(getFact)(5) //120
1
var factorial = (function() {
    var cache = [1];
    return function(value) {
        for (var index = cache.length; index <= value; index++) {
            cache[index] = index * cache[index - 1]
        }
        return cache[value];
    }
})();

これは同じ場合に便利です:

function factorialDivision(n, d) {
    var value = 1;
    for (d++ < n) {
        value *= d;
    }
    return value;
}
1
Triqui
    function factorial(num){    
        var num=Number(num);
        if (num < 0){
            return "this is not a positive number";
        }
        else{

        for (i=2 , f=1 ; i<=num;i++){

        f=f*i;

        }

    return f;
    }
    }
    // the function assumes that a number < 0 is null and factorial of any Word is considerate as factorial of 0 //

    console.log("-5! ="+factorial(-1));
    console.log("something ="+factorial("something"));
    console.log("15! ="+factorial(15));
    console.log("20! ="+factorial(20));
1
hakim

これはnの階乗を返します

function f(n) {
    var e = n;
    if (e == 1 | e == 0) return 1;
    while (n--) {
        if (n < 1)
            break;
        e *= n;
    }
    return e
}
1
Ben Johnson mk2

最初はおそらく非常にシンプルで、おそらくこの短いコードから、ニーズに応じてより適切に設定できます。

<body>

    <button  onclick="fact()">Open the Prompt</button>
    <h2 id="output"></h2>

    <script>

    function fact(){ 
        var enter=Prompt("Enter You Factorial Number Bellow :","");
        var Num_enter=Number(enter);

        for (var i=1,FactNumber=1;i<=Num_enter;i++){

        FactNumber=FactNumber*i;
        }

        if(Num_enter){ 
           document.getElementById("output").textContent="the factorial of "+ Num_enter + " is: "+Num_enter+"!= "+ FactNumber;
        }


     }

   </script>


 </body>
1
hakim
var factorial = function() {
    var memo = [1];
    var facto = function (n) {
        var result = memo[n];
    if (typeof result !== 'number'){
        result = facto(n-1)*n;
    }
        return result;
    };
    return facto;
}();

メモ化を使用して数値の階乗を計算するには、factorial(n)を使用して呼び出します。

1
broken_arrow

これは、階乗関数を作成するために知っている最も簡単な方法です

function factorial(num) {

    var result = 1;
    for(var i = 2; i<= num; i++) {
        result *= i;
    }
    return result;
}
1
Timmy

上記のものはすべて良いですが、それらはすべて私には長すぎますので、私は自分で作りました。

function factorial(n) {              //"n" is the number used in the factorial

    var ans = 1,                     //define the base variables
        neg = false,
        n = Math.round(n);

    if (n<0 || !n) neg = true;       //check if the number is negative or if the number doesn't exist

    for (var i=1;i<=n;i++) ans *= i; //the actual repeating code, won't run if the number is less than or equal to 0

    return neg ? NaN : ans;          //return the answer if the original was positive

}

forループの動作方法は、1未満の数値に対しては自動的に何も行いません。したがって、基本的には「(数値)が0以下の場合、1を返します。

if (n<0 || !n) neg = true;行とreturn neg ? NaN : ans;行は、一緒に機能して、「数値が負の場合、NaN(Not a Number)を返します」と言います。また、これらは番号が存在するかどうかを確認し、番号が存在しない場合はNaN(Not a Number)を返します。

注意

少なくともChrome v50.0.2661.86(64ビット)では、最大で170です。したがって、170(たとえば171)よりも大きい数でこの関数を実行すると、infinityが返されます。 。

1
Jaketr00

ES6機能を使用すると、1行再帰なしでコードを記述できます:

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

階乗は、与えられた数から1までの単純な縮退乗算であるため、実際に乗算をループする方が簡単です。

Math.factorial = function(n) {

  if (n === 0||n === 1) {

    return 1;

  } else {

    for(var i = n; i > 0; --i) { //always make sure to decrement the value BEFORE it's tacked onto the original as a product
      n *= i;
    }

    return n;

  }

}
1
realkstrawn93

使用できます

function factorial(n) {
    return [...Array(n+1).keys()].slice(1).reduce( (a,b) => a * b, 1 );
}
var factorial = function(numToBeFactored)
    {
        if (numToBeFactored == 0)
                return 1;
            var numLength = 0;
            var numBeingFactored = 1;
        /*goes through the loop numToBeFactored times and each time multiplies numBeingFactored by one less than the last loop*/
            for (numLength = 0; numLength < numToBeFactored; numLength++)
            {
                numBeingFactored *= (numToBeFactored - numLength);
            }
            return numBeingFactored;
    };
1
anonymous

反復および再帰オプションの1行ソリューション。

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

const sf = n => {for (var i = n, c = 1; i > 1; i --) c *= i; return c;}
1
harry

まあ、この質問には十分な答えがありますが、階乗と逆階乗のための読みやすく、高速で短い解決策を投稿するだけです。

{
    const cache = [1, 1];
    let i = 2;

    function factorial(n) {
        if (!isFinite(n = parseInt(n)) || n < 0)
            throw new Error('argument for factorial has to be a positive finite integer but was ' + n);

        for (; i <= n; i++)
            cache[i] = cache[i - 1] * i;

        return cache[n];
    }

    function reverseFactorial(n) {
        if (!isFinite(n = parseFloat(n)) || n < 0)
            throw new Error('argument for reverseFactorial has to be a positive finite floatingpoint number but was ' + n);

        let f = 1;

        while (true)
            if (factorial(++f) >= n)
                return f - 1; // lower bound (f! which will fit in the given n, for upper bound just return f)

    }
}

reverseFactorialは、指定されたnに収まる最大のk!であるkを返します。

両方の関数は、factorialによってキャッシュを構築します。

少しテストしたい場合:

for (let i = 0; i < 10; i++) {
    let random = Math.random() * 100;
    random = factorial(random) * Math.random() * random;

    const reverse = reverseFactorial(random);
    const resultOfReverse = factorial(reverse);

    function expo(x) {
        return x.toExponential(2);
    }

    console.log('%s fits %d! which is %s (upper bound %d! is %s)', expo(random), reverse, expo(resultOfReverse), reverse + 1, expo(factorial(reverse + 1)));
}
1
Feirell

反復:Math.factorial=n=>{for(var o=n;n>1;)o*=--n;return o};

再帰的:Math.factorial=n=>n>1?n--*Math.fac(n):1;

事前計算済み:(_=>{let f=[],i=0;for(;i<171;i++)f[i]=(n=>{for(var o=n;n>1;)o*=--n;return o})(i);Math.factorial=n=>{n=Math.round(n);return n<171?f[n]:Infinity}})();

https://code.sololearn.com/Wj4rlA27C9fD 。ここにもっとソリューションを投稿するかもしれません。

1
NZ.

彼女はIIFy関数と再帰を使用した私のソリューションです。

console.log((function factorial(n){return (n>1)?n*factorial(n-1):1;})(10))

これは、1行のコードで階乗の出力を取得するための最適なソリューションです。

0
Mayank Tiwari

古い質問ですが、このアプローチは非常に読みやすく簡単です

function factorialize(num) {
  var arr = [];
  var result;
  if ( num === 0 || num === 1 ) {
    return 1;
  } else {
    for (var i = num; i > 0; i--) {
      arr.Push(i);
      result = arr.reduce(function(previousVal, nextVal){
                return previousVal * nextVal;
              });
    }
    return result;
  }
}
0
pSkarl

これはまだ提供されていないアプローチです。 BigInt とメモ化を使用すると、正確な結果を取得し、すでに計算されている値の計算をスキップできます。

// using let and const, block scope can be used instead of IIFE for closure
{
  const lut = [1n, 1n];
  
  // returns factorial as BigInt instead of Number
  function factorial (n) {
    if (n >= lut.length) {
      for (let i = lut.length; i <= n; i++) {
        lut.Push(BigInt(i) * lut[i - 1]);
      }
    }

    return lut[n];
  }
}

console.log('starting');
// first time will require computation
console.log(factorial(10000).toString());
// second time will return result from cache
console.log(factorial(10000).toString());
div.as-console-wrapper { overflow-x: scroll; }
0
Patrick Roberts