トップページに戻る    次のC#のサンプルへ    前のC#のサンプルへ

Problem193 平方因子を含まない数

問題

正の整数 n が, 任意の素数の2乗によって割り切れないとき,
n を"平方因子を含まない"(squarefree)と呼ぶ.
1, 2, 3, 5, 6, 7, 10, 11 は平方因子を含まないが, 4, 8, 9, 12 は含む.

2の50乗未満で平方因子を含まない数はいくつあるか?


ソース

using System;
using System.Collections.Generic;

class Program
{
    static long[] mHeihouSuuArr;

    //エラトステネスの篩をしてから、素数の平方の配列を作成
    static void DeriveHeihouSuuArr()
    {
        var IsSosuuArr = new System.Collections.BitArray((int)mJyousuu25 + 1);

        for (int I = 2; I <= IsSosuuArr.Count - 1; I++) {
            IsSosuuArr[I] = true;
        }
        for (int I = 2; I * I <= IsSosuuArr.Count - 1; I++) {
            if (I != 2 && I % 2 == 0) continue;

            if (IsSosuuArr[I]) {
                for (int J = I * 2; J <= IsSosuuArr.Count - 1; J += I) {
                    IsSosuuArr[J] = false;
                }
            }
        }
        var HeihouSuuList = new List<long>();
        for (int I = 2; I <= IsSosuuArr.Count - 1; I++)
            if (IsSosuuArr[I]) HeihouSuuList.Add((long)I * I);

        mHeihouSuuArr = HeihouSuuList.ToArray();
    }

    static long mJyousuu25 = 1;
    static long mJyousuu50 = 1;

    static void Main()
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();

        for (int I = 1; I <= 25; I++) mJyousuu25 *= 2;
        for (int I = 1; I <= 50; I++) mJyousuu50 *= 2;
        Console.WriteLine("2の25乗={0}", mJyousuu25);
        Console.WriteLine("2の50乗={0}", mJyousuu50);

        DeriveHeihouSuuArr();

        Console.WriteLine("Answer={0}。経過時間={1}", Solve(mJyousuu50), sw.Elapsed);
    }

    //指定した上限未満の平方因子を含まない数の個数を返す
    static long Solve(long pJyougen)
    {
        //2*2の倍数、または、3*3の倍数、または、5*5の倍数、または、7*7の倍数・・・
        //の集合の要素数を、包除原理で求める
        long WaSyuugouCnt = 0;
        for (int I = 0; I <= mHeihouSuuArr.GetUpperBound(0); I++) {
            int SelectCnt = I + 1;
            bool HasYouso = false;

            foreach (long EachProd in DeriveProdEnum(SelectCnt)) {
                HasYouso = true;
                long CurrCnt = (pJyougen - 1) / EachProd;

                //奇数なら加算し、偶数なら減算
                if (SelectCnt % 2 == 1)
                    WaSyuugouCnt += CurrCnt;
                else WaSyuugouCnt -= CurrCnt;
            }
            if (HasYouso == false) break;
        }
        return pJyougen - 1 - WaSyuugouCnt;
    }

    struct JyoutaiDef
    {
        internal int CurrInd;
        internal int SelectedCnt;
        internal long ProdVal;
    }

    //引数の個数を選んだ素数の平方の積の列挙を返す
    static IEnumerable<long> DeriveProdEnum(int pNeedCnt)
    {
        var Stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        WillPush.CurrInd = 0;
        WillPush.SelectedCnt = 0;
        WillPush.ProdVal = 1;
        Stk.Push(WillPush);

        while (Stk.Count > 0) {
            JyoutaiDef Popped = Stk.Pop();

            //クリア判定
            if (Popped.SelectedCnt == pNeedCnt) {
                yield return Popped.ProdVal;
                continue;
            }

            for (int I = Popped.CurrInd; I <= mHeihouSuuArr.GetUpperBound(0); I++) {
                WillPush.CurrInd = I + 1;
                WillPush.SelectedCnt = Popped.SelectedCnt + 1;

                //葉ノードが、2の50乗未満である、必要条件のチェック
                int RestCnt = pNeedCnt - Popped.SelectedCnt;
                long wkSahen = mJyousuu50;
                wkSahen /= Popped.ProdVal;
                for (int J = 1; J <= RestCnt; J++)
                    wkSahen /= mHeihouSuuArr[I];
                if (wkSahen < 1) break;

                WillPush.ProdVal = Popped.ProdVal * mHeihouSuuArr[I];

                //2の50乗未満である、十分条件のチェック
                if (WillPush.ProdVal >= mJyousuu50) break;

                Stk.Push(WillPush);
            }
        }
    }
}


実行結果

2の25乗=33554432
2の50乗=1125899906842624
Answer=684465067343069。経過時間=00:00:03.0597556


解説

   2の50乗 < A * B
⇔ 2の50乗 / A / B < 1
ですので、
2の50乗をAで割って、端数を切り捨て、
さらにBで割って、端数を切り捨てた数が1未満であることは、

2の50乗 < A * B
であるための必要条件であることを使ってます。