簡體   English   中英

快速查找兩組數字之間的交集,一組由按位條件定義,另一組由算術條件定義

[英]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=65x 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! */
}

邏輯解釋說

首先,在testOnestestZeros您將感興趣的位設置為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM