C#中的二进制模式比较快捷方式/最快实现

时间:2012-08-07 12:47:31

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

我需要检查特定位序列的给定字节或字节序列,如下所示:

  • 可以从零个或多个0s开始。
  • 可以从零个或多个1s开始。
  • 最后必须至少包含一个0

换句话说,如果字节值不是0,那么我们只对包含连续1s后跟至少一个0的值感兴趣。< / p>

我编写了以下代码来做到这一点,但希望确保它高度优化。我觉得if分支中的多个检查可以优化但不确定如何。请指教。

// The parameter [number] will NEVER be negative.
public static bool ConformsToPattern (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] == 1) // 00000001
                        || (bytes [b] == 3) // 00000011
                        || (bytes [b] == 7) // 00000111
                        || (bytes [b] == 15) // 00001111
                        || (bytes [b] == 31) // 00011111
                        || (bytes [b] == 63) // 00111111
                        || (bytes [b] == 127) // 01111111
                        || (bytes [b] == 255) // 11111111
                    )
                    {
                        // So far so good. Continue to the next byte with
                        // a possibility of more consecutive 1s.
                    }
                    else if
                    (
                        (bytes [b] == 128) // 10000000
                        || (bytes [b] == 192) // 11000000
                        || (bytes [b] == 224) // 11100000
                        || (bytes [b] == 240) // 11110000
                        || (bytes [b] == 248) // 11111000
                        || (bytes [b] == 252) // 11111100
                        || (bytes [b] == 254) // 11111110
                    )
                    {
                        moreOnesPossible = false;
                    }
                    else
                    {
                        return (false);
                    }
                }
                else
                {
                    if (bytes [b] > 0)
                    {
                        return (false);
                    }
                }
            }
        }
    }

    return (true);
}

重要事项:发送给该函数的参数[number]将 NEVER 为负数,因此无需检查符号位。

6 个答案:

答案 0 :(得分:2)

我要说这些答案都不是

00000010
00000110
00001110
00011110
00111110
01111110    
00000100
00001100
00011100
00111100
01111100    
等等,等等。

这是我的字节数组方法:

public static bool ConformsToPattern(System.Numerics.BigInteger number)
{
    bool foundStart = false, foundEnd = false;
    int startPosition, stopPosition, increment;

    if (number.IsZero || number.IsPowerOfTwo)
        return true;
    if (!number.IsEven)            
        return false;
    byte[] bytes = number.ToByteArray();

    if(BitConverter.IsLittleEndian)
    {
        startPosition = 0;
        stopPosition = bytes.Length;
        increment = 1;
    }
    else
    {
        startPosition = bytes.Length - 1;
        stopPosition = -1;
        increment = -1;
    }

    for(int i = startPosition; i != stopPosition; i += increment)
    {
        byte n = bytes[i];      
        for(int shiftCount = 0; shiftCount < 8; shiftCount++)       
        {           
            if (!foundEnd)
            {
                if ((n & 1) == 1)
                    foundEnd = true;
                n = (byte)(n >> 1);
                continue;      
            }
            if (!foundStart)
            {
                if ((n & 1) == 0)   
                    foundStart = true;
                n = (byte)(n >> 1);
                continue;                
            }
            if (n == 0)
                continue;
            return false;
        }
    }
    if (foundEnd)
        return true;
    return false;
}

这是我的BigInteger方法:

public static bool ConformsToPattern(System.Numerics.BigInteger number)
{
    bool foundStart = false;
    bool foundEnd = false;
    if (number.IsZero || number.IsPowerOfTwo)
        return true;
    if (!number.IsEven)            
        return false;
    while (!number.IsZero)
    {
        if (!foundEnd)
        {
            if (!number.IsEven)
                foundEnd = true;
            number = number >> 1;
            continue;                
        }
        if (!foundStart)
        {
            if (number.IsEven)                
                foundStart = true;
            number = number >> 1;
            continue;                
        }
        return false;
    }
    if (foundEnd)
        return true;
    return false;
}

选择更适合您的方式。到目前为止,字节数组更快。 BigIntegers代码是100%准确的参考。

如果您不担心本机字节序删除该部分代码,但将其保留在那里将确保除x86系统之外的可移植性。 BigIntegers已经为我提供了IsZeroIsEvenIsPowerOfTwo,因此这不是额外的计算。我不确定这是否是最快的移位方式,因为有byteint演员,但是现在,我找不到另一种方式。至于使用byte vs short vs int vs long进行循环操作,如果您认为它会更好地工作,那么您可以自行更改。我不确定你会发送什么样的BigIntegers所以我认为int是安全的。您可以修改代码以删除for loop,只需复制粘贴代码8次,它可能会更快。或者你可以把它扔进静态方法。

答案 1 :(得分:1)

这样的事情怎么样?如果找到一个,那么之后的唯一事情就是1秒,直到找到0。在那之后,只有0。这看起来它会更快一点,因为它没有做不必要的或条件。

// The parameter [number] will NEVER be negative.
public static bool ConformsToPattern (System.Numerics.BigInteger number)
{
    byte [] bytes = null;
    bool moreOnesPossible = true;
    bool foundFirstOne = false;

    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(!foundFirstOne)
                    {
                        if
                        (
                            (bytes [b] == 1) // 00000001
                            || (bytes [b] == 3) // 00000011
                            || (bytes [b] == 7) // 00000111
                            || (bytes [b] == 15) // 00001111
                            || (bytes [b] == 31) // 00011111
                            || (bytes [b] == 63) // 00111111
                            || (bytes [b] == 127) // 01111111
                            || (bytes [b] == 255) // 11111111
                        )
                        {
                            foundFirstOne = true;
                            // So far so good. Continue to the next byte with
                            // a possibility of more consecutive 1s.
                        }
                        else if
                        (
                            (bytes [b] == 128) // 10000000
                            || (bytes [b] == 192) // 11000000
                            || (bytes [b] == 224) // 11100000
                            || (bytes [b] == 240) // 11110000
                            || (bytes [b] == 248) // 11111000
                            || (bytes [b] == 252) // 11111100
                            || (bytes [b] == 254) // 11111110
                        )
                        {
                            moreOnesPossible = false;
                        }
                        else
                        {
                            return (false);
                        }
                    }
                    else
                    {
                        if(bytes [b] != 255) // 11111111
                        {
                            if
                            (
                                (bytes [b] == 128) // 10000000
                                || (bytes [b] == 192) // 11000000
                                    || (bytes [b] == 224) // 11100000
                                || (bytes [b] == 240) // 11110000
                                || (bytes [b] == 248) // 11111000
                                || (bytes [b] == 252) // 11111100
                                || (bytes [b] == 254) // 11111110
                            )
                            {
                                moreOnesPossible = false;
                            }
                        }                            
                    }
                }
                else
                {
                    if (bytes [b] > 0)
                    {
                        return (false);
                    }
                }
            }
        }
    }

    return (true);
}

答案 2 :(得分:1)

这是我自己写的方法。不是很优雅但很快。

/// <summary>
/// Checks to see if this cell lies on a major diagonal of a power of 2.
/// ^[0]*[1]*[0]+$ denotes the regular expression of the binary pattern we are looking for.
/// </summary>
public bool IsDiagonalMajorToPowerOfTwo ()
{
    byte [] bytes = null;
    bool moreOnesPossible = true;
    System.Numerics.BigInteger number = 0;

    number = System.Numerics.BigInteger.Abs(this.X - this.Y);

    if ((number == 0) || (number == 1)) // 00000000
    {
        return (true); // All bits are zero.
    }
    else
    {
        // The last bit should always be 0.
        if (number.IsEven)
        {
            bytes = number.ToByteArray();

            for (byte b=0; b < bytes.Length; b++)
            {
                if (moreOnesPossible)
                {
                    switch (bytes [b])
                    {
                        case 001: // 00000001
                        case 003: // 00000011
                        case 007: // 00000111
                        case 015: // 00001111
                        case 031: // 00011111
                        case 063: // 00111111
                        case 127: // 01111111
                        case 255: // 11111111
                        {
                            // So far so good.
                            // Carry on testing subsequent bytes.

                            break;
                        }
                        case 128: // 10000000
                        case 064: // 01000000
                        case 032: // 00100000
                        case 016: // 00010000
                        case 008: // 00001000
                        case 004: // 00000100
                        case 002: // 00000010

                        case 192: // 11000000
                        case 096: // 01100000
                        case 048: // 00110000
                        case 024: // 00011000
                        case 012: // 00001100
                        case 006: // 00000110

                        case 224: // 11100000
                        case 112: // 01110000
                        case 056: // 00111000
                        case 028: // 00011100
                        case 014: // 00001110

                        case 240: // 11110000
                        case 120: // 01111000
                        case 060: // 00111100
                        case 030: // 00011110

                        case 248: // 11111000
                        case 124: // 01111100
                        case 062: // 00111110

                        case 252: // 11111100
                        case 126: // 01111110

                        case 254: // 11111110
                        {
                            moreOnesPossible = false;

                            break;
                        }
                        default:
                        {
                            return (false);
                        }
                    }
                }
                else
                {
                    if (bytes [b] > 0)
                    {
                        return (false);
                    }
                }
            }
        }
        else
        {
            return (false);
        }
    }

    return (true);
}

答案 3 :(得分:0)

我是正则表达式的忠实粉丝,所以我想过简单地将字节转换为字符串并针对正则表达式进行测试。但是,仔细定义模式很重要。通过阅读你的问题,我想出了这个问题:

^(?:1*)(?:0+)$

请检查出来:

    public static bool ConformsToPattern(System.Numerics.BigInteger number)
    {
        byte[] ByteArray = number.ToByteArray();

        Regex BinaryRegex = new Regex("^(?:1*)(?:0+)$", RegexOptions.Compiled);

        return ByteArray.Where<byte>(x => !BinaryRegex.IsMatch(Convert.ToString(x, 2))).Count() > 0;
    }

答案 4 :(得分:0)

如果我理解正确,你必须只有1个连续的1系列,然后是连续的零。

因此,如果必须以零结束,则必须是均匀的。 中间的所有字节必须全为1,第一个和最后一个字节是唯一的特殊情况。

         if (number.IsZero)
            return true;

         if (!number.IsEven)
            return false;


         var bytes = number.ToByteArray();

         for (int i = 0; i < bytes.Length; i++)
         {
            if (i == 0)  //first byte case
            {
               if (!(
                     (bytes[i] == 1) // 00000001 
                     || (bytes[i] == 3) // 00000011 
                     || (bytes[i] == 7) // 00000111 
                     || (bytes[i] == 15) // 00001111 
                     || (bytes[i] == 31) // 00011111 
                     || (bytes[i] == 63) // 00111111 
                     || (bytes[i] == 127) // 01111111 
                     || (bytes[i] == 255) // 11111111 
                    ))
               {
                  return false; 
               }
            }
            else if (i == bytes.Length) //last byte case
            {
               if (!(
                  (bytes[i] == 128) // 10000000 
                        || (bytes[i] == 192) // 11000000 
                        || (bytes[i] == 224) // 11100000 
                        || (bytes[i] == 240) // 11110000 
                        || (bytes[i] == 248) // 11111000 
                        || (bytes[i] == 252) // 11111100 
                        || (bytes[i] == 254) // 11111110 
                    ))
               {
                  return false;
               }
            }
            else //all bytes in the middle
            {
               if (bytes[i] != 255)
                  return false;
            }

         }

答案 5 :(得分:0)

不确定这是否会比你已经拥有的更快或更慢,但这是尝试的东西(希望我没有破坏逻辑)......

public bool ConformsToPattern(System.Numerics.BigInteger number) {
    bool moreOnesPossible = true;
    if (number == 0) {
        return true;
    }
    else {
        byte[] bytes = number.ToByteArray();
        if ((bytes[bytes.Length - 1] & 1) == 1) {
            return false;
        }
        else {
            for (byte b = 0; b < bytes.Length; b++) {
                if (moreOnesPossible) {
                    switch (bytes[b]) {
                        case 1:
                        case 3:
                        case 7:
                        case 15:
                        case 31:
                        case 63:
                        case 127:
                        case 255:
                            continue;
                        default:
                            switch (bytes[b]) {
                                case 128:
                                case 192:
                                case 224:
                                case 240:
                                case 248:
                                case 252:
                                case 254:
                                    moreOnesPossible = false;
                                    continue;
                                default:
                                    return false;
                            }
                    }
                }
                else {
                    if (bytes[b] > 0) { return (false); }
                }
            }
        }
    }
    return true;
}