JavaScriptの最速の階乗関数は何ですか?[閉まっている]


94

JavaScript の階乗関数の本当に速い実装を探しています。何か提案はありますか?


8
可能な引数の範囲は何ですか?
Nikita Rybak

5
階乗の事前計算とルックアップテーブルへの値の格納を検討しましたか?
Waleed Amjad、

2
そのような関数の適用は何ですか?つまり、何に使うのですか?
先のとがっ

@Nikita Rybak、たった1つのagrument(n)。if(n> 170)e = Infinity
Ken

@先のとがった、まだ別の数学計算サービス。
ケン

回答:


110

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

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

編集:21.08.2014

解決策2

比較としてメモ化キャッシュを使用して正確な結果を得るには、大きな数値を使用する遅延 反復 階乗関数の実用例を追加すると便利だと思いました

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

変数名の可視性を制限するために、ある種のクロージャーを使用すると思います。

参照BigNumber サンドボックスJsFiddle


6402373705728000を超える値は切り捨てられるため、このアプローチを使用する場合は、前述の表を使用する前に必ず指数に変換してください。
デビッドスコットカービー

1
@DavidScottKirby Javascriptは、これらの数値を最も近い64ビット浮動小数点表現に自動的に変換します。コードに完全な精度の数値がないことの真の利点は、ファイルサイズが小さくなることです。
le_m 2017年

2番目のソリューションは、サードパーティのライブラリではなく、より最近の組み込みを使用する私の回答function factorial (n) { for (var i = f.length; i <= n; i++) f.push(f[i - 1].multiply(i.toString())); return f[n]; }も参照し簡略化できますBigInt
Patrick Roberts、

95

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

100の階乗を10.000回計算してベンチマークした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/

私の結果は:
- 再帰〜150ミリ秒
- 反復〜5ミリ秒..


+1正解です。より大きな数の階乗を計算するための複数の呼び出しがある場合、メモ化は合理的かもしれませんが。
Tadeck 2012年

@Tadeck、ありがとう。確かにこの場合、メモ化は非常に役立ちます。そのため、Margusの回答が正しい回答として選択されます:)
Gabriele Petrioli

再帰の1行バージョン:function factorial(num){return(num == 1)?num:num * arguments.callee(num-1); }
jbyrd 2014年

2
@HWTech、メソッドを呼び出すことはありません。テストでは、2つのメソッドを定義する速度を比較します。実行にかかる時間ではありません。これはより良いテストです(15の階乗のみを
試行

3
rval = rval * i;あなたが書く代わりにrval *= i;
Ryan

29

私はまだマーガスの答えが一番良いと思います。ただし、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)、などのクールなことができるようになりますが、精度は少しずれている可能性があります。結局のところ、これは結果の概算です。


非常に興味深いアプローチ、ありがとうございます。
ケン

おかげで非常に多くの時間を節約できました:)
nicolaskruchten

forループの下の部分をに変更することをお勧めしvar d3d4 = Math.exp((z + 0.5) * Math.log(z + 5.5) - z - 5.5); return d1 * d2 * d3d4;ます。これにより、169までの階乗を計算できます。現在は140のみです。これは、Numberデータ型を使用して表現可能な最大階乗、つまり170!にかなり近いです。
le_m 2017年

18

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

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

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


3
私はこの回答に基づいて、任意の関数の自動メモライザを作成しました(これも少し高速です:))。これには、キャッシュサイズの制限も含まれます。stackoverflow.com/a/10031674/36537
Phil H

16

これが私の解決策です:

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

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


編集:
本当にいくつかの文字を保存したい場合は、矢印関数 (21バイト)を使用できます

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

7
さらに節約するf=n=>n?f(n-1)*n:1...
le_m

残念ながら、見た目がよく、形式が短い場合でも、これが最も遅い方法です。
Zibri、

11

ES6での1行

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


factorial = n => n <= 1 ? 1 : factorial(n - 1) * n
ナラムシム2018年

10

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

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

非常に大きなnの場合、スターリング近似を使用できますこれでは近似値しか得られません。

編集:なぜこれに対する反対票を獲得しているのかについてのコメントはいいでしょう...

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

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

マーガスが述べたように、キャッシュされた値を使用して、より大きな値にスターリング近似を使用するのが最善の解決策だと思います(非常に高速である必要があり、そのような大きな数値正確である必要はないと想定しています)。


3
末尾呼び出しの最適化を行わない言語(つまり、最も広く使用されている言語)では、非再帰的な実装を使用する方が簡単ですが、その方法は簡単ですが、paulbarry.com
Daniel Earwicker

それが実装されていれば、TCOを使用することさえないので、それは確かに確かに最速ではありません。しかし、それは単純であり、私はそれを反対票を投じません。確かに最速ではありません。
ヘイレム

再帰呼び出しが末尾の位置にないため、この関数では末尾呼び出しの最適化は不可能です。
フレッド・フー

3
@Josh、(反対投票ではなく)最速のループはかなりのマージンによるループです..
Gabriele Petrioli

7

見なさい、単一引数関数を取り、それをメモするメモライザー。@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の約10%高速です。


5

この投稿を見つけました。ここでのすべての貢献に触発されて、私は自分のバージョンを思いつきました。これには、これまで説明したことのない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

最速の階乗関数

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

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)
  • 2つのパラメータのみnr使用されているが、その理論少ないパラメータにメモリを割り当てる手段より少ない時間を費やし
  • nゼロかどうかをチェックするデクリメントループを使用します-コンピューターは他の整数をチェックするよりも2進数(0と1)をチェックする方が優れているという説を聞きました


4

ここに1つの解決策があります:

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

4

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

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

3

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

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

ポイント1と4に関しては、階乗自体を評価する関数を使用するよりも、階乗の対数を直接評価する関数を使用する方が便利な場合があります。

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


番号付きリストはおそらくコメントにあるはずです。残っているのは2つのリンクだけです。リンクのみの回答はお勧めしません。
バレット

3

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

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

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


1
上記のコード内で修正しました。ありがとうございました!
Sandro Rosa

3

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

一行の答え:

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

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タイプであることを示します。
  • 明示的に強制BigIntNumberない限り、これらを混用しないでください。混合すると、精度が低下する可能性があります。

3

ES6の機能を使用して、上のコードを書くことができONEライン再帰なし

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


2

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

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

それを呼び出すには:

rFact(x, 1);

ES6はTCOをサポートしていますが、この機能はどの主要エンジンでもデフォルトではまだアクティブではありません
le_m

2

これは、使用するスタックスペースが少なく、以前に計算された値を自己記憶的に保存する反復ソリューションです。

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オブジェクトに追加しているため、プロトタイプはありません。むしろ、これらを関数に直接バインドするだけです。


これは、副問題のメモ化を完全に活用していません。たとえば、Math.factorial(100); Math.factorial(500);1..100の乗算を2回計算します。
バレット2015

2

以下は、上記のコメントから最も持続可能で効率的なコードです。これをグローバルアプリケーションの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代わりに、他の方法でラウンドの、あなたはnの精度>> 20で4桁まで失う
le_m

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

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

これにより、最初の100個の値がその場でキャッシュされ、外部変数がキャッシュのスコープに導入されず、関数オブジェクト自体のプロパティとして値が保存されます。つまり、factorial(n)すでに計算されていることがわかっている場合は、単にと呼ぶだけでfactorial[n]、少し効率的です。これらの最初の100個の値を実行すると、最新のブラウザーではミリ秒未満の時間がかかります。


21の後でそれを理解しました。数値は信頼できません。
AutoSponge 2012年

@AutoSponge 21! > Number.MAX_SAFE_INTEGERは、64ビットfloatとして安全に表現できないためです。
le_m 2017年


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

乗算をn *(n-1)*(n-2)* ... * 1で開始することにより、逆の代わりにn >> 20の精度が最大4桁失われます。また、不要なグローバル変数でiあり、Number変換が多すぎるため、0の結果が不正確になります。(あなたが述べたように、しかしなぜですか?)
le_m 2017年

2

これが私のコードです

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

1
if(n> 170)e = Infinity。そして、あなたのコードは膨大な数を生成します。オーバーフローはありませんか?
プライム

の不正な結果factorial(0)。また、乗算をn *(n-1)*(n-2)* ... * 1で開始することにより、逆のラウンドではなく、n >> 20の精度が4桁まで失われます。@prime:170! > Number.MAX_VALUEで最もよく表されInfinityます。
le_m 2017年

2

キャッシュされたループは最速である必要があります(少なくとも複数回呼び出された場合)

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

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

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

オブジェクトが計算に適切な整数かどうかを評価する簡単な方法として、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桁の数学プラスを処理します。私はあなたがそれについて十分なステッカーがどこに階乗表で実行されるかを実装していると考えています(実装されています)。4文字を使用し、無駄な計算を最小限に抑えます。3回目の反復を過ぎると、2桁の数学プラスを処理します。私はあなたがそれについて十分なステッカーがどこに階乗表で実行されるかを実装していると考えています(実装されています)。4文字を使用し、無駄な計算を最小限に抑えます。3回目の反復を過ぎると、2桁の数学プラスを処理します。私はあなたがそれについて十分なステッカーがどこに階乗表で実行されるかを実装していると考えています(実装されています)。

この後、私は何を計画しましたか?必要な反復のケースバイケースのキャッシュを可能にするローカル&|セッションストレージ。基本的に、上記の「テーブル」の問題を処理します。これにより、データベースとサーバー側のスペースも大幅に節約されます。ただし、localStorageを使用する場合、基本的にはユーザーのコンピューターのスペースを消費し、単に数値のリストを保存して画面の表示を高速化しますが、非常に長い時間をかけてこれを行うと時間がかかります。sessionStorage(タブが去った後にクリアする)の方がずっと良いルートだと思います。おそらくこれを自己分散サーバー/ローカル依存キャッシュと組み合わせますか?ユーザー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);
});

この編集により、別のスタックの提案が実装され、関数をfactorial(true)(5)として呼び出すことができます。これは、私の目標の1つでした。:3また、不要な割り当てをいくつか削除し、いくつかの非パブリック変数名を簡略化しました。


undefined0!を返します。ES6は置き換えることができますisNumericNumber.isInteger。のような行factorials[0][factorials[1]]=factorials[0][factorial_index]*(factorials[1]+1);は完全に読めません。
le_m 2017年

2

これは、新しいjavascript関数であるfillmapreduce、およびコンストラクタ (および太矢印の構文)を使用したものです。

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

編集:n === 0を処理するように更新


2
これは、ひどく醜く読めないコード行です。
jungledev

1
それは素晴らしいアイデアです。長さを2回トラバースするのではなく、すべてのロジックをreduce関数に変換し、その初期値を使用してエッジケースを処理しn === 0ませんか? Math.factorial = n => Array.from({ length: n }).reduce((product, _, i) => product * (i + 1), 1)
AlexSashaRegan 2017

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

2
StackOverflowへようこそ。ご協力いただきありがとうございます。あなたはいくつかの説明を追加することであなたの答えをさらに良くしたいと思うかもしれません。
エリアスMP
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.