寻找“逆天”常量
? 同事在研究LZ4 壓縮算法時候給我發來了一段代碼,看完了頓時表示非常震驚:
static const int[] MultiplyDeBruijnBitPosition = new int[32] {0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };/// <summary> /// Find the number of trailing zeros in 32-bit. /// </summary> /// <param name="v"></param> /// <returns></returns> static int GetMultiplyDeBruijnBitPosition(uint v) {return MultiplyDeBruijnBitPosition[((uint)((v & -v) * 0x077CB531U)) >> 27]; }下面依次解釋下這段代碼的意思:
假設變量v=123456, 那么其二進制表示形式為(...)11110001001000000, -v 在計算機中的二進制表示形式為(...)00001110111000000, 所以(v & -v) == 1000000, 十進制表示形式為64。
(v & -v) * 0x077CB531 的意思是將常量0x077CB531 向左移位6位(左移6位相當于乘64)。
((uint)(v & -v) * 0x077CB5310) >> 27 位的意思是繼續將上一步的結果向右移位27位,因為01串總長度是32位,向右移27位以后低位只剩下5個bits。
而0x077CB5310 的二進制表示形式為00000111011111001011010100110001, 所以上面的步驟相當于如下代碼:
static int GetMultiplyDeBruijnBitPosition(uint v) {return MultiplyDeBruijnBitPosition[27]; }根據上面的常量數組,可知當v 等于123456時,其(v & -v) 的二進制表示行為末尾含有6個0。
這個算法的用處目前看主要有兩種:
1. 快速計算log2(v & -v);
2. 任意給定兩個32-bit 的整型數組,對其中的數據進行異或運算,得到的值v, 采用如上算法判斷第幾位是不同的,從而用于壓縮算法。
?
? 以上是關于這個常量的簡要介紹,下面重點介紹下這個常量的特點:
1. 32-bit 長度;
2. 上一個5 bits 長度的01串的后四位是下一個01串的前四位,比如10001 的下一位是00010/00011;
3. 首尾是循環的;
根據以上3條規則,設計查找常量值算法代碼如下:
using System; using System.Collections.Generic;namespace Test {class Program{static List<string> deBruijnList = new List<string>();static List<string> deBruijnReserveList = new List<string>();static string[] flagArray = new string[] { "0", "1" };static readonly int DeBruijnLength = 5;static readonly double MaxDeBruijnListCount = Math.Pow(2, DeBruijnLength) - 4;static readonly uint ConstOne = 0x077CB531;static readonly uint ConstTwo = 0x0653ADF1;static void Init(){deBruijnReserveList.Add("00010");deBruijnReserveList.Add("00100");deBruijnReserveList.Add("01000");deBruijnReserveList.Add("10000");}static uint[] GetConstArray(uint constInt){//uint constInt = 0x077CB531;uint[] constArray = new uint[32];uint j = 0;for (int i = 0; i < constArray.Length; i++){j = (uint)((constInt << i)) >> 27;constArray[j] = (uint)i;}return constArray;}static const int[] MultiplyDeBruijnBitPosition = new int[32]{0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9};/// <summary>/// Find the number of trailing zeros in 32-bit./// </summary>/// <param name="v"></param>/// <returns></returns>static int GetMultiplyDeBruijnBitPosition(uint v){return MultiplyDeBruijnBitPosition[((uint)((v & -v) * 0x077CB531U)) >> 27];}static void GetDeBruijnKeyStr(){string deBruijnStr = "00000111011111001011010100110001";for (int i = 0; i < deBruijnStr.Length - DeBruijnLength; i++){Console.WriteLine(deBruijnStr.Substring(i, DeBruijnLength));}}static void GetDeBruijnKey(string currentKey){string currentKeysLast4ValueStr = currentKey.Substring(1);string nextKeyFormer4ValueStr = currentKeysLast4ValueStr;string nextKeyFlagZero = nextKeyFormer4ValueStr + "0";string nextKeyFlagOne = nextKeyFormer4ValueStr + "1";if (deBruijnList.Count == MaxDeBruijnListCount){return;}else if (deBruijnList.Count > MaxDeBruijnListCount){deBruijnList.Remove(currentKey);return;}if ((deBruijnList.Contains(nextKeyFlagZero) || deBruijnReserveList.Contains(nextKeyFlagZero))&& (deBruijnList.Contains(nextKeyFlagOne) || deBruijnReserveList.Contains(nextKeyFlagOne))){deBruijnList.Remove(currentKey);return;}if (!deBruijnList.Contains(nextKeyFlagZero) && !deBruijnReserveList.Contains(nextKeyFlagZero)){deBruijnList.Add(nextKeyFlagZero);GetDeBruijnKey(nextKeyFlagZero);}if (!deBruijnList.Contains(nextKeyFlagOne) && !deBruijnReserveList.Contains(nextKeyFlagOne)){deBruijnList.Add(nextKeyFlagOne);GetDeBruijnKey(nextKeyFlagOne);}//No new entry was added, so just remove the parent key.int lastIndexOfDeBruijnList = deBruijnList.Count - 1;if (deBruijnList[lastIndexOfDeBruijnList] == currentKey){deBruijnList.Remove(currentKey);}}static void Main(string[] args){Init();GetDeBruijnKey("00000");foreach (string deBruijnStr in deBruijnList){Console.WriteLine(deBruijnStr);}Console.ReadLine();}} }最后得到的新的“逆天”常量值為0x0653ADF1U, 根據常量可以得到常量數組,算法如下:
//ConstOne = 0x077CB531; //ConstOne = 0x0653ADF1; static uint[] GetConstArray(uint constInt) {//uint constInt = 0x077CB531;uint[] constArray = new uint[32];uint j = 0;for (int i = 0; i < constArray.Length; i++){j = (uint)((constInt << i)) >> 27;constArray[j] = (uint)i;}return constArray; }新的常量數組如下:
static const int[] MultiplyDeBruijnBitPosition2 = new int[32] {0, 1, 28, 2, 29, 7, 3, 12, 30, 10, 8, 17, 4, 19, 13, 22,31, 27, 6, 11, 9, 16, 18, 21, 26, 5, 15, 20, 25, 14, 24, 23 };?
由此可知,“逆天”常量并不止一個,歡迎大家參與研究、討論。
?
參考鏈接:http://www.matrix67.com/blog/archives/3985?
轉載于:https://www.cnblogs.com/danielWise/p/4378460.html
總結
- 上一篇: 视频目标跟踪算法综述
- 下一篇: htmlcss笔记--标签默认值样式重置