代码之家  ›  专栏  ›  技术社区  ›  Daniel

转换表示为ulong值的4x4矩阵(尽可能快)

  •  3
  • Daniel  · 技术社区  · 7 年前

    2048 为了实施强化学习。

    直到最近,我还在使用4x4字节矩阵:

    var field = new byte[4,4];
    

    每个值都是2的指数,所以 0=0 , 1=2 2=4 , 3=8

    由于给定磁贴的(实际)最大值为15(仅需要4位),因此可以将此4x4字节数组的内容推入一个 ulong 价值

    事实证明,使用这种表示法,某些操作的效率要高得多。例如,我通常必须像这样反转阵列:

        //flip horizontally
        const byte SZ = 4;
        public static byte[,] Invert(this byte[,] squares)
        {
            var tmp = new byte[SZ, SZ];
            for (byte x = 0; x < SZ; x++)
                for (byte y = 0; y < SZ; y++)
                    tmp[x, y] = squares[x, SZ - y - 1];
            return tmp;
        }
    

    我可以把这个倒装成一个 无符号长整型 大约快15倍:

        public static ulong Invert(this ulong state)
        {
            ulong c1 = state & 0xF000F000F000F000L;
            ulong c2 = state & 0x0F000F000F000F00L;
            ulong c3 = state & 0x00F000F000F000F0L;
            ulong c4 = state & 0x000F000F000F000FL;
    
            return (c1 >> 12) | (c2 >> 4) | (c3 << 4) | (c4 << 12);
        }
    

    我最头疼的手术是 Transpose x 和 y 二维数组中值的坐标,如下所示:

        public static byte[,] Transpose(this byte[,] squares)
        {
            var tmp = new byte[SZ, SZ];
            for (byte x = 0; x < SZ; x++)
                for (byte y = 0; y < SZ; y++)
                    tmp[y, x] = squares[x, y];
            return tmp;
        }
    

        public static ulong Transpose(this ulong state)
        {
            ulong result = state & 0xF0000F0000F0000FL; //unchanged diagonals
    
            result |= (state & 0x0F00000000000000L) >> 12;
            result |= (state & 0x00F0000000000000L) >> 24;
            result |= (state & 0x000F000000000000L) >> 36;
            result |= (state & 0x0000F00000000000L) << 12;
            result |= (state & 0x000000F000000000L) >> 12;
            result |= (state & 0x0000000F00000000L) >> 24;
            result |= (state & 0x00000000F0000000L) << 24;
            result |= (state & 0x000000000F000000L) << 12;
            result |= (state & 0x00000000000F0000L) >> 12;
            result |= (state & 0x000000000000F000L) << 36;
            result |= (state & 0x0000000000000F00L) << 24;
            result |= (state & 0x00000000000000F0L) << 12;
    
            return result;
        }
    

    令人震惊的是,这仍然比循环版本快近3倍。然而,我正在寻找一种更高效的方法,或者利用转置中固有的模式,或者更有效地管理我正在移动的位。

    2 回复  |  直到 7 年前
        1
  •  2
  •   Aldert    7 年前

    您可以通过组合跳过6个步骤,我将它们注释掉以显示结果,应使其速度加倍:

    public static ulong Transpose(this ulong state)
            {
                ulong result = state & 0xF0000F0000F0000FL; //unchanged diagonals
    
                result |= (state & 0x0F0000F0000F0000L) >> 12;
                result |= (state & 0x00F0000F00000000L) >> 24;
                result |= (state & 0x000F000000000000L) >> 36;
                result |= (state & 0x0000F0000F0000F0L) << 12;
                //result |= (state & 0x000000F000000000L) >> 12;
                //result |= (state & 0x0000000F00000000L) >> 24;
                result |= (state & 0x00000000F0000F00L) << 24;
                //result |= (state & 0x000000000F000000L) << 12;
                //result |= (state & 0x00000000000F0000L) >> 12;
                result |= (state & 0x000000000000F000L) << 36;
                //result |= (state & 0x0000000000000F00L) << 24;
                //result |= (state & 0x00000000000000F0L) << 12;
    
                return result;
            }
    
        2
  •  1
  •   user555045    7 年前

    例如,12和24的左移可以按如下方式进行:

    ulong t = (state & 0x0000F000FF000FF0UL) * ((1UL << 12) + (1UL << 24));
    r0 |= t & 0x0FF000FF000F0000UL;
    

    imul

    public static ulong Transpose(this ulong state)
    {
        ulong r0 = state & 0xF0000F0000F0000FL; //unchanged diagonals
    
        ulong t = (state & 0x0000F000FF000FF0UL) * ((1UL << 12) + (1UL << 24));
        ulong r1 = (state & 0x0F0000F0000F0000L) >> 12;
        r0 |= (state & 0x00F0000F00000000L) >> 24;
        r1 |= (state & 0x000F000000000000L) >> 36;
        r0 |= (state & 0x000000000000F000L) << 36;
        r1 |= t & 0x0FF000FF000F0000UL;
    
        return r0 | r1;
    }
    
    推荐文章