高效生成二进制回文算法

编程领域,生成特定类型的字符串或数字序列是一个常见的任务。特别是对于二进制回文,即正读和反读都一样的二进制字符串,存在多种生成方法。本文将介绍一种高效的算法,该算法专注于直接构造所需的第n个二进制回文,而无需构造任何其他回文。

二进制回文的构造可以通过将每个回文分为左右两部分来实现,因为一个部分应该是另一个部分的反向。例如,对于一个偶数位的回文,中间的分隔符可以是 "|",用于可视化分隔。

以下是根据位数将连续的二进制回文分组的示例:

0 1 ---------------Group 0------------------ 1|1 101 111 ---------------Group 1------------------ 10|01 11|11 10001 10101 11011 11111 ---------------Group 2------------------ 100|001 101|101 110|011 111|111 1000001 1001001 1010101 1011101 1100011 1101011 1110111 1111111

如上所示,组0包含2位和3位的回文,组1包含4位和5位的回文,依此类推。

算法逻辑

为了构造第n个二进制回文,首先需要确定它属于哪个组。这里可以使用二进制对数来计算:

groupIndex = floor(log2((nth / 3) + 1))

除以3是因为每个组可以被分成3个子组:没有中间位、中间位为0、中间位为1。接下来,可以计算每个子组中的元素数量:

subGroupCount = 2 ^ groupIndex

然后,计算当前组之前的所有二进制回文数量(使用几何级数求和公式的变体):

Sum = ((2 ^ groupIndex) - 1) * 3

现在可以计算目标回文在其组内的索引:

var insideGroupIndex = nth - Sum;

以及在子组内的索引:

var insideSubGroupIndex = insideGroupIndex % subGroupCount;

接下来,可以确定二进制回文的中间位是空的、0还是1(这是子组的索引):

var opType = (int)Math.Floor((double)insideGroupIndex / subGroupCount);

现在有了组装回文所需的所有信息,但需要根据不同的子组进行不同的组装:

if (opType == 0) left side of a binary palindrome should be "1" + binaryStringRepresentation(subGroupIndex) padded left to digits count in a group. There should be no connector between sides if (opType == 1) left side of a binary palindrome should be "1" + binaryStringRepresentation(subGroupIndex / 2) padded left to digits count in a group. Connector should be '0' or '1' based on if groupIndex is divisible by 2 (even or odd) if (opType == 2) left side of a binary palindrome should be "1" + binaryStringRepresentation((subGroupIndex / 2) + (subGroupCount / 2)) padded left to digits count in a group. Connector should be '0' or '1' based on if groupIndex is divisible by 2 (even or odd)

最终输出应该是:

output = leftSide + connector + reverse(leftSide)

代码实现

以下是算法C#实现:

private string GetNthBinaryPalindrome(int n) { switch (n) { case 1: return "0"; case 2: return "1"; case 3: return "11"; case 4: return "101"; case 5: return "111"; default: if (n < 1) return null; var S = n - 3; var groupIndex = (int)Math.Floor(Math.Log((S / 3) + 1, 2)); var digitsCount = groupIndex + 1; var subGroupCount = (int)Math.Round(Math.Pow(2, groupIndex)); var Sn = (subGroupCount - 1) * 3; var insideGroupIndex = S - Sn; var subGroupIndex = insideGroupIndex % subGroupCount; var opType = (int)Math.Floor((double)insideGroupIndex / subGroupCount); string leftSide; string connector; switch (opType) { case 0: connector = ""; leftSide = "1" + Convert.ToString(subGroupIndex, 2).PadLeft(digitsCount, '0'); break; case 1: connector = ((groupIndex & 1) == 0) ? "0" : "1"; leftSide = "1" + Convert.ToString(subGroupIndex / 2, 2).PadLeft(digitsCount, '0'); break; case 2: connector = ((groupIndex & 1) == 0) ? "0" : "1"; leftSide = "1" + Convert.ToString(subGroupIndex / 2 + subGroupCount / 2, 2).PadLeft(digitsCount, '0'); break; default: return null; } return String.Format("{0}{1}{2}", leftSide, connector, String.Join("", leftSide.Reverse())); } }

优化

尽管这个算法可能还有进一步优化的空间,但对于作者的目的来说,它已经足够高效了。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485