按特定顺序重新排列乌龙

对于计算机游戏,我使用存储在 ulong 中的位掩码(希望我正确使用此术语)。 该位掩码正在与其他位掩码交互,当另一个位掩码重叠时,该位掩码会将位的1s变为0s。与其他位掩码的交互结束后,我要检查特定位是否为1,一个接一个。但是,它的顺序不符合ulong中的顺序。

在交互阶段,由于对称性原因,它们最初的排序方式有所不同,这使得可以对预排序的位掩码进行旋转。

我写下了原始位的顺序。如果需要该命令帮助我,这里是:

31,39,34,27,43,30,38,26,42,33,29,37,23,47,25,41,22,46,32,28,36,21,45,24,40,20,44,48,16,49,17,50,18,51,19,12,52,8,56,13,53,4,60,14,54,9,57,15,55,5,61,1,10,58,6,62,11,59,2,7,63,3

(例如,第一个数字为31。这意味着预排序的ulong的第31位(从0开始计数)必须进入已排序的ulong的第一位。然后,第39位的值进入进入排序的ulong中的第二个,依此类推)

我的游戏代码的这一部分是最关键的性能,因此我寻求实现这一目标的最有效方法。它与人群机制有关,并且该代码显示的效率越高,我可以并且将包含的人群成员越多。

我也愿意使用低MB大小的查找表。我的一个想法是将ulong分为四个短裤,将它们放入一个可查询的表中,每个返回一个排序后的短裤。但是,由于不仅需要在这四个块之间移动许多位,所以仅此一项是行不通的。

我的另一个想法是:

ulong unsorted = x;
ulong sorted = 0;
sorted += (unsorted & 0x0001000010000000) >> 18 //bits that are shifted the same manner
//go on
sunyinglu 回答:按特定顺序重新排列乌龙

一些初步注意事项:

  • 您的示例数据只有63个值。出于下面我的代码示例的目的,我将35的缺失值随意添加到列表的末尾。
  • 在您的问题中,没有任何关于您如何首先陷入这种混乱状态的细节。不用说,但是无论如何我都会提到: best 解决方案是完全消除问题的解决方案。在您的确切情况下是否有可能这样做,不要紧怎么做,我不能说,因为问题中缺少细节。

所有所说的…

  

我的另一个想法是:

     
ulong unsorted = x;
ulong sorted = 0;
sorted += (unsorted & 0x0001000010000000) >> 18 //bits that are shifted the same manner
//go on

该想法可能会奏效。但是您没有在问题中提供足够的详细信息让其他人知道,因为我们没有有关为何对位进行重新排序的任何信息。您似乎在想编写特殊情况的代码,以利用首先导致位排序的任何模式,但是由于没有关于该模式的具体细节,没有其他人可以充实这种想法。

  

我的一个想法是将ulong分为四个短裤,将它们放入一个可查询的表中,每个返回一个排序后的短裤。但是,由于不仅需要在这四个块之间移动许多位,所以仅此一项是行不通的。

这个想法也行得通。没错,您无法映射到short值(甚至是ushort)。但是您可以映射到ulong值,然后合并结果。

当然,对于任何代码来说,第一件事也是最重要的事情就是让它起作用。因此,恕我直言,从一个可以验证的更简单的实现入手是有意义的。完成之后,您就可以继续进行其他选择了。

这里是一种选择:

static readonly int[] bitOrder = // NOTE: 35 not in original example,added here at end arbitrarily
{                                // so that a valid implementation can be demonstrated
    31,39,34,27,43,30,38,26,42,33,29,37,23,47,25,41,22,46,32,28,36,21,45,24,40,20,44,48,16,49,17,50,18,51,19,12,52,8,56,13,53,4,60,14,54,9,57,15,55,5,61,1,10,58,6,62,11,59,2,7,63,3,35
};

private static ulong[] BuildBitwiseMapping()
{
    ulong[] bitwiseMapping = new ulong[bitOrder.Length];

    for (int i = 0; i < bitwiseMapping.Length; i++)
    {
        bitwiseMapping[i] = 1UL << bitOrder[i];
    }

    return bitwiseMapping;
}

private static ulong SortBits(ulong[] bitwiseMapping,ulong value)
{
    ulong sorted = 0,mask = 1;

    for (int i = 0; i < bitwiseMapping.Length; i++,mask <<= 1)
    {
        if ((value & bitwiseMapping[i]) != 0)
        {
            sorted |= mask;
        }
    }

    return sorted;
}

您将首先调用BuildBitwiseMapping(),然后对每次调用SortBits()使用返回的数组。自然地,您可以将内置数组作为static readonly字段并入,以简化对SortBits()的调用。

这必须为每个位循环一次,这不一定是最有效的。但是除了循环条件之外,严格来说,它仅是查找和按位“或”运算,因此已经相当快了。

如果您想使用查找表的思想来改进它,那就很简单:

private static ulong[][] BuildWordwiseMapping(ulong[] bitwiseMapping)
{
    ulong[][] result = new ulong[4][];
    ulong increment = 1;

    for (int i = 0; i < result.Length; i++)
    {
        result[i] = new ulong[ushort.MaxValue + 1];
        ulong value = 0;

        for (int j = 0; j <= ushort.MaxValue; j++)
        {
            result[i][j] = SortBits(bitwiseMapping,value);
            value += increment;
        }

        increment = value;
    }

    return result;
}

private static ulong SortBits(ulong[][] wordwiseMapping,ulong value)
{
    ulong sorted = 0;

    for (int i = 0; i < wordwiseMapping.Length; i++,value >>= 16)
    {
        int ushortPart = (int)(value & 0xffff);

        sorted |= wordwiseMapping[i][ushortPart];
    }

    return sorted;
}

上面的代码从第一个示例中的64位按位表开始,并使用它来构建四个更大的表以用于按字查找。

您需要四个不同的表,一个用于原始ulong值的每个位子集,一个表,因为它们的映射当然不同。它们可以分布在整个ulong结果中,因此表元素大小仍然需要为ulong

这将为您提供四个表,每个表包含65536个元素,每个元素为8个字节长,总内存占用为2MB。另一方面,虽然较简单的按位版本每个值需要64次循环迭代,而按字版本仅需要4次循环。这可以显着提高吞吐量。否则可能总体上影响不大;它实际上取决于该代码实际上到底有多大的瓶颈,以及四表版本的速度有多快。

您可能在上面已经注意到,从技术上讲,实际上不需要在每个表中处理值0,因为不会为这些值设置任何位,无论它们出现在什么位置在原始的ulong中。但是,实际上可能是对0进行 compare 并处理这种特殊情况,然后只查找NOP“零映射到零”值,这会在性能上花费更多。

,

执行此排列的另一种方法是:

ulong permute(ulong x)
{
    x = bit_permute_step(x,0x00000000c539d22d,32);
    x = bit_permute_step(x,0x0000cbc10000749e,16);
    x = bit_permute_step(x,0x0039007e00a9007f,8);
    x = bit_permute_step(x,0x06010e04070e0507,4);
    x = bit_permute_step(x,0x2113320311022110,2);
    x = bit_permute_step(x,0x5401151151100141,1);
    x = bit_permute_step(x,0x0032323110011333,0x030b0a0b080e0d00,0x00a5009500220097,0x00005cd6000043cc,0x00000000b3f003f3,32);
    return x;
}

bit_permute_step的定义为:

ulong bit_permute_step(ulong source,ulong mask,int shift)
{
    ulong t = ((source >> shift) ^ source) & mask;
    return (source ^ t) ^ (t << shift);
}

该构造是一个完整的11步Beneš网络(6 * 2-1,其中6是64的以2为底的对数)。这是一种非常通用的技术,可以处理任何排列,在许多情况下,有些特殊情况下的技术会更好,但我找不到这种方法。

本文链接:https://www.f2er.com/3056971.html

大家都在问