K!が2で割れる回数=K-popcount(K)であること

$${K!}$$が2で割れる回数を$${f(K)}$$とすると、$${f(K)=K-\text{popcount}(K)}$$ となることの証明。ARC156のD問題で使って、忘れそうなので備忘メモ。

まず、$${K!}$$が素数$${p}$$で割れる回数$${f(K)}$$は中学受験の頻出問題で、$${K}$$を$${p}$$で割った商、$${K}$$を$${p^2}$$で割った商、…を全てたせばよい。これは、$${1}$$から$${K}$$まで全て並べて、$${p}$$の倍数をチェックし、$${p^2}$$の倍数をチェックし、$${p^3}$$の倍数をチェックし・・・という作業をするとわかる。

例:K=10, p=2
1 x 2 x 3 x 4 x 5 x 6 x 7 x 8 x 9 x 10
--------------------------------------
    1       1       1       1        1  <-- 2で割った商=5
            1               1           <-- 4で割った商=2
                            1           <-- 8で割った商=1
--------------------------------------
    1回     2回     1回     3回     1回

これを2進数で考えると、$${K}$$を$${2}$$で割った商というのは、2進数で1回右シフト、$${2^2}$$で割った商は2進数で2回右シフト、・・・なので、0になるまでシフトしながらたしていけば、$${f(K)}$$を算出できる。例えば$${K=10}$$の場合、

10   = 1010

10/2 =  101 = 5
10/4 =   10 = 2
10/8 =    1 = 1
---------------
              8

さて、この表をじっくり眺めると、$${f(K)+K}$$は次のように変形できる。

1010     1111
 101      000
  10  =    11
+  1        0
----     ----

一番上に$${K}$$を追加して並べると、斜めに1が並んでいる。つまり$${K}$$の$${b}$$桁目の1は、右シフトしながら$${b}$$回出現するので、それらをまとめて並べ替えれば、$${11…11(b個) = 2^b-1}$$を、1が出現する回数分だけたせばよいことがわかる。ところで、$${\sum 2^b}$$は、$${K}$$のbitで1が立っているところを渡りながら$${b-1}$$番目のbitを$${b}$$番目にしてたしているだけなので、これは$${2K}$$であり、$${-1}$$は$${K}$$のbitが立っている回数分だけ出現するので、結局この和は$${2K-p(K)}$$となる。(但し、$${K}$$のpopcountを$${p(K)}$$とした。)

1111 = 2^4 - 1 = 10000 - 1
 000 =   0     =  0000
  11 = 2^2 - 1 =   100 - 1
+  0 =   0     =    00
-----------------------------
               = 10100 - p(K)
               =   2*K - p(K)

さて、もともとこの和は$${f(K)+K}$$だったので、$${f(K)+K=2K-p(K)}$$、つまり、$${f(K)=K-p(K)}$$であることがわかった。

なお、$${K}$$を右シフトしながらたす作業と、popcount $${p(K)}$$を求める計算とどちらが速いかは微妙なところなんじゃないか。シフト和はint32なら30回程度だし、popcountは高速アルゴリズムでも5~10回くらいは計算する。但し、popcountがハードウェアで実装されている場合はかなり速いらしい。

いいなと思ったら応援しよう!