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

如何利用预计算查找表实现多格骨牌(Polyomino)的一一映射?

问题:利用0岛查找表直接定位第N个Polyomino

本周早些时候,我询问了如何加速判断bitboard中是否包含polyomino的方法。我对polyomino的定义是所有方块通过边相连的任意排列,不会因存在镜像、旋转或翻转版本,或带有孔洞而排除。有用户指出,使用查找表统计0岛数量可实现更快判断(多数人统计1岛,但我的现有逻辑使用0岛)。

经过一番研究后我思考:能否直接使用该查找表?例如输入100,返回第100个polyomino。目前我通过遍历每个ulong并逐一检查来查找目标,而polyomino之间的间隔可能极大,最大约为4×10¹⁸。因此我希望跳过无需关注的内容,仅聚焦于polyomino。我已在评论中询问该用户,对方表示可行,因此发布此帖,欢迎任何人解答。

以下是上一篇帖子中用于创建查找表的代码:

/// <summary>
/// Map from the long-form state code for each state to the state number
/// see expandState.
/// 
/// This is only used during state table construction.
/// </summary>
Dictionary<string, ushort> stateNumbers = new Dictionary<string, ushort>();

/// <summary>
/// Packed map from (state*256 + next_row_byte) -> transition
/// 
/// transition is next_state + (island_count<<12), where island_count is the
/// number of islands cut off from the further rows
/// </summary>
List<ushort> transitions = new List<ushort>();

/// <summary>
/// The byte representing a row of all water.  Note that this code counts
/// 0-islands, not 1-islands
/// </summary>
const byte ALL_WATER = (byte)0xFF;

#region Disjoint Set
/*
 * Disjoint set the proper way.  The sets are integers in an array:
 * For each integer i
 *   - i === 0 => set is uninitialized (not yet a set)
 *   - i < 0 => set is a link to ~i
 *   - i >= 0 => set is of size i
 */

// find with path compression.
int find(int[] sets, int s)
{
    int parent = sets[s];
    if (parent > 0)
    {
        return s;
    }
    else if (parent < 0)
    {
        parent = find(sets, ~parent);
        sets[s] = ~parent;
        return parent;
    }
    else
    {
        sets[s] = 1;
        return s;
    }
}

// union by size
bool union(int[] sets, int x, int y)
{
    x = find(sets, x);
    y = find(sets, y);
    if (x == y)
    {
        return false;
    }
    int szx = sets[x];
    int szy = sets[y];
    if (szx < szy)
    {
        sets[y] += szx;
        sets[x] = ~y;
    }
    else
    {
        sets[x] += szy;
        sets[y] = ~x;
    }
    return true;
}

#endregion


/// <summary>
/// Expands the specified state code.
/// 
/// A state code is a string of digits.
///  0 => water
///  x => island number x.  new islands are numbered from left to right
/// </summary>
/// <param name="stateCode">The state code to expand.</param>
/// <param name="nextrow">the lower 8 bits represent the next row.  0-bits are land</param>
/// <returns>The transition code for the transition from stateCode to nextrow</returns>
ushort expandState(string stateCode, int nextrow)
{
    // convert the next row into a disjoint set array
    // if you want to count 1-islands instead of 0-islands, change `~nextrow` into `nextrow` below,
    // and fix the ALL_WATER constant
    int[] sets = new int[8];
    for (int i = 0; i < 8; ++i)
    {
        sets[i] = (~nextrow >> i) & 1;
    }
    for (int i = 0; i < 7; ++i)
    {
        if (((~nextrow >> i) & 3) == 3)
        {
            union(sets, i, i + 1);
        }
    }
    // map from state code island to first attached set in sets
    int[] links = [-1, -1, -1, -1, -1, -1, -1, -1];
    int topIslandCount = 0;
    for (int i = 0; i < 8; ++i)
    {
        char digit = stateCode[i];
        int topisland = digit - '1';
        topIslandCount = Math.Max(topIslandCount, topisland + 1);
        if (sets[i] != 0 && topisland >= 0)
        {
            // connection from prev row to nextrow
            int bottomSet = links[topisland];
            if (bottomSet < 0)
            {
                // this island is not yet connected
                links[topisland] = i;
            }
            else
            {
                // the top island is already connected. union bottom sets
                union(sets, bottomSet, i);
            }
        }
    }

    // count the number of top-row islands that don't connect to the next row.
    int cutOffCount = 0;
    for (int i = 0; i < topIslandCount; ++i)
    {
        if (links[i] < 0)
        {
            ++cutOffCount;
        }
    }

    // turn the new union-find array into a state code
    char nextSet = '1';
    char[] newChars = "00000000".ToCharArray();
    for (int i = 0; i < 8; ++i)
    {
        links[i] = -1;
    }
    for (int i = 0; i < 8; ++i)
    {
        if (sets[i] != 0)
        {
            int set = find(sets, i);
            int link = links[set];
            if (link >= 0)
            {
                newChars[i] = newChars[link];
            }
            else
            {
                newChars[i] = nextSet++;
                links[set] = i;
            }
        }
    }
    string newStateCode = new string(newChars);

    // get the state number
    if (stateNumbers.ContainsKey(newStateCode))
    {
        // state already exists and is/will be expanded
        return (ushort)(stateNumbers[newStateCode] | (cutOffCount << 12));
    }
    ushort newState = (ushort)stateNumbers.Count;
    stateNumbers.Add(newStateCode, newState);

    // fill out the state table
    while (transitions.Count <= (newState + 1) * 256)
    {
        transitions.Add(0);
    }
    for (int i = 0; i < 256; ++i)
    {
        transitions[newState * 256 + i] = expandState(newStateCode, i);
    }
    return (ushort)(newState | (cutOffCount << 12));
}

int startState = expandState("00000000", ALL_WATER);

Console.WriteLine(startState);
Console.WriteLine(stateNumbers.Count);

int Count0Islands(ulong bitboard)
{
    int state = 0;
    int count = 0;
    for (int i = 0; i < 8; ++i)
    {
        var transition = transitions[state * 256 + (int)(bitboard & 0xFF)];
        count += transition >> 12;
        state = transition & 0xFFF;
        bitboard >>= 8;
    }
    // transition to ALL_WATER to count last islands
    count += transitions[state * 256 + ALL_WATER] >> 12;
    return count;
}

ulong[] tests = {
    0x7e220a7e4a58580Ful,
    0x7e22087e4a58580Ful,
    0xFFFFFFFFFFFFFFFFul,
    0x813c425a5a423c81ul
};

foreach (ulong test in tests)
{
    Console.WriteLine();
    Console.WriteLine();
    for (int row = 0; row < 8; ++row)
    {
        int rowByte = (int)(test >> (row * 8)) & 0xFF;
        string rowStr = Convert.ToString(rowByte, 2).PadLeft(8, '0');
        rowStr = rowStr.Replace("1", " ");
        rowStr = rowStr.Replace("0", "#");
        Console.WriteLine(rowStr);
    }
    Console.WriteLine();
    Console.WriteLine("Islands: " + Count0Islands(test));
}

内容的提问来源于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 20:14:53