praktyczne zastosowania operacji bitowych [zamknięte]
- do czego użyłeś operacji bitowych? Dlaczego są tak poręczne?
- Czy ktoś może polecić bardzo prosty tutorial?
13 answers
Chociaż wszyscy wydają się być uzależnieni od flags usecase, to nie jest to jedyne zastosowanie operatorów bitowych (choć prawdopodobnie najbardziej powszechne). Również C# jest językiem na tyle wysokim poziomie, że inne techniki prawdopodobnie będą rzadko używane, ale nadal warto je znać. Oto co mogę wymyślić:
Operatory <<
i >>
mogą szybko mnożyć się przez moc 2. Oczywiście. NET JIT optimizer prawdopodobnie zrobi to za Ciebie (i każdy przyzwoity kompilator inny język, jak również), ale jeśli jesteś naprawdę zaniepokojony każdym mikrosekundy, po prostu może napisać to, aby mieć pewność.
Innym powszechnym zastosowaniem tych operatorów jest umieszczenie dwóch 16-bitowych liczb całkowitych w jedną 32-bitową liczbę całkowitą. Like:
int Result = (shortIntA << 16 ) | shortIntB;
Jest to powszechne w przypadku bezpośredniego połączenia z funkcjami Win32, które czasami używają tej sztuczki ze względów starszych.
I, oczywiście, te operatory są przydatne, gdy chcesz zmylić niedoświadczonych, jak podczas udzielania odpowiedzi na zadanie domowe. :)
W każdym prawdziwym kodzie, choć będzie znacznie lepiej używając mnożenie zamiast, ponieważ ma znacznie lepszą czytelność i JIT optymalizuje go do shl
i shr
instrukcje i tak nie ma kary wydajności.
Sporo ciekawych trików dotyczy operatora ^
(XOR). Jest to w rzeczywistości bardzo potężny operator, ze względu na następujące właściwości:
A^B == B^A
A^B^A == B
- jeśli wiesz
A^B
wtedy nie można powiedzieć, czym sąA
iB
, ale jeśli znasz jedno z nich, możesz obliczyć drugie. - operator nie cierpi na żadne przepełnienia, takie jak mnożenie/dzielenie/dodawanie / odejmowanie.
Kilka trików, które widziałem używając tego operatora:
Zamiana dwóch zmiennych całkowitych bez zmiennej pośredniczącej:
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
Lista Podwójnie połączona z jedną dodatkową zmienną na element. Będzie to miało niewielkie zastosowanie w C#, ale to może się przydać do niskopoziomowego programowania systemów wbudowanych, w których liczy się każdy bajt.
Chodzi o to, aby śledzić wskaźnik dla pierwszego elementu; wskaźnik dla ostatniego elementu; i dla każdego elementu, który śledzisz pointer_to_previous ^ pointer_to_next
. W ten sposób możesz przemierzać listę z dowolnego końca, ale narzut jest tylko o połowę mniejszy niż w przypadku tradycyjnej listy połączonej. Oto kod C++ do przejścia:
ItemStruct *CurrentItem = FirstItem, *PreviousItem=NULL;
while ( CurrentItem != NULL )
{
// Work with CurrentItem->Data
ItemStruct *NextItem = CurrentItem->XorPointers ^ PreviousItem;
PreviousItem = CurrentItem;
CurrentItem = NextItem;
}
Aby przejść od końca wystarczy zmienić pierwszą linię z FirstItem
do LastItem
. To kolejna oszczędność pamięci.
Innym miejscem, gdzie regularnie używam operatora ^
W C# jest sytuacja, gdy muszę obliczyć HashCode dla mojego typu, który jest typem kompozytowym. Like:
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();
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-15 20:08:44
Używam operatorów bitowych dla bezpieczeństwa w moich aplikacjach. Będę przechowywać różne poziomy w Enum:
[Flags]
public enum SecurityLevel
{
User = 1, // 0001
SuperUser = 2, // 0010
QuestionAdmin = 4, // 0100
AnswerAdmin = 8 // 1000
}
A następnie przypisać użytkownikowi ich poziomy:
// Set User Permissions to 1010
//
// 0010
// | 1000
// ----
// 1010
User.Permissions = SecurityLevel.SuperUser | SecurityLevel.AnswerAdmin;
A następnie sprawdź uprawnienia w wykonywanej akcji:
// Check if the user has the required permission group
//
// 1010
// & 1000
// ----
// 1000
if( (User.Permissions & SecurityLevel.AnswerAdmin) == SecurityLevel.AnswerAdmin )
{
// Allowed
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-24 13:56:17
Nie wiem, jak praktyczne jest rozwiązywanie sudoku, ale załóżmy, że tak.
Wyobraź sobie, że chcesz napisać sudoku solver lub nawet prosty program, który pokazuje planszę i pozwala rozwiązać zagadkę samodzielnie, ale zapewnia, że ruchy są legalne.
Sama tablica będzie najprawdopodobniej reprezentowana przez tablicę dwuwymiarową, taką jak:
uint [, ] theBoard = new uint[9, 9];
Wartość 0
oznacza, że komórka jest nadal pusta, a wartości z zakresu [1U, 9u] są rzeczywistymi wartościami w zarządzie.
Można utrzymywać tablice bitmasek, które przechowują informacje o liczbach, które są już wstawione w każdym wierszu, każdej kolumnie a i każdym polu 3x3.
uint [] maskForNumbersSetInRow = new uint[9];
uint [] maskForNumbersSetInCol = new uint[9];
uint [, ] maskForNumbersSetInBox = new uint[3, 3];
Odwzorowanie od liczby do bitpattern, z jednym bitem odpowiadającym temu zestawowi liczb, jest bardzo prosty
1 -> 00000000 00000000 00000000 00000001
2 -> 00000000 00000000 00000000 00000010
3 -> 00000000 00000000 00000000 00000100
...
9 -> 00000000 00000000 00000001 00000000
W C# można obliczyć bitpattern w ten sposób (value
jest uint
):
uint bitpattern = 1u << (int)(value - 1u);
W linii powyżej 1u
odpowiadającej bitpattern 00000000 00000000 00000000 00000001
jest przesunięty w lewo o value - 1
. Jeśli na przykład value == 5
, otrzymujesz
00000000 00000000 00000000 00010000
Na początku maską dla każdego wiersza, kolumny i pola jest 0
. Za każdym razem, gdy umieścisz jakiś numer na tablicy, aktualizujesz maskę, więc bit odpowiadający do nowej wartości jest ustawiona.
Załóżmy, że wstawiasz wartość 5 w wierszu 3 (wiersze i kolumny są ponumerowane od 0). Maska dla wiersza 3 jest przechowywana w maskForNumbersSetInRow[3]
. Załóżmy również, że przed wstawką były już liczby {1, 2, 4, 7, 9}
w wierszu 3. Wzór bitowy w masce maskForNumbersSetInRow[3]
wygląda następująco:
00000000 00000000 00000001 01001011
bits above correspond to:9 7 4 21
Celem jest ustawienie bitu odpowiadającego wartości 5 w tej masce. Można to zrobić za pomocą operatora bitowego lub (|
). Najpierw tworzymy wzór bitowy odpowiadający wartości 5
uint bitpattern = 1u << 4; // 1u << (int)(value - 1u)
A następnie użyj operator |
, aby ustawić bit w masce maskForNumbersSetInRow[3]
maskForNumbersSetInRow[3] = maskForNumbersSetInRow[3] | bitpattern;
Lub używając krótszej formy
maskForNumbersSetInRow[3] |= bitpattern;
00000000 00000000 00000001 01001011
|
00000000 00000000 00000000 00010000
=
00000000 00000000 00000001 01011011
Teraz twoja maska wskazuje, że są wartości {1, 2, 4, 5, 7, 9}
w tym wierszu (wiersz 3).
Jeśli chcesz sprawdzić, czy jakaś wartość jest w wierszu, możesz użyć operator &
, aby sprawdzić, czy odpowiedni bit jest ustawiony w masce. Jeśli wynik tego operatora zastosowanego do maski i odpowiadającego tej wartości wzorca bitowego jest niezerowy, to wartość jest już w wierszu. Jeżeli wynikiem jest 0, wartość nie znajduje się w wierszu.
Na przykład, jeśli chcesz sprawdzić, czy wartość 3 jest w wierszu, możesz to zrobić w ten sposób:
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.
Poniżej znajdują się metody ustawiania nowej wartości na planszy, aktualizowania odpowiednich bitmasek i sprawdzania, czy ruch jest legalny.
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;
}
Mając maski, możesz sprawdzić, czy ruch jest legalny w ten sposób:
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);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-08 07:59:42
Jeśli kiedykolwiek będziesz musiał komunikować się ze sprzętem, musisz użyć bit twidling w pewnym momencie.
Wyodrębnianie wartości RGB wartości piksela.
Tyle rzeczy
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 16:33:54
Dziesiątki bitowych przykładów tutaj
Kod jest w C, ale można go łatwo dostosować do C #
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 23:26:55
Mogą być używane do całego obciążenia różnych aplikacji, oto pytanie, które wcześniej tu zamieszczałem, które wykorzystuje operacje bitowe:
Bitwise i, bitwise Inclusive lub question, w Javie
Dla innych przykładów, spójrz na (powiedzmy) oznaczonych wyliczeń.
W moim przykładzie używałem operacji bitowych, aby zmienić zakres liczby binarnej z -128...127 do 0..255 (zmiana reprezentacji z podpisanej na niepodpisaną).
MSN artykuł tutaj - >
Http://msdn.microsoft.com/en-us/library/6a71f45d%28VS.71%29.aspx
Jest przydatne.
I chociaż ten link:
Jest bardzo techniczny, obejmuje wszystko.
HTH
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:15
Za każdym razem, gdy masz opcję 1 lub więcej w kombinacji elementów, wtedy bitwise jest zwykle łatwym rozwiązaniem.
Niektóre przykłady obejmują bity bezpieczeństwa (czekając na próbkę Justina..), planowanie dni itp.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 15:48:42
Muszę powiedzieć, że jednym z najczęstszych zastosowań jest modyfikowanie pól bitowych w celu kompresji danych. Widać to głównie w programach próbujących być ekonomicznymi z pakietami.
Przykład kompresji sieci przy użyciu bitfieldów
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 15:53:33
Jedną z najczęstszych rzeczy, do których ich używam w C#, jest tworzenie hashcodów. Jest kilka całkiem dobrych metod haszujących, które ich używają. Na przykład dla klasy współrzędnych z X i Y, które były zarówno ints mogę użyć:
public override int GetHashCode()
{
return x ^ ((y << 16) | y >> 16);
}
To szybko generuje liczbę, która jest gwarantowana, że będzie równa, gdy zostanie wytworzona przez równy obiekt (zakładając, że równość oznacza, że zarówno parametry X, jak i Y są takie same w obu porównywanych obiektach), jednocześnie nie wytwarzając ścierających się wzorców dla obiektów o niskiej wartości (prawdopodobnie najczęściej w większości zastosowań).
Innym jest łączenie wyliczeń flag. Np. RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase
Są pewne operacje niskiego poziomu, które nie są zwykle konieczne, gdy kodujesz na frameworku takim jak. NET (np. w C# Nie będę musiał pisać kodu, aby przekonwertować UTF - 8 na UTF-16, to jest dla mnie w frameworku), ale oczywiście ktoś musiał napisać ten kod.
Istnieje kilka technik, takich jak zaokrąglanie do najbliższej liczby binarnej (np. round up 1010 to 10000):
unchecked
{
--x;
x |= (x >> 1);
x |= (x >> 2);
x |= (x >> 4);
x |= (x >> 8);
x |= (x >> 16);
return ++x;
}
Które są przydatne, gdy ich potrzebujesz, ale to nie jest zbyt powszechne.
Wreszcie, można również użyć ich do mikro-optymalizacji matematyki, takiej jak << 1
zamiast * 2
, ale dodam, że jest to ogólnie zły pomysł, ponieważ ukrywa intencje prawdziwego kodu, nie oszczędza prawie nic w wydajności i może ukryć kilka subtelnych błędów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 15:56:36
- mogą być używane do przekazywania wielu argumentów do funkcji poprzez jedną zmienną o ograniczonym rozmiarze.
- zaletami są niski koszt pamięci lub niski koszt pamięci: dlatego zwiększona wydajność.
- nie mogę napisać tutoriala na miejscu, ale są tam na pewno.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 15:48:19
Sortowanie binarne. Pojawiły się problemy, w których implementacja używała operatora podziału zamiast operatora bitshift. To spowodowało, że BS nie powiodło się po tym, jak kolekcja osiągnęła rozmiary powyżej 10 000 000
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 15:57:21
Będziesz ich używać z różnych powodów:
- przechowywanie (i sprawdzanie!) flagi opcji w pamięci efektywny sposób
- jeśli wykonujesz programowanie obliczeniowe, możesz rozważyć optymalizację niektórych operacji za pomocą operacji bitowych zamiast operatorów matematycznych (uważaj na skutki uboczne)
- Szary Kod !
- Tworzenie wartości wyliczonych
Jestem pewien, że możesz myśleć o innych.
To jest powiedziane, czasami trzeba zapytać yourself: czy zwiększenie pamięci i wydajności jest warte wysiłku. Po napisaniu tego rodzaju kodu pozostaw go na chwilę i wróć do niego. Jeśli zmagasz się z tym, przepisz kod bardziej dający się utrzymać.
Z drugiej strony, czasami ma sens używanie operacji bitowych (myśl kryptografia).
Jeszcze lepiej, niech ktoś inny to przeczyta i udokumentuje.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 17:52:26
Gry!
W dawnych czasach, używałem go do reprezentowania elementów odtwarzacza Reversi. To jest 8X8, więc zajęło mi long
typ, a następnie, na przykład, jeśli chcesz wiedzieć, gdzie są wszystkie kawałki na pokładzie-ty {[1] } obu graczy kawałki.
Jeśli chcesz wszystkie możliwe kroki gracza, powiedz po prawej-Ty >>
figury gracza reprezentują przez jeden, i AND
to z figurami przeciwnika, aby sprawdzić, czy są teraz wspólne 1 (to znaczy, że jest przeciwnik kawałek po prawej). Wtedy rób tak dalej. jeśli wrócisz do swoich własnych kawałków - nie ruszaj się. Jeśli dojdziesz do jasnego bitu - możesz się tam przenieść i uchwycić wszystkie elementy po drodze.
(Technika ta jest szeroko stosowana w wielu rodzajach gier planszowych, w tym w szachach)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-07 18:09:16