编程语言
首页 > 编程语言> > c#-换位表示为ulong值的4×4矩阵(尽可能快)

c#-换位表示为ulong值的4×4矩阵(尽可能快)

作者:互联网

为了实现强化学习,我一直在研究2048的C#实现.

每次移动的“滑动”操作都需要根据特定规则移动并组合磁贴.这样做涉及到二维值数组上的许多转换.

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

var field = new byte[4,4];

每个值都是2的指数,因此0 = 0、1 = 2、2 = 4、3 = 8,依此类推. 2048磁贴将由11表示.

由于给定图块的(实际)最大值为15(仅需要4位),因此可以将此4×4字节数组的内容推入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,它翻转了2d数组中值的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倍.但是,我正在寻找一种性能更高的方法,要么利用转置中固有的模式,要么对我要移动的位进行更有效的管理.

解决方法:

您可以通过合并跳过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;
        }

标签:bit-manipulation,c,64-bit
来源: https://codeday.me/bug/20191108/2007877.html