You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何更快判断Bitboard中的0是否构成Polyomino?

如何高效判断Bitboard中的所有0是否构成单连通Polyomino

我当前使用的方法逻辑清晰,但希望进一步提升性能,寻求其他可行方案。需求是判断Bitboard里所有0的位置是否构成Polyomino——定义为所有方块通过边缘(上下左右)相连的排列,允许镜像、旋转、翻转,也允许带孔。

现有实现思路

我的现有方法:

  • 统计Bitboard中0的总数;
  • 找到第一个0的位置,通过**广度优先搜索(BFS)**遍历所有边缘连通的0;
  • 若遍历到的0的数量等于总数,则判定为Polyomino,否则不是。

另一种思路构想

我还想到一个跨行合并岛屿的思路:遍历每一行拆分出0的“岛屿”(也可反转Bitboard找1的岛屿,逻辑一致)。例如下面两行:

1 1 1 0 0 1 1 1
0 0 1 1 1 1 0 0

第一行有两个0的岛屿a(前三个1后的两个0)和b(最后三个1前的两个0),第二行有一个0的岛屿c(中间四个1两侧的两个0)。若a和c的按位与结果不为0,说明二者连通;同理b和c也连通,可将a和b合并。重复这个跨行合并过程,最终若所有岛屿合并为一个,就说明是Polyomino。但不确定拆分岛屿的开销是否会影响效率。

注:Bitboard索引规则为左上角起点,从左到右排列,index+1对应右侧单元格,index-8对应上方单元格。

现有代码实现

public static bool PolyominoChecker(ulong bitboard)
{
    // 统计0的数量
    int population = 64 - BitOperations.PopCount(bitboard);

    HashSet<int> visited = new HashSet<int>();
    Queue<int> queue = new Queue<int>();

    // 获取第一个0的位置
    int firstZero = BitOperations.TrailingZeroCount(~bitboard);
    visited.Add(firstZero);
    queue.Enqueue(firstZero);

    // BFS遍历所有连通的0
    while (queue.Count > 0)
    {
        int index = queue.Dequeue();
        // 检查左侧单元格
        if(index % 8 > 0 && !GetBitboardCell(bitboard, index - 1))
        {
            if (!visited.Contains(index - 1))
            {
                visited.Add(index - 1);
                queue.Enqueue(index - 1);
            }
        }
        // 检查上方单元格
        if (index >= 8 && !GetBitboardCell(bitboard, index - 8))
        {
            if (!visited.Contains(index - 8))
            {
                visited.Add(index - 8);
                queue.Enqueue(index - 8);
            }
        }
        // 检查下方单元格
        if (index + 8 < 64 && !GetBitboardCell(bitboard, index + 8))
        {
            if (!visited.Contains(index + 8))
            {
                visited.Add(index + 8);
                queue.Enqueue(index + 8);
            }
        }
        // 检查右侧单元格
        if (index % 8 < 7 && !GetBitboardCell(bitboard, index + 1))
        {
            if (!visited.Contains(index + 1))
            {
                visited.Add(index + 1);
                queue.Enqueue(index + 1);
            }
        }
    }

    // 遍历到的0数量等于总数则符合条件
    return visited.Count == population;
}

public static bool GetBitboardCell(ulong bitboard, int index)
{
    return (bitboard & (1UL << index)) != 0;
}

优化方案建议

1. 现有BFS的性能优化

现有代码的瓶颈在于HashSet<int>的哈希查找开销,以及GetBitboardCell的重复调用。可以通过以下方式优化:

  • 用位掩码替代HashSet:用ulong visitedMask记录已访问的0位置,位运算判断访问状态比哈希查找快得多;
  • 提前计算反转后的Bitboard:减少重复的位运算调用;
  • 提前终止遍历:一旦遍历到的0数量等于总数,直接返回结果。

优化后的BFS代码:

public static bool PolyominoCheckerOptimized(ulong bitboard)
{
    ulong emptyCells = ~bitboard;
    int population = BitOperations.PopCount(emptyCells);

    // 无0或全为0的情况直接返回true
    if (population == 0 || population == 64)
        return true;

    ulong visitedMask = 0;
    Queue<int> queue = new Queue<int>();

    int firstZero = BitOperations.TrailingZeroCount(emptyCells);
    ulong firstZeroMask = 1UL << firstZero;
    visitedMask |= firstZeroMask;
    queue.Enqueue(firstZero);
    int visitedCount = 1;

    while (queue.Count > 0)
    {
        int index = queue.Dequeue();

        // 左侧单元格
        if (index % 8 > 0)
        {
            int left = index - 1;
            ulong leftMask = 1UL << left;
            if ((emptyCells & leftMask) != 0 && (visitedMask & leftMask) == 0)
            {
                visitedMask |= leftMask;
                queue.Enqueue(left);
                visitedCount++;
                if (visitedCount == population)
                    return true;
            }
        }

        // 上方单元格
        if (index >= 8)
        {
            int up = index - 8;
            ulong upMask = 1UL << up;
            if ((emptyCells & upMask) != 0 && (visitedMask & upMask) == 0)
            {
                visitedMask |= upMask;
                queue.Enqueue(up);
                visitedCount++;
                if (visitedCount == population)
                    return true;
            }
        }

        // 下方单元格
        if (index < 56) // 56 = 64 - 8,避免越界
        {
            int down = index + 8;
            ulong downMask = 1UL << down;
            if ((emptyCells & downMask) != 0 && (visitedMask & downMask) == 0)
            {
                visitedMask |= downMask;
                queue.Enqueue(down);
                visitedCount++;
                if (visitedCount == population)
                    return true;
            }
        }

        // 右侧单元格
        if (index % 8 < 7)
        {
            int right = index + 1;
            ulong rightMask = 1UL << right;
            if ((emptyCells & rightMask) != 0 && (visitedMask & rightMask) == 0)
            {
                visitedMask |= rightMask;
                queue.Enqueue(right);
                visitedCount++;
                if (visitedCount == population)
                    return true;
            }
        }
    }

    return visitedCount == population;
}

2. 跨行岛屿合并思路的评估

这个思路的核心是通过行内岛屿的跨行连通性合并,但拆分行内岛屿需要额外的位运算操作。对于8x8的Bitboard来说,每行拆分岛屿的开销不大,但合并逻辑需要维护岛屿集合并检查连通性,整体复杂度比优化后的BFS更高,甚至可能因为集合操作的开销更慢。如果是更大尺寸的Bitboard,该思路可能有并行处理的潜力,但8x8场景下优化后的BFS更直接高效。

3. 极致位运算连通性判断

针对8x8的Bitboard,可完全用位运算实现连通分量计算,无需集合和队列,性能最优:

public static bool PolyominoCheckerBitwise(ulong bitboard)
{
    ulong empty = ~bitboard;
    if (empty == 0) return true;

    ulong connected = empty & -empty; // 获取第一个0对应的位掩码
    ulong prev;

    do
    {
        prev = connected;
        // 处理右侧邻居:左移1位,用掩码避免跨行列
        ulong right = connected << 1;
        right &= 0x7F7F7F7F7F7F7F7F;
        // 处理左侧邻居:右移1位,用掩码避免跨行列
        ulong left = connected >> 1;
        left &= 0xFEFEFEFEFEFEFEFE;
        // 处理上方邻居:上移8位
        ulong up = connected << 8;
        // 处理下方邻居:下移8位
        ulong down = connected >> 8;

        // 合并所有连通的邻居,并仅保留属于empty的部分
        connected |= right | left | up | down;
        connected &= empty;
    } while (connected != prev);

    return connected == empty;
}

内容的提问来源于stack exchange,提问作者timeslidr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 21:44:53