- आपने किसके लिए बिटवाइज़ ऑपरेशन का उपयोग किया है?
- वे इतना आसान क्यों हैं?
- किसी कृपया एक बहुत ही सरल ट्यूटोरियल की सिफारिश कर सकते हैं?
जवाबों:
हालांकि सभी को लगता है कि झंडे usecase पर झुके हुए हैं, लेकिन यह बिटवाइज़ ऑपरेटरों का एकमात्र अनुप्रयोग नहीं है (हालाँकि संभवतः सबसे आम)। इसके अलावा C # एक उच्च स्तर की भाषा है जिसे अन्य तकनीकों का उपयोग शायद ही कभी किया जाएगा, लेकिन यह अभी भी उन्हें जानने के लायक है। यहाँ मैं क्या सोच सकता हूँ:
<<
और >>
ऑपरेटरों जल्दी से गुणा 2. बेशक की शक्ति से, .NET JIT अनुकूलक शायद यह आप (और साथ ही किसी अन्य भाषा की किसी भी सभ्य संकलक) के लिए क्या कर सकते हैं होगा, लेकिन अगर आप वास्तव में हर माइक्रोसेकंड पर fretting रहे हैं, आप बस यह सुनिश्चित करने के लिए लिख सकते हैं।
इन ऑपरेटरों के लिए एक और आम उपयोग दो 16-बिट पूर्णांक को एक 32-बिट पूर्णांक में सामान करना है। पसंद:
int Result = (shortIntA << 16 ) | shortIntB;
यह Win32 फ़ंक्शंस के साथ सीधे इंटरफेसिंग के लिए आम है, जो कभी-कभी विरासत के कारणों के लिए इस ट्रिक का उपयोग करते हैं।
और, निश्चित रूप से, ये ऑपरेटर तब उपयोगी होते हैं जब आप अनुभवहीन को भ्रमित करना चाहते हैं, जैसे कि होमवर्क के प्रश्न का उत्तर प्रदान करते समय। :)
किसी भी वास्तविक कोड में, हालांकि आप इसके बजाय गुणा का उपयोग करके बहुत बेहतर होंगे, क्योंकि यह एक बहुत बेहतर पठनीयता है और JIT इसे किसी भी तरह के प्रदर्शन के दंड के रूप में वैसे भी निर्देश shl
और shr
निर्देश देता है।
^
ऑपरेटर (XOR) के साथ कुछ जिज्ञासु चालें व्यवहार करें । निम्न गुणों के कारण यह वास्तव में एक बहुत शक्तिशाली ऑपरेटर है:
A^B == B^A
A^B^A == B
A^B
तो यह बताना असंभव है कि क्या A
और क्या B
हैं, लेकिन यदि आप उनमें से एक को जानते हैं, तो आप दूसरे की गणना कर सकते हैं।कुछ चालें मैंने इस ऑपरेटर का उपयोग करके देखी हैं:
एक मध्यस्थ चर के बिना दो पूर्णांक चर को स्वैप करना:
A = A^B // A is now XOR of A and B
B = A^B // B is now the original A
A = A^B // A is now the original B
प्रति आइटम केवल एक अतिरिक्त चर के साथ संदेह से जुड़ी सूची। इसका C # में बहुत कम उपयोग होगा, लेकिन यह एंबेडेड सिस्टम की निम्न स्तर की प्रोग्रामिंग के लिए काम आ सकता है, जहाँ हर बाइट मायने रखती है।
विचार यह है कि आप पहले आइटम के लिए पॉइंटर का ट्रैक रखते हैं; अंतिम आइटम के लिए सूचक; और हर आइटम के लिए आप का ध्यान रखें pointer_to_previous ^ pointer_to_next
। इस तरह आप सूची को किसी भी छोर से पार कर सकते हैं, फिर भी ओवरहेड एक पारंपरिक लिंक्ड सूची का आधा हिस्सा है। यहां ट्रैवर्सिंग के लिए C ++ कोड दिया गया है:
ItemStruct *CurrentItem = FirstItem, *PreviousItem=NULL;
while ( CurrentItem != NULL )
{
// Work with CurrentItem->Data
ItemStruct *NextItem = CurrentItem->XorPointers ^ PreviousItem;
PreviousItem = CurrentItem;
CurrentItem = NextItem;
}
अंत से आगे बढ़ने के लिए आपको बस पहली पंक्ति को बदलने की जरूरत FirstItem
है LastItem
। वहीं एक और मेमोरी सेविंग है।
एक अन्य स्थान जहां मैं ^
सी # में नियमित आधार पर ऑपरेटर का उपयोग करता हूं , जब मुझे अपने प्रकार के लिए एक हैशकोड की गणना करनी होती है जो एक संयुक्त प्रकार है। पसंद:
class Person
{
string FirstName;
string LastName;
int Age;
public int override GetHashCode()
{
return (FirstName == null ? 0 : FirstName.GetHashCode()) ^
(LastName == null ? 0 : LastName.GetHashCode()) ^
Age.GetHashCode();
}
}
मैं अपने अनुप्रयोगों में सुरक्षा के लिए बिटवाइज़ ऑपरेटरों का उपयोग करता हूं। मैं एक Enum के अंदर विभिन्न स्तरों को संग्रहीत करूँगा:
[Flags]
public enum SecurityLevel
{
User = 1, // 0001
SuperUser = 2, // 0010
QuestionAdmin = 4, // 0100
AnswerAdmin = 8 // 1000
}
और फिर एक उपयोगकर्ता को उनके स्तर असाइन करें:
// Set User Permissions to 1010
//
// 0010
// | 1000
// ----
// 1010
User.Permissions = SecurityLevel.SuperUser | SecurityLevel.AnswerAdmin;
और फिर की जा रही कार्रवाई में अनुमतियों की जाँच करें:
// Check if the user has the required permission group
//
// 1010
// & 1000
// ----
// 1000
if( (User.Permissions & SecurityLevel.AnswerAdmin) == SecurityLevel.AnswerAdmin )
{
// Allowed
}
मुझे नहीं पता कि आप कितने सुडोकू हैं, इसे हल करना कितना व्यावहारिक है, लेकिन चलो मान लेते हैं।
कल्पना कीजिए कि आप एक सुडोकू सॉल्वर या सिर्फ एक साधारण कार्यक्रम लिखना चाहते हैं, जो आपको बोर्ड दिखाता है और आपको पहेली को स्वयं हल करने देता है, लेकिन यह सुनिश्चित करता है कि चालें कानूनी हैं।
बोर्ड को संभवतः दो-आयामी सरणी द्वारा दर्शाया जाएगा जैसे:
uint [, ] theBoard = new uint[9, 9];
मूल्य का 0
मतलब है कि सेल अभी भी खाली है और सीमा [1u, 9u] से मान बोर्ड में वास्तविक मूल्य हैं।
अब कल्पना करें कि आप जांचना चाहते हैं कि कुछ चाल कानूनी है या नहीं। जाहिर है आप इसे कुछ छोरों के साथ कर सकते हैं, लेकिन बिटकॉम्स आपको चीजों को बहुत तेज करने की अनुमति देते हैं। एक साधारण कार्यक्रम में जो यह सुनिश्चित करता है कि नियमों का पालन किया जाता है, इससे कोई फर्क नहीं पड़ता, लेकिन एक विलायक में यह हो सकता है।
आप बिटमास्क के सरणियों को बनाए रख सकते हैं, उन संख्याओं के बारे में जानकारी संग्रहीत करें जो पहले से ही प्रत्येक पंक्ति में डाली गई हैं, प्रत्येक कॉलम ए और प्रत्येक 3x3 बॉक्स।
uint [] maskForNumbersSetInRow = new uint[9];
uint [] maskForNumbersSetInCol = new uint[9];
uint [, ] maskForNumbersSetInBox = new uint[3, 3];
संख्या से बिटपटन तक की मैपिंग, उस नंबर सेट के अनुरूप एक बिट के साथ, बहुत सरल है
1 -> 00000000 00000000 00000000 00000001
2 -> 00000000 00000000 00000000 00000010
3 -> 00000000 00000000 00000000 00000100
...
9 -> 00000000 00000000 00000001 00000000
सी # में, आप इस तरह से bitpattern गणना कर सकता है ( value
एक है uint
):
uint bitpattern = 1u << (int)(value - 1u);
1u
बिटपटेन के अनुरूप ऊपर की लाइन में 00000000 00000000 00000000 00000001
बाईं ओर शिफ्ट किया जाता है value - 1
। यदि, उदाहरण के लिए value == 5
, आपको मिलता है
00000000 00000000 00000000 00010000
शुरुआत में, प्रत्येक पंक्ति, स्तंभ और बॉक्स के लिए मुखौटा है 0
। हर बार जब आप बोर्ड पर कुछ नंबर डालते हैं, तो आप मास्क को अपडेट करते हैं, इसलिए नए मूल्य के अनुरूप बिट सेट हो जाता है।
मान लें कि आप पंक्ति 3 में मान 5 डालें (पंक्तियाँ और स्तंभ 0 से क्रमांकित हैं)। पंक्ति 3 के लिए मास्क को स्टोर किया जाता है maskForNumbersSetInRow[3]
। चलो यह भी मानते हैं कि डालने से पहले {1, 2, 4, 7, 9}
पंक्ति में पहले से ही संख्याएं थीं 3. मुखौटा में थोड़ा पैटर्न maskForNumbersSetInRow[3]
इस तरह दिखता है:
00000000 00000000 00000001 01001011
bits above correspond to:9 7 4 21
लक्ष्य इस मास्क में मूल्य 5 के अनुरूप बिट सेट करना है। आप इसे बिटवाइस या ऑपरेटर ( |
) का उपयोग करके कर सकते हैं । पहले आप मान 5 के अनुरूप थोड़ा पैटर्न बनाते हैं
uint bitpattern = 1u << 4; // 1u << (int)(value - 1u)
और फिर आप operator |
मास्क में बिट सेट करने के लिए उपयोग करते हैंmaskForNumbersSetInRow[3]
maskForNumbersSetInRow[3] = maskForNumbersSetInRow[3] | bitpattern;
या छोटे रूप का उपयोग करना
maskForNumbersSetInRow[3] |= bitpattern;
00000000 00000000 00000001 01001011
|
00000000 00000000 00000000 00010000
=
00000000 00000000 00000001 01011011
अब आपका मुखौटा इंगित करता है कि {1, 2, 4, 5, 7, 9}
इस पंक्ति (पंक्ति 3) में मान हैं ।
यदि आप जांचना चाहते हैं, अगर कुछ मूल्य पंक्ति में है, तो आप यह operator &
जांचने के लिए उपयोग कर सकते हैं कि क्या संबंधित बिट मास्क में सेट है। यदि उस ऑपरेटर का परिणाम मुखौटा पर लागू होता है और उस मूल्य के अनुरूप एक सा पैटर्न, गैर-शून्य है, तो मूल्य पहले से ही पंक्ति में है। यदि परिणाम 0 है तो मान पंक्ति में नहीं है।
उदाहरण के लिए, यदि आप जाँचना चाहते हैं कि क्या मान 3 पंक्ति में है, तो आप इस तरह से कर सकते हैं:
uint bitpattern = 1u << 2; // 1u << (int)(value - 1u)
bool value3IsInRow = ((maskForNumbersSetInRow[3] & bitpattern) != 0);
00000000 00000000 00000001 01001011 // the mask
|
00000000 00000000 00000000 00000100 // bitpattern for the value 3
=
00000000 00000000 00000000 00000000 // the result is 0. value 3 is not in the row.
नीचे बोर्ड में एक नया मान सेट करने की विधियाँ हैं, उपयुक्त बिटकॉम्स को बनाए रखना और अगर कोई क़ानूनी है तो जाँच के लिए।
public void insertNewValue(int row, int col, uint value)
{
if(!isMoveLegal(row, col, value))
throw ...
theBoard[row, col] = value;
uint bitpattern = 1u << (int)(value - 1u);
maskForNumbersSetInRow[row] |= bitpattern;
maskForNumbersSetInCol[col] |= bitpattern;
int boxRowNumber = row / 3;
int boxColNumber = col / 3;
maskForNumbersSetInBox[boxRowNumber, boxColNumber] |= bitpattern;
}
मुखौटे होने पर, आप जाँच सकते हैं कि क्या इस तरह कानूनी है:
public bool isMoveLegal(int row, int col, uint value)
{
uint bitpattern = 1u << (int)(value - 1u);
int boxRowNumber = row / 3;
int boxColNumber = col / 3;
uint combinedMask = maskForNumbersSetInRow[row] | maskForNumbersSetInCol[col]
| maskForNumbersSetInBox[boxRowNumber, boxColNumber];
return ((theBoard[row, col] == 0) && ((combinedMask & bitpattern) == 0u);
}
दर्जनों बिटिंग उदाहरण यहां दिए गए हैं
कोड C में है, लेकिन आप इसे आसानी से C # में अनुकूलित कर सकते हैं
उनका उपयोग विभिन्न अनुप्रयोगों के पूरे भार के लिए किया जा सकता है, यहां एक प्रश्न है जो मैंने पहले यहां पोस्ट किया है, जो बिटवाइज़ ऑपरेशन का उपयोग करता है:
बिटवाइंड एंड, बिटवाइज इनक्लूसिव या प्रश्न, जावा में
अन्य उदाहरणों के लिए, एक नज़र (कहे) पर ध्यान दें।
मेरे उदाहरण में, मैं एक द्विआधारी संख्या की सीमा को -128 ... 127 से 0..255 तक बदलने के लिए बिटवाइज़ ऑपरेशंस का उपयोग कर रहा था (यह हस्ताक्षरित से हस्ताक्षर किए बिना बदल रहा है)।
एमएसएन लेख यहां ->
http://msdn.microsoft.com/en-us/library/6a71f45d%28VS.71%29.aspx
उपयोगी है।
और, हालांकि यह लिंक:
बहुत तकनीकी है, यह सब कुछ कवर कर रहा है।
HTH
मुझे कहना होगा कि डेटा को संपीड़ित करने के लिए सबसे आम उपयोग बिटफ़िल्ड को संशोधित करना है। आप इसे ज्यादातर पैकेट वाले किफायती बनाने के कार्यक्रमों में देखते हैं।
का उदाहरण नेटवर्क संपीड़न bitfields का उपयोग कर
C # के लिए मैं सबसे ज्यादा इस्तेमाल की जाने वाली चीजों में से एक हैशकोड का उत्पादन कर रहा हूं। कुछ हद तक अच्छे हैशिंग तरीके हैं जो उनका उपयोग करते हैं। उदाहरण के लिए, एक X Y के साथ सह-समन्वित वर्ग के लिए जो मेरे द्वारा उपयोग किए जाने वाले दोनों प्रकार के थे:
public override int GetHashCode()
{
return x ^ ((y << 16) | y >> 16);
}
यह जल्दी से एक संख्या उत्पन्न करता है जो समान वस्तु द्वारा उत्पादित होने पर समान होने की गारंटी देता है (यह मानते हुए कि समानता का अर्थ है कि दोनों वस्तुओं की तुलना में X और Y दोनों पैरामीटर समान हैं) जबकि निम्न-मूल्यवान वस्तुओं के लिए क्लैशिंग पैटर्न का उत्पादन नहीं किया जा सकता है (होने की संभावना है) अधिकांश अनुप्रयोगों में सबसे आम)।
एक अन्य ध्वज गणना है। उदाहरण के लिएRegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
कुछ निम्न-स्तर के ऑपरेशन हैं जो आमतौर पर आवश्यक नहीं होते हैं जब आप .NET जैसे फ्रेमवर्क के खिलाफ कोडिंग करते हैं (जैसे C # में मुझे UTF-8 को UTF-16 में बदलने के लिए कोड लिखने की आवश्यकता नहीं होगी, यह मेरे लिए है रूपरेखा), लेकिन निश्चित रूप से किसी को उस कोड को लिखना था।
कुछ बिट-ट्विडलिंग तकनीक हैं, जैसे निकटतम बाइनरी नंबर को गोल करना (जैसे 1010 से 10000 तक गोल):
unchecked
{
--x;
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return ++x;
}
जब आपको उनकी आवश्यकता होती है तो वे उपयोगी होते हैं, लेकिन यह बहुत सामान्य नहीं होता है।
अंत में, आप उन्हें गणित को माइक्रो-ऑप्टिमाइज़ करने के लिए भी उपयोग कर सकते हैं जैसे कि << 1
इसके बजाय, * 2
लेकिन मैं इसमें केवल यह कहना चाहता हूं कि यह आमतौर पर एक बुरा विचार है क्योंकि यह वास्तविक कोड के इरादे को छुपाता है, प्रदर्शन में लगभग कुछ भी नहीं बचाता है, और कुछ सूक्ष्म कीड़े छिपा सकता है ।
आप विभिन्न कारणों से उनका उपयोग करेंगे:
मुझे यकीन है कि आप दूसरों के बारे में सोच सकते हैं।
कहा जा रहा है, कभी-कभी आपको खुद से पूछने की जरूरत है: क्या स्मृति और प्रदर्शन को बढ़ावा देने लायक प्रयास है। उस तरह के कोड को लिखने के बाद, उसे थोड़ी देर के लिए आराम करने दें और वापस आ जाएं। यदि आप इसके साथ संघर्ष करते हैं, तो अधिक रखरखाव योग्य कोड के साथ फिर से लिखें।
दूसरी ओर, कभी-कभी यह बिटवाइज़ ऑपरेशंस (थिंक क्रिप्टोग्राफी) का उपयोग करने के लिए सही समझ में आता है।
बेहतर अभी तक, यह किसी और ने पढ़ा है, और बड़े पैमाने पर दस्तावेज़।
खेल!
वापस दिनों में, मैंने इसका इस्तेमाल एक रिवर्सी खिलाड़ी के टुकड़ों का प्रतिनिधित्व करने के लिए किया। यह 8X8 है, इसलिए इसने मुझे एक long
प्रकार का लिया , और उदाहरण के लिए, यदि आप जानना चाहते हैं कि बोर्ड पर सभी टुकड़े कहाँ हैं - तो आप or
दोनों खिलाड़ी टुकड़े करते हैं।
यदि आप किसी खिलाड़ी के सभी संभव कदम चाहते हैं, तो दाईं ओर कहें - आप >>
खिलाड़ी के टुकड़ों को एक-एक करके दर्शाते हैं, और AND
यह जांचने के लिए प्रतिद्वंद्वी के टुकड़ों के साथ कि क्या अब आम 1 हैं (इसका मतलब है कि आपके दाईं ओर प्रतिद्वंद्वी टुकड़ा है)। फिर तुम वही करते रहना। यदि आप अपने टुकड़ों में वापस आते हैं - कोई चाल नहीं। यदि आपको एक स्पष्ट बिट मिलता है - आप वहां जा सकते हैं, और रास्ते में सभी टुकड़ों पर कब्जा कर सकते हैं।
(यह तकनीक मोटे तौर पर शतरंज सहित कई प्रकार के बोर्ड गेम में उपयोग की जाती है)