LOCKYOUで学ぶ

階乗進数

戻る

めええええええええええええええちゃあああああああああああああああんんんんんんん!!!!!!
……
もっふもっふもっふもっふwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
wwwwwwwwwwwwwwwwwwwwwwwwwwwww
wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww
(大安にまさぐられる)
よし!!!!階乗進法なんてどうでもいいからめぇちゃんをただもふもふするだけのコーナーにしよう!!!
繰り返す気?
イザナミだ
うるさいわね
というわけで前回は順列の順番についてやったわけなんだけど
今回は順番から順列を出すの?
いや、その前にちょっと前回の修正
前回のプログラムになにか不備があるの?
まあ別にあれでもいいんだけどさ……
例えばこんな計算をするとき
3×4!+2×3!+1×2!+1×1!
4!は4×3!なわけだからさ、
3!を二回行っているということになる
うーん……無駄ね
というわけで、
数学 以下3つの法則は小学3年生ほどの内容です
ただし、数学の深い部分でも重要となってきます
作者にはよくわからない世界です
の勉強のおさらい
分配法則とかってあったよね?おさらいしてみると
交換法則 A×B=B×A 結合法則 (A×B)×C=A×(B×C) 分配法則 (A+B)×C=A×C+B×C
あったわね
なんだかどれも当たり前みたいな感じなんだけど
ただの数ひと 自然数同士から複素数同士までなら全て成り立ちますが
集合や行列ではA×B=B×A等は基本成り立ちません
驚きました
つ同士だけならね?
それはともかく、3×4!+2×3!をなんとかしてまとめてみよう
3×4!+2×3!ってCの部分が違うわよね
……でもさっき大安が言ってたとおり4!は4×3!だから……
3×(4×3!)+2×3!になって
結合法則だね
(3×4)×3!+2×3!だから、分配法則で
((3×4)+2)×3!
こんな感じね
正解<エサクタ>
続けて、3×4!+2×3!+1×2!をまとめてみようね
えっと?
3×4!+2×3!はさっきやったように((3×4)+2)×3!になるから
((3×4)+2)×3!+1×2!を考えると……
数字がごっちゃで分かりづらいから
((3×4)+2)の部分はAって書いたほうが良くない?
……そうね
そうするとA×3!+1×2!は
(A×3)×2!+1×2!
=((A×3)+1)×2!
最終的にAを戻して
((((3×4)+2)×3)+1)×2!
……余計な括弧があるから外したほうがいいわね
((3×4+2)×3+1)×2!
じゃあ最後
3×4!+2×3!+1×2!+1×1!をまとめて……って、まあ面倒だわね
結論を言うと
(((3×4+2)×3+1)×2+1)×1
になるよ
なんかだいぶ簡単な感じになるわね
最初の3に掛けて足して掛けて足して掛けて……
階乗進法だからわかりにくいけど、十進法で考えてみるとわかりやすくなるかも?
例えば35242(3×10^4+5×10^3+2×10^2+4×10^1+2×10^0)
だったら
((((3×10+5)×10+2)×10+4)×10+2)×1
3に 10かけて30、それに5を足すと35 10かけて350、それに2を足すと352 10かけて3520、それに4を足すと3524 10かけて35240、それに2を足すと35242
最初にやったプログラムだと4!かけて、3!かけて……ってなるけど、
この考え方なら4かけて、3かけて……ってなるから明らかに処理時間が短くなるよね
それじゃあ、前回書いたプログラムは修正したほうがいいのね?
まあ、百万回ぐらい繰り返すとかそんなレベルでようやく差が出てくるぐらいだとは思うけど
効率化はしたほうがいいね
それじゃいってみよー!
var sequenceNumber = function(value) {
  
  // 渡された順列を配列にしたもの
  var valueList = String(value).split('');
  // 順番(戻り値)
  var resultValue = 0;
  // 調べたい順列の桁数分だけ繰り返す
  for (var i = 0; i < (valueList.length - 1); i++) {
    // 調べてる桁の数字を抜き出す変数
    var nowValue = valueList[i];
    // 調べてる桁の数字より小さい数を数える変数
    var smallCount = 0;
    
    // 現在調べている桁から前の方を調べる
    for (var j = i; j >= 0; j--) {
      // 調べてる桁の数字より小さい数をカウント
      if (valueList[j] < nowValue) {
        smallCount++;
      }
    }
    
    // resultValue += (nowValue - smallCount) * fact「階乗の関数」(valueList.length - (i + 1));
    resultValue += (nowValue - smallCount);
    resultValue *= (valueList.length - (i + 1));
  }

  return resultValue;
};
階乗した数を掛けたあとに足しているところを、足して掛けてという処理に変えるだけ!
ね?簡単でしょう?

(分かりづらいけど)前回のプログラムを改良
たしかにそうだけど……
ループの条件のところも変わってない?
あ、それねぇ
前回の中盤あたりで最後の数字がどうとか言ってたよね
最後の数字については考えなくていいから、配列の個数よりも一個少なくループを回すようにしてるよ
これやらないと全部0になっちゃう
ふーん……
……前回もやったほうが良かったんじゃないの?
前回の処理なら0を足すことになるから特に問題はなかったけど、
今回は0をかけることになるからねぇ……
全部俺と作者が悪いよ
……まあいいけど
あ、そうそう
とりあえず前回と今回でプログラムの速度を測ってみたよ
まあ軽くflashで
チョチョイのチョイ 9876543210を数値そのままで入れてしまい「あれ?あれ?」ってなったのはナイショ
とね
「"9876543210"の10個の数字の順番を計算する」を1000回繰り返す
っていうのをやった結果、前者は0.5~0.6秒ぐらい、後者は0.3~0.4秒ぐらいだったよ
まあ、ずいぶん極端な測り方だと思うけど……
人によってこの秒数は変わってくると思うけど、
前者のほうが早いって言うことがわかったね
それで、次は何をやるのよ
えーっと、次回は今回とは逆、順番から順列を導き出す方法をやるよ
今回はちょっと内容薄かったと思うけどまあいいよね
というわけで、また次回!