[英]Fast way to find a intersection between two sets of numbers, one defined by a bitwise condition and another by an arithmetic condition
這可能是很好的覆蓋,但我對這個問題一無所知,所以我將使用業余術語。 假設我正在搞亂一些條件,每個條件都在int中定義一組非零數字,我們只說8位int。 所以對於一個按位,我可能有這個:
11XX00XX
說我希望所有字節都有1s,其中有1s,0s有0,並且不關心Xs。 所以11110000或11000010實現了這一點,但01110000卻沒有。 夠容易吧? 對於算術條件,我只能想象使用==,!=,> =,>,<=或<與常數進行比較。 所以我可以說:
X > 16
所以任何大於16的數字(00010000)。 如果我想查找上述示例集中的所有數字,該怎么辦? 我可以通過觀察它看出任何以100XX結尾的數字都符合要求,因此交叉點的按位部分包括11X100XX。 然后我必須包括區域111X00XX以填充其上方的其余范圍。 對? 雖然我認為對於其他情況,它不會如此整潔,正確嗎? 無論如何,對於任何這些算術條件與任何可能的那些按位算法相比,這背后的算法是什么。 當然必須有一般情況!
假設有一個,它可能是顯而易見的,如果事情變得更復雜怎么辦? 如果我的按位要求變為:
11AA00XX
標有A的任何東西必須相同。 所以110000XX或111100XX,但不是111000XX。 對於任意數量和任何位置的任意數量的相同位“類型”(A,B,C等),通過某種算術比較求解交點的最佳方法是什么? 有嗎?
我正在考慮這些按位運算是一種單一的比較運算/分支,就像算法的設置一樣。 所以也許一個是所有常數,當某個字節B 01110000與它們進行AND運算時,會產生00110000.因此,常數區域,即我的“按位條件”,將是X011XXXX,因為X011XXXX AND 01110000 = 00110000我所有的“按位條件”都是通過像AND,OR和XOR這樣的操作的反轉形成的。 不確定是否會包含像NAND這樣的東西。 這可能會限制實際可能的條件,也許? 如果是這樣,那么我不關心那些類型的條件。
很抱歉蜿蜒的嘗試解釋。 我正在做什么名字? 看起來它在CS中已經很好用了,所以一個名字可以讓我對這個主題進行一些很好的閱讀。 但我主要只是尋找一個好的算法來解決這個問題。 我最終會在十字路口使用兩個以上的東西(可能有幾十個或更多),所以解決它的方法可以很好地擴展。
好的,所以我們看一下按位操作,因為這是做你想要的最有效的方法。 為清楚起見(和參考),轉換為十進制的按位值是
00000001 = 1
00000010 = 2
00000100 = 4
00001000 = 8
00010000 = 16
00100000 = 32
01000000 = 64
10000000 = 128
現在,給定變量x
上的11XX00XX
位模式,我們將執行以下檢查:
x AND 128 == true
x AND 64 == true
x AND 8 == false
x AND 4 == false
如果所有這些條件都為真,則該值與模式匹配。 基本上,您正在檢查以下條件:
1XXXXXXX AND 10000000 == true
X1XXXXXX AND 01000000 == true
XXXX0XXX AND 00001000 == false
XXXXX0XX AND 00000100 == false
把它放在編程語言的說法中(我將使用C#),你會尋找
if ((x && 128) && (x && 64) && !(x && 8) && !(x && 4))
{
// We have a match
}
對於更復雜的11AA00XX位模式,您將添加以下條件:
NOT ((x AND 32) XOR (x AND 16)) == true
這樣做首先檢查x AND 32
,根據x
中該位的值返回0或1。 然后,它對另一個位x AND 16
進行相同的檢查。 XOR
操作檢查位的差異,如果位是不同的則返回1,如果位相同則返回0。 從那里開始,因為我們想要返回1,如果它們是相同的,我們NOT
整個條款。 如果位相同,則返回1。
在算術方面,您將看到使用除法和模運算的組合來隔離有問題的位。 要與除法一起工作,首先要找到數字可以除以的最大二次冪。 換句話說,如果你有x=65
,那么2的最高功率是64。
完成除法之后,然后使用模數來取除除法后的余數。 如上例所示,給定x=65
, x MOD 64 == 1
。 使用該數字,您可以重復之前的操作,找到最高的2的冪,並繼續直到模數返回0。
在saluce的答案上稍微擴展一下:
比特測試
您可以構建測試模式,因此您不需要單獨檢查每個位(測試整數比測試一位一次更快,特別是一次性測試整個數字就像好):
testOnes = 128 & 64 // the bits we want to be 1
testZeros = ~(8 & 4) // the bits we want to be 0, inverted
然后以這種方式執行測試:
if (!(~(x & testOnes) & testOnes) &&
!(~(~x | testZeros))) {
/* match! */
}
邏輯解釋說 :
首先,在testOnes
和testZeros
您將感興趣的位設置為1,其余為0。
testOnes testZeros
11000000 11110011
然后x & testOnes
將把我們不想測試的所有位清零為零(注意&
和&&
之間的區別: &
按位執行邏輯AND
運算,而&&
是整數的邏輯AND
)。
testOnes
11000000
x x & testOnes
11110000 11000000
11000010 11000000
01010100 01000000
現在最多我們測試為1的位可以是1,但是我們不知道它們是否都是1:通過反轉結果( ~(x & testOnes)
),我們得到所有我們不關心的數字關於1和我們想要測試的位是0(如果它們是1)或1(如果它們是0)。
testOnes
11000000
x ~(x & testOnes)
11110000 00111111
11000010 00111111
01010100 10111111
通過bitwise- AND
用-ing它testOnes
我們得到0,如果位,在利益均全1 x
,和非否則為零。
testOnes
11000000
x ~(x & testOnes) & testOnes
11110000 00000000
11000010 00000000
01010100 10000000
此時我們有:0如果我們想要測試1的所有位實際上都是1,否則非0,所以我們執行邏輯NOT
以將0變為true
而將非0變為false
。
x !(~(x & testOnes) & testOnes)
11110000 true
11000010 true
01010100 false
零位測試類似,但我們需要使用按位 - OR
( |
),而不是按位 - AND
( &
)。 首先,我們翻轉x
,所以should-be-0位變為should-be-1,然后OR
-ing將所有非興趣位變為1,同時保持其余位; 所以在這一點上,如果x
中的0位實際上是0,那么我們有全1,而非全1,否則,所以我們再次翻轉結果,在第一種情況下獲得0而在非0中第二。 然后我們應用邏輯NOT
( !
)將結果轉換為true
(第一種情況)或false
(第二種情況)。
testZeros
11110011
x ~x ~x | testZeros ~(~x | testZeros) !(~(~x | testZeros))
11110000 00001111 11111111 00000000 true
11000010 00111101 11111111 00000000 true
01010100 10101011 11111011 00000100 false
注意:您需要意識到我們已經為每個測試執行了4次操作,因此共計8次。 根據您要測試的位數,這可能仍然小於單獨檢查每個位。
算術測試
對等式/差異的測試很容易:對測試的數字進行XOR
- 如果所有位相等則得到0(因此數字相等),如果至少有一位不同則得到0(因此數字不同) 。 (應用NOT
將相等的測試結果設為true
,將差異設為false
。)
然而,為了測試不平等,大多數時候你都不幸運,至少它適用於邏輯運算。 你是正確的,檢查的權力,以2的(例如,在你的問題16),可與邏輯運算來完成(bitwise- AND
和測試0),但對於某些數字不是掌權者的-2,這是不是太簡單。 例如,讓我們測試x>5
:模式是00000101,那么你如何測試? 如果它在第5個最高有效位中具有1,則該數字更大,但是6(00000110)也更大,所有前5位為0。
您可以做的最好的事情是測試該數字是否至少是該數字中2的最大功率的兩倍(上例中為4為5)。 如果是,那么它比原來大; 否則,它必須至少與數字中最高2的冪一樣多,然后對不太重要的位執行相同的測試。 如您所見,根據測試編號中的1位數,操作數是動態的。
鏈接位
在這里, XOR
是你的朋友:對於兩位XOR
如果相同則產生0,否則為1。
我不知道執行測試的簡單方法,但以下算法應該有所幫助:
假設您需要位b1
,..., bn
相同(全1或全0),然后將所有其他位清零(參見邏輯 - AND
上面的),然后隔離測試模式中的每個位,然后排列它們在相同的位置(為方便起見,讓它成為最不重要的位置)。 然后對它們中的兩個進行XOR
,然后對結果等的第三個進行XOR
,將在每個奇數步驟產生偶數,如果原始數字中的位相同,則在每個偶數步驟產生奇數。 您將需要在每個步驟進行測試,因為測試只有最終結果可能不正確,因為大量的待測鏈接位。
testLinks
00110000
x x & testLinks
11110000 00110000
11000010 00000000
01010100 00010000
x x's bits isolated isolated bits shifted
11110000 00100000 00000001
00010000 00000001
11000010 00000000 00000000
00000000 00000000
01010100 00000000 00000000
00010000 00000001
x x's bits XOR'd result
11110000 00000000 true (1st round, even result)
11000010 00000000 true (1st round, even result)
01010100 00000001 false (1st round, odd result)
注意:在類C語言中, XOR
運算符是^
。
注意:如何將位排到同一位置? 比特移位。 Shift-left( <<
)將所有位向左移位,“丟失”最高有效位並將“0”引入最低有效位,基本上將該數字乘以2; shift-right( >>
)操作類似,向右移位,基本上整數除以2,但是,它將相同的位“引入”到已存在的最高位(因此保持負數)負)。
TLDR saluce的答案:
按位檢查分別考慮各個位,算術檢查將所有位一起考慮。 確實如此,它們與2的冪相吻合,但不適用於任意數字。
因此,如果您同時擁有這兩者,則需要實施兩組檢查。
32位int的所有可能值的空間有點大,因此您每次都必須檢查它們。 只需確保使用短路來消除重復檢查,例如x> 5 || x> 3。
您已經定義了一個適合指定掩碼的DSL。 我會寫一個解析器來讀取該掩碼並執行特定於每個唯一字符的操作。
AABBB110 =面具
步驟1:將所有唯一字符提取到數組[01AB]中。 您可以省略'X',因為不需要任何操作。
第2步:遍歷該數組,將文本掩碼處理為單獨的位掩碼,每個唯一字符對應一個,將該字符位置的位替換為1,將所有其他位替換為0。
Mask_0 = 00000001 = 0x01
Mask_1 = 00000110 = 0x06
Mask_A = 11000000 = 0xC0
Mask_B = 00111000 = 0x38
步驟3:將每個掩模傳遞到下面定義的適當功能。
boolean mask_zero(byte data, byte mask) {
return (data & mask) == 0;
}
boolean mask_one(byte data, byte mask) {
return (data & mask) == mask;
}
boolean mask_same(byte data, byte mask) {
byte masked=data & mask;
return (masked==0) || (masked==mask);
}
您希望結果集的格式是什么? 算術集(讓我們稱之為A)和按位集(讓我們稱之為B)都具有可快速測試的優點,以及易於迭代的優點。 但是這些定義中的每一種都可以定義另一種不能定義的東西,因此它們的交集需要完全不同。
我要做的是分別處理測試和迭代。 通過簡單地使用邏輯“和”,可以通過將兩個集合轉換為任意數學表達式(按位集合可以轉換為幾個按位操作,如其他海報所描述的)來輕松創建易於測試的定義。 這很容易推廣到任何類型的集合 - 只是存儲對兩個父集合的引用,當被問及兩個集合中是否存在數字時,只需檢查兩個父集合。
但是,任意數學表達式都不容易迭代。 對於迭代,最簡單的方法是迭代集合B(可以通過僅更改不受集合約束的位來完成),並允許集合A約束結果。 如果A使用>或> =,則向下迭代(從最大數字開始)並在false時停止以獲得最大效率; 如果A使用<或<=,則迭代(從最小數字開始)並停止為false。 如果A使用==,那么只有一個要檢查的數字,如果A使用!=,那么任何一個方向都可以(但你不能停止為假)。
請注意,按位集的行為類似於可索引的數字數組 - 例如,11XX00XX定義的按位集可視為索引范圍為0000到1111的數組,索引的位適合相應的插槽。 這使得在集合上向上或向下迭代變得容易。 集合A可以以類似的方式編制索引,但由於它可以很容易地成為無限集(除非受機器的int值約束,盡管它不必如此,即BigInteger),迭代不是最安全的事情。過度。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.