1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

階乗の末尾に 0 はいくつ付く?  正規表現 vs 5の因子

Posted at

Paizaで階乗の末尾の 0 を数える問題に挑戦したら、まさかの“桁数オーバー事件”!

でも、冷静に考えたら意外とシンプルなルールに気づいて解決!


問題概要

  • 整数 N が与えられる。
  • N の階乗 N! の末尾に 0 がいくつ付くか求め、出力。

入力例:

100

出力例:

24




❌ NG例

const rl = require('readline').createInterface({ input: process.stdin });

rl.once('line', (input) => {
  const N = Number(input);

  // 階乗を計算(前回のコード)
  let factorial = 1;
  for (let i = 1; i <= N; i++) {
    factorial *= i;
  }


  // 文字列化して、末尾の0を正規表現で取得
  const zeroMatch = String(factorial).match(/0+$/);

  // nullの場合は末尾に0がないから、カウントは0
  const zeroCount = zeroMatch ? zeroMatch[0].length : 0;

  console.log(zeroCount);
  rl.close();
});
  • 末尾の連続する 0 を /0+$/ で取得
  • .match() は、見つからないと null になるから、
    zeroMatch ?zeroMatch[0].length : 0 で安全に出力

⚠️ 問題点 :N! がとても大きくなる

例えば N=100 だと、
100! は 158桁もある巨大な整数になる。

JavaScript の Number型 は 約 16 桁までしか正確に表せない。
(64ビット浮動小数点数のため)


🤔 末尾0の仕組みを考えてみる

→末尾の 0 は「10 の倍数」がいくつ含まれているかで決まる!

末尾に0がつくのは、数字が10の倍数になっているときだよね。
10 = 2 × 5 だから、10を作るために必要な因数「2」と「5」を数えればいい!


でも、N! には 2 の因子がものすごく多いのに対して、5 の因子はずっと少ない。

例: 10! :

10! = 1×2×3×…×10
5 は 5,10 など
2 は 2,4,6,8,10 などからたくさん出てくる



🔥 「5の倍数」で末尾0をカウント!

具体的には、N! に含まれる 5 の因子の数を数えるだけでOK!

でも注意!5の倍数だけじゃなくて、25(=5²)、125(=5³)…と、5の累乗が出てくる数も追加でカウントする必要がある。

例えば、N=100のときの5の因子の数を数える流れはこう。

1️⃣ まず「5の倍数の個数」を数える。
  100/5 = 20(20個ある!)

2️⃣ 次に「25の倍数の個数」も考慮。
  100/25 = 4(25は5×5だから、さらに1つ5が含まれる)

3️⃣ 125は100以下にないから、終了。

合計すると、20+4=24 で、末尾の0は24個になる。

最初に階乗を計算しなくても、こうやって5の因子の個数を足すだけで解決!


🔎 なぜ 25(5²)で割れるものも考慮するのか?

末尾ゼロを生む条件は「10 = 2×5」なので、N! に含まれる「5の数」を数えるんだけど…
N! には 5 の倍数だけでなく、25(=5²)、125(=5³) で割れる数 もある。

たとえば 25! には:

5 の倍数が 5, 10, 15, 20, 25 → 5の倍数は5回

25 は 5² なので、さらに1回カウントされる(5²は5が2回含まれるから)




✅OK例

そんな気づきから、最終的に書いて正解したコードがこちら。

const rl = require('readline').createInterface({input: process.stdin});

rl.once('line', (input) => {
  const N = Number(input);
  let count = 0;

  for (let i = 5; i <= N; i *= 5) {
    count += Math.floor(N / i);
  }

  console.log(count);
  rl.close();
});
  • 末尾の 0 は「10 の倍数」がいくつ含まれているかで決まる。
  • 10 = 2 × 5 なので、N! に含まれる 5 の数 に注目すればいい。
  • 5, 25, 125… の倍数ごとに加算

📝 気づきメモ

  • 末尾0の数は「10の因子の数」だけど、2は多すぎるから「5の因子数」で決まる。
  • 5, 25, 125…の累乗を考慮するのがポイント!
  • JSの浮動小数点数の限界を意識して、計算の方針を変える必要がある。

🎯 まとめ

というわけで、Paizaの「階乗の末尾0」問題は、大きな数の階乗を直接計算しようとすると失敗するってことと、数の性質に注目するのが大事ってことを学んだ!数学チックな問題だった(´;ω;`)




僕の失敗談(´;ω;`)と解決法🐈

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?