【问题标题】:Verifying binary patterns for large integers (BigInteger)验证大整数的二进制模式 (BigInteger)
【发布时间】:2012-08-02 19:21:45
【问题描述】:

我想测试一个正整数,看看它的二进制表示是否以零个或多个 1 开头,后跟一个或多个 0。

00000000 // Valid
10000000 // Valid
11000000 // Valid
11100000 // Valid
11110000 // Valid
11111100 // Valid
11111110 // Valid
11111110 // Valid
11111111 // Not Valid
// Any other combination is Not Valid

用正则表达式表示的同样是^[1]*[0]+$。当然这只是为了澄清,我们不能使用正则表达式。

蛮力逼近:

  • 创建多个位掩码,并一起确定结果。
  • 使用动态掩码遍历每个数字以确定结果。

问题是我正在处理可能有数十万位数字的巨大正整数,并且需要对数千个这样的数字执行此测试。

有没有更有效的方法来确定这种二进制模式?

更新

这是我尝试过的实现。尚未将时间与其他答案进行比较。

public static bool IsDiagonalToPowerOfTwo (this System.Numerics.BigInteger number)
{
    byte [] bytes = null;
    bool moreOnesPossible = true;

    if (number == 0) // 00000000
    {
        return (true); // All bits are zero.
    }
    else
    {
        bytes = number.ToByteArray();

        if ((bytes [bytes.Length - 1] & 1) == 1)
        {
            return (false);
        }
        else
        {
            for (byte b=0; b < bytes.Length; b++)
            {
                if (moreOnesPossible)
                {
                    if (bytes [b] == 255)
                    {
                        // Continue.
                    }
                    else if
                    (
                        ((bytes [b] & 128) == 128) // 10000000
                        || ((bytes [b] & 192) == 192) // 11000000
                        || ((bytes [b] & 224) == 224) // 11100000
                        || ((bytes [b] & 240) == 240) // 11110000
                        || ((bytes [b] & 248) == 248) // 11111000
                        || ((bytes [b] & 252) == 252) // 11111100
                        || ((bytes [b] & 254) == 254) // 11111110
                    )
                    {
                        moreOnesPossible = false;
                    }
                    else
                    {
                        return (false);
                    }
                }
                else
                {
                    if (bytes [b] > 0)
                    {
                        return (false);
                    }
                }
            }
        }
    }

    return (true);
}

【问题讨论】:

  • 您实际上是将它们用作整数,还是仅用作位存储?坦率地说,如果这些是唯一有效的集合,我会使用不同的表示,即“个数,零个数”。当然,如果你需要做算术等,那是行不通的。
  • 我不知道什么是HUME大整数,这在.net上是如何表示的?我想 ubyte/uint/etc 的数组……是这个吗? @MarcGravell 已经问过了,但是你是在用这些数据做一些算术还是只是一个表示?
  • @devundef:我的错误。它应该是 HUGE/LARGE,而不是 HUME。
  • 提示:不要使用扩展方法,每次调用该方法时,都会在内存中生成一个巨大整数的副本;尽可能使用“ref”。

标签: c# .net binary numbers bit-manipulation


【解决方案1】:

假设整数以二进制形式存储,分组到一个无符号整数数组 x[] 中,您可以这样做:

Define UINT to be the unsigned integer type you are using for the grouped bits.
Define UMAX to be the maximum value of that type (all bits are on).

// Find first word that has a zero bit.
int i;
for (i = highest word in x; 0 <= i; --i)
    if (x[i] != UMAX)
        break;

// Return true if all bits in all of x[] are on.
if (i < 0)
    return true;

// Test whether word conforms to the ones-then-zeroes rule.
UINT y = x[i];
if (y + (y & -y))
    return false;

// Test whether all remaining words are zero.
for (; 0 <= i; --i)
    if (x[i])
        return false;

return true;

y + (y &amp; -y) 中,y &amp; -y 返回 y 中设置的最低位。 (证明留给读者作为练习。)如果 y 中的所有高位都打开,添加最低位会导致进位传播通过所有这些位,将它们更改为零。如果这些高位中的任何一个为关闭,则进位停止,结果不为零。否则,结果为零。

你能在上面改进吗?假设比较和分支比 AND 等操作具有更高的成本。在这种情况下,您可以使用二进制搜索在数组中查找值从全 1 变为全零或都不变为全零的位置。测试如上标识的关键字,然后将所有较高的值与所有较高的值相加并测试其结果为所有 1,然后将所有较低的值与所有较低的值相或并测试其结果为所有零。

这为您提供了一个二分搜索,然后是一个加载和每个单词的一个 AND 或 OR。这将很难改进。

【讨论】:

  • 谢谢。我将尝试这两种方法,并返回与使用预定义掩码 128、192、224、240、252 和 254 的从左到右字节数组掩码相比有多少改进。
  • 非常优雅:y + (y & -y)。我在我的答案中使用了。
  • 谢谢。 BigInteger 类公开了一个 ToByteArray 函数。转换为 long/ulong 等数组会导致额外的开销。有什么理由你的函数不应该在字节数组上工作吗?
  • 我的答案是以与大小无关的方式编写的;它假定一个无符号整数数组。这些整数可以是 8 位、32 位或其他大小。尽管如此,使用更大的单元尺寸,无论对机器来说是什么自然的,都可能具有更好的性能。将字节数组转换为 32 位或 64 位整数数组实际上不应更改任何数据或移动任何内存。这只是编译器中要处理的语义问题,例如将指针转换为新类型。
  • 更正:将字节数组转换为更大的数组可能会导致问题。如果字节数组未按照 32 位操作的要求/期望对齐(例如),则对其执行 32 位操作可能会导致对齐异常或性能不佳,具体取决于目标系统。如果 BigInteger 类自然地使用对齐内存,我不会感到惊讶,因为这将有助于它的工作,但有人或某些文档必须断言。
【解决方案2】:

在最坏的情况下,如果没有存储有关输入的额外数据,您无法比 O(n) 算法做得更好 - 其中 n 是位数- 因为你需要检查数字中的每一位。

如果您可以跟踪例如“最右边的1”和“最左边的0”在之前的操作中,你可以通过检查它们是否确实是“10”来立即得到答案。

否则,您将不得不有效地遍历这些位以检查它是否正确。请注意,从左边开始遍历数字直到您点击 1,然后检查所有内容都是 0(使用适当的极端情况)是 O(n) 而拥有 O(n) 个可能的值并检查它是否等于(大概?) O(n) 比较中的任何一个值是 O(n^2)因此是个坏主意。

【讨论】:

    【解决方案3】:

    将二进制数据分成固定大小的块 ... 32 位 ... 64 位 -> 将它们视为无符号整数

    准备两个包含所有有效模式的哈希图,以及反向模式(以'0'开始,以'1'结束)......再次无符号整数

    现在测试最左边的块是否包含在逆模式哈希图中...如果不是 -> 模式无效
    现在测试最右边(非零)块是否包含在正常模式哈希图中...如果不是 -> 模式无效

    现在测试所有其他块是否等于所有位设置模式(应该是与无符号整数的比较)......如果所有都相等 -> 模式是有效的......否则......模式无效

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-23
      • 2017-08-21
      • 1970-01-01
      • 2012-11-25
      • 1970-01-01
      • 1970-01-01
      • 2012-08-02
      • 1970-01-01
      相关资源
      最近更新 更多