Possible Duplicate:
Optimizing function that searches for binary patterns
I have two functions that calculate whether or not a given coordinate (x, y) is a diagonal or anti-diagonal of another coordinate that is a power of 2. In other words if (x+/-k, y+/-k) == (cx, cy) where cx or cy is a power of 2, then the binary representation of (x+/-k, y+/-k) follows known patterns.
- In case of major diagonals (\), the binary representation could contain
any number of 1sany number of consecutive 1s (could be none) and has to end with at least one 0. - In case of minor diagonals (/), the binary representation of the product contains at most two 1s (in any order, consecutive or not).
These functions are called on very large numbers that have around 5,000,000 bits and have become the most expensive call path. They end up taking 60% of the algorithm time and desperately need to be optimized.
Here are the functions.
/// <summary>
/// A look up array to match binary patterns for the numbers 0 to 255 inclusive.
/// Checks for the numbers: 000, 001, 003, 007, 015, 031, 063, 127, 255.
/// </summary>
public static readonly bool [] LookupArrayDiagonalMajorMoreOnesPossible = new bool [256];
/// <summary>
/// A look up array to match binary patterns for the numbers 0 to 255 inclusive.
/// Checks for the numbers: 000, 001, 003, 007, 015, 031, 063, 127, 255.
/// </summary>
public static readonly bool [] LookupArrayDiagonalMajorIsValidByteBeforeEncounter = new bool [256];
/// <summary>
/// A look up array to match binary patterns for the numbers 0 to 255 inclusive.
/// Checks for the numbers: 000, 001, 003, 007, 015, 031, 063, 127, 255.
/// </summary>
public static readonly bool [] LookupArrayDiagonalMajorIsValidByteAfterEncounter = new bool [256];
/// <summary>
/// A look up array of bit counts for the numbers 0 to 255 inclusive.
/// Declared static for performance.
/// </summary>
public static readonly byte [] LookupArrayBitCount = new byte []
{
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};
/// <summary>
/// Static constructor.
/// </summary>
static Global ()
{
LookupArrayDiagonalMajorMoreOnesPossible = new bool [256];
for (int i=0; i < LookupArrayDiagonalMajorMoreOnesPossible.Length; i++)
LookupArrayDiagonalMajorMoreOnesPossible [i] = false;
LookupArrayDiagonalMajorMoreOnesPossible [000 /* 00000000 */] =
LookupArrayDiagonalMajorMoreOnesPossible [001 /* 00000001 */] =
LookupArrayDiagonalMajorMoreOnesPossible [003 /* 00000011 */] =
LookupArrayDiagonalMajorMoreOnesPossible [007 /* 00000111 */] =
LookupArrayDiagonalMajorMoreOnesPossible [015 /* 00001111 */] =
LookupArrayDiagonalMajorMoreOnesPossible [031 /* 00011111 */] =
LookupArrayDiagonalMajorMoreOnesPossible [063 /* 00111111 */] =
LookupArrayDiagonalMajorMoreOnesPossible [127 /* 01111111 */] =
LookupArrayDiagonalMajorMoreOnesPossible [255 /* 11111111 */] =
true;
LookupArrayDiagonalMajorIsValidByteBeforeEncounter = new bool [256];
for (int i=0; i < LookupArrayDiagonalMajorIsValidByteBeforeEncounter.Length; i++)
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [i] = false;
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [000 /* 00000000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [001 /* 00000001 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [002 /* 00000010 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [003 /* 00000011 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [004 /* 00000100 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [006 /* 00000110 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [007 /* 00000111 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [008 /* 00001000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [012 /* 00001100 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [014 /* 00001110 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [015 /* 00001111 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [016 /* 00010000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [024 /* 00011000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [028 /* 00011100 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [030 /* 00011110 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [031 /* 00011111 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [032 /* 00100000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [048 /* 00110000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [056 /* 00111000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [060 /* 00111100 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [062 /* 00111110 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [063 /* 00111111 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [064 /* 01000000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [096 /* 01100000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [112 /* 01110000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [120 /* 01111000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [124 /* 01111100 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [126 /* 01111110 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [127 /* 01111111 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [128 /* 10000000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [192 /* 11000000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [224 /* 11100000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [240 /* 11110000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [248 /* 11111000 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [252 /* 11111100 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [254 /* 11111110 */] =
LookupArrayDiagonalMajorIsValidByteBeforeEncounter [255 /* 11111111 */] =
true;
LookupArrayDiagonalMajorIsValidByteAfterEncounter = new bool [256];
for (int i=0; i < LookupArrayDiagonalMajorIsValidByteAfterEncounter.Length; i++)
LookupArrayDiagonalMajorIsValidByteAfterEncounter [i] = false;
LookupArrayDiagonalMajorIsValidByteAfterEncounter [000 /* 00000000 */] =
LookupArrayDiagonalMajorIsValidByteAfterEncounter [128 /* 10000000 */] =
LookupArrayDiagonalMajorIsValidByteAfterEncounter [192 /* 11000000 */] =
LookupArrayDiagonalMajorIsValidByteAfterEncounter [224 /* 11100000 */] =
LookupArrayDiagonalMajorIsValidByteAfterEncounter [240 /* 11110000 */] =
LookupArrayDiagonalMajorIsValidByteAfterEncounter [248 /* 11111000 */] =
LookupArrayDiagonalMajorIsValidByteAfterEncounter [252 /* 11111100 */] =
LookupArrayDiagonalMajorIsValidByteAfterEncounter [254 /* 11111110 */] =
LookupArrayDiagonalMajorIsValidByteAfterEncounter [255 /* 11111111 */] =
true;
}
/// <summary>
/// Checks to see if this cell lies on a major diagonal of a power of 2.
/// The binary representation could contain any number of consecutive 1s (could be none) and has to end with at least one 0.
/// ^[0]*[1]*[0]+$ denotes the regular expression of the binary pattern we are looking for.
/// </summary>
/// <param name="bytes">The expected number should be [Absolute(cell.X - cell.Y)].ToByteArray().</param>
/// <returns>[True] if [cell] lies on a power of 2 major diagonal. [False] otherwise.</returns>
public static bool IsDiagonalMajorToPowerOfTwo (byte [] bytes)
{
bool oneEncountered = false;
bool moreOnesPossible = true;
// The last bit should always be 0.
if ((((int) bytes [0]) & 1) == 1)
return (false);
for (int i=(bytes.Length - 1); i >= 0; i--)
{
if (oneEncountered)
{
if (moreOnesPossible)
{
if (LookupArrayDiagonalMajorIsValidByteAfterEncounter [bytes [i]])
{
if (bytes [i] == 0)
{
moreOnesPossible = false;
}
else
{
moreOnesPossible = LookupArrayDiagonalMajorMoreOnesPossible [bytes [i]];
}
}
else
{
return (false);
}
}
else
{
if (bytes [i] > 0)
{
return (false);
}
}
}
else
{
if (LookupArrayDiagonalMajorIsValidByteBeforeEncounter [bytes [i]])
{
moreOnesPossible = LookupArrayDiagonalMajorMoreOnesPossible [bytes [i]];
}
else
{
return (false);
}
oneEncountered = (bytes [i] > 0);
}
}
return (true);
}
/// <summary>
/// Checks to see if this cell lies on a minor diagonal of a power of 2.
/// The condition is met if the binary representation of the product contains
/// at most two 1s.
/// </summary>
public static bool IsDiagonalMinorToPowerOfTwoParallel (System.Numerics.BigInteger number)
{
int sum = 0;
byte [] bytes = null;
// 00000000 && 00000001
if ((number == 0) || (number == 1))
return (true);
// Contains only one 1.
if (number.IsPowerOfTwo)
return (false);
bytes = number.ToByteArray();
System.Threading.Tasks.Parallel.For<int>
(
0,
bytes.Length,
options, // MaxDegreeOfParallelism = Environment.ProcessorCount.
() => 0, // Initial value for [sumLocal].
(int i, System.Threading.Tasks.ParallelLoopState state, int sumLocal) =>
{
sumLocal += LookupArrayBitCount [bytes [i]];
if (sumLocal > 2)
{
state.Break();
}
return (sumLocal);
},
(int sumLocal) =>
{
System.Threading.Interlocked.Add(ref sum, sumLocal);
}
);
return (sum <= 2);
}
I've done all I can to optimize these functions and am now at a loss for ideas. By the way, I've also cached the byte []
for the BigInteger which is not reflected here.
NOTE: I'm not even sure if these binary patterns can be detected using some other more efficient algorithm. For those interested in context, I wrote these functions based on joriki's answer here.