C#で整数値をn個でいい感じに分割してみる。

C# コンピュータ
C#

1000ミリ秒(1秒)で60回の処理を行う場合、1回に使える時間をミリ秒で計算したいと思い調べてみました。

たとえば電卓で1000を60で割ると16.66666666666667となります。少数が使えればこれでよいのですが、整数値のみで考えてみたいと思います。
整数の割り算の場合商は16と余りが40となります。
この場合16が20個(60-40)と17が40個にすると、16×20+17×40=1000でちょうど1000になります。
そして、なるべく数が偏らないように配置し、できれば16,17,16,17,…ような並びにしたいと思います。

1000を60で割るのサンプルプログラム。

class Program
{
    static void Main()
    {
        uint x = 1000;
        uint n = 60;

        uint div = x / n;
        uint mod = x % n;

        uint result = 0;
        uint a = 0;
        uint b = 0;

        uint t = 0;
        for (uint i=0; i < n;  i++)
        {
            t = (x+i)/n;
            Console.WriteLine("i:{0} t:{1} x+i:{2}", i, t, (x+i));
            a += (uint)(t == div ? 1 : 0);
            b += (uint)(t == div ? 0 : 1);
            result += t;
        }
        Console.WriteLine("result:{0} a:{1} b:{2}", result, a, b);
        if (result != x) throw new Exception("result != x");
        if (n != (a+b)) throw new Exception("n != (a+b)");

        result = 0; // リセット
        for (uint i=0; i < n;  i++)
        {
            if ((i % 2) == 0)
            {
                // 偶数
                if (a <= 0)
                {
                    b--;
                    t = div + 1;
                }
                else
                {
                    a--;
                    t = div;
                }
            }
            else 
            {
                // 奇数
                if (b <= 0)
                {
                    a--;
                    t = div;
                }
                else
                {
                    b--;
                    t = div + 1;
                }
            }
            Console.WriteLine("i:{0} t:{1}", i, t);
            result += t;
        }
        if (result != x) throw new Exception("result != x");
    }
}

1000割る60の結果

i:0 t:16 x+i:1000
i:1 t:16 x+i:1001
i:2 t:16 x+i:1002
i:3 t:16 x+i:1003
i:4 t:16 x+i:1004
i:5 t:16 x+i:1005
i:6 t:16 x+i:1006
i:7 t:16 x+i:1007
i:8 t:16 x+i:1008
i:9 t:16 x+i:1009
i:10 t:16 x+i:1010
i:11 t:16 x+i:1011
i:12 t:16 x+i:1012
i:13 t:16 x+i:1013
i:14 t:16 x+i:1014
i:15 t:16 x+i:1015
i:16 t:16 x+i:1016
i:17 t:16 x+i:1017
i:18 t:16 x+i:1018
i:19 t:16 x+i:1019
i:20 t:17 x+i:1020
i:21 t:17 x+i:1021
i:22 t:17 x+i:1022
i:23 t:17 x+i:1023
i:24 t:17 x+i:1024
i:25 t:17 x+i:1025
i:26 t:17 x+i:1026
i:27 t:17 x+i:1027
i:28 t:17 x+i:1028
i:29 t:17 x+i:1029
i:30 t:17 x+i:1030
i:31 t:17 x+i:1031
i:32 t:17 x+i:1032
i:33 t:17 x+i:1033
i:34 t:17 x+i:1034
i:35 t:17 x+i:1035
i:36 t:17 x+i:1036
i:37 t:17 x+i:1037
i:38 t:17 x+i:1038
i:39 t:17 x+i:1039
i:40 t:17 x+i:1040
i:41 t:17 x+i:1041
i:42 t:17 x+i:1042
i:43 t:17 x+i:1043
i:44 t:17 x+i:1044
i:45 t:17 x+i:1045
i:46 t:17 x+i:1046
i:47 t:17 x+i:1047
i:48 t:17 x+i:1048
i:49 t:17 x+i:1049
i:50 t:17 x+i:1050
i:51 t:17 x+i:1051
i:52 t:17 x+i:1052
i:53 t:17 x+i:1053
i:54 t:17 x+i:1054
i:55 t:17 x+i:1055
i:56 t:17 x+i:1056
i:57 t:17 x+i:1057
i:58 t:17 x+i:1058
i:59 t:17 x+i:1059
result:1000 a:20 b:40
i:0 t:16
i:1 t:17
i:2 t:16
i:3 t:17
i:4 t:16
i:5 t:17
i:6 t:16
i:7 t:17
i:8 t:16
i:9 t:17
i:10 t:16
i:11 t:17
i:12 t:16
i:13 t:17
i:14 t:16
i:15 t:17
i:16 t:16
i:17 t:17
i:18 t:16
i:19 t:17
i:20 t:16
i:21 t:17
i:22 t:16
i:23 t:17
i:24 t:16
i:25 t:17
i:26 t:16
i:27 t:17
i:28 t:16
i:29 t:17
i:30 t:16
i:31 t:17
i:32 t:16
i:33 t:17
i:34 t:16
i:35 t:17
i:36 t:16
i:37 t:17
i:38 t:16
i:39 t:17
i:40 t:17
i:41 t:17
i:42 t:17
i:43 t:17
i:44 t:17
i:45 t:17
i:46 t:17
i:47 t:17
i:48 t:17
i:49 t:17
i:50 t:17
i:51 t:17
i:52 t:17
i:53 t:17
i:54 t:17
i:55 t:17
i:56 t:17
i:57 t:17
i:58 t:17
i:59 t:17

1回目のループで16と17の個数を求め、2回目のループで偶数奇数で16と17がなるべく交互に来るように配置します。
あとは、結果を配列に仕込んでテーブルとして使えばよさそうです。

追記:
偶数奇数にしても後半に17が並ぶので、もうひとひねりして17,17,16,17,17,16の並びが繰り返されるようにしてみました。

class Program
{
    static void Main()
    {
        uint x = 1000;
        uint n = 60;

        uint div = x / n;
        uint mod = x % n;

        uint result = 0;
        uint a = 0;
        uint b = 0;

        uint t = 0;
        for (uint i=0; i < n;  i++)
        {
            t = (x+i)/n;
            Console.WriteLine("i:{0} t:{1} x+i:{2}", i, t, (x+i));
            a += (uint)(t == div ? 1 : 0);
            b += (uint)(t == div ? 0 : 1);
            result += t;
        }
        Console.WriteLine("result:{0} a:{1} b:{2}", result, a, b);
        if (result != x) throw new Exception("result != x");
        if (n != (a+b)) throw new Exception("n != (a+b)");

        // 最大公約数
        uint aa = a;
        uint bb = b;
        while(aa != bb)
        {
            if (aa > bb)
            {
                aa -= bb;
            }
            else
            {
                bb -= aa;
            }
        }

        uint aaa = a / aa;
        uint bbb = b / aa;

        /*
        Console.WriteLine("aaa:{0} bbb:{1}", aaa, bbb);
        */

        uint ccc = aaa + bbb;
        uint[] tbl = new uint[ccc];

        int j = 0;
        while(aaa > 0 || bbb > 0)
        {
            if (aaa == 0)
            {
                bbb--;
                tbl[j] = div+1; // b
            }
            else if (bbb == 0)
            {
                aaa--;
                tbl[j] = div; // a
            }
            else
            {
                if (aaa > bbb)
                {
                    aaa--;
                    tbl[j] = div; // a
                }
                else
                {
                    bbb--;
                    tbl[j] = div+1; // b
                }
            }
            j++;
        }
        /*
        for(j=0;j<tbl.Length;j++)
        {
            Console.WriteLine("{0}:{1}", j, tbl[j]);
        }
        */
        t = 0;
        result = 0;
        for (uint i=0; i < n;  i++)
        {
            int ii = (int)(i % ccc);
            Console.WriteLine("i:{0} t:{1}", i, tbl[ii]);
            result += tbl[ii];
        }
        if (result != x) throw new Exception("result != x");
    }
}

最大公約数なんて言葉を何十年ぶりに思い出しました。
算数や数学をもっと頑張って勉強すればよかったと感じる瞬間でしたが、こういった問題に直面しないと公式の有難みは理解できないと思われます。また、公式をアルゴリズムに落とし込む作業はプログラマの領分ですが、いつもライブラリばかり使っていると、この辺りのスキルが向上せず効率の悪いプログラムになりがちです。
とまぁ自分の不勉強と衰える感じる案件でした。

コメント