Skip to content

Commit f08cb86

Browse files
committed
docs: 비트마스크 문서 업데이트
1 parent 7640353 commit f08cb86

File tree

1 file changed

+231
-145
lines changed

1 file changed

+231
-145
lines changed

_posts/2024-03-25-bitmask.md

Lines changed: 231 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: Bitmask
3-
description: Bitmask에 대해 설명하는 페이지입니다.
2+
title: 비트마스크 (Bitmask)
3+
description: 비트마스크 (Bitmask)에 대해 정리한 페이지입니다.
44
date: 2024-03-25 00:00:00 +/-TTTT
55
categories: [Algorithms]
66
tags: [algorithm]
@@ -15,152 +15,238 @@ comments: true
1515
<blockquote class="prompt-info"><p><strong><u>Tags</u></strong> <br />
1616
Algorithm</p></blockquote>
1717

18-
## Introduction
19-
20-
- **Definition**
21-
- **비트마스크(Bitmask)** 란 정수의 이진수 표현을 자료 구조로 쓰는 기법을 말한다.
22-
- **비트(Bit)**: 이진수의 한 자리를 비트(Bit)라고 한다.
23-
- **최상위 비트(Most Significant Bit)**: 2<sup>N - 1</sup>에 해당하는 비트를 말한다.
24-
- **최하위 비트(Least Significant Bit)**: 2<sup>0</sup>에 해당하는 비트를 말한다.
25-
- **Advantages**
26-
- **더 빠른 수행 시간**: 비트마스크 연산은 **O(1)** 에 구현되는 것이 많으므로, 적절히 사용할 경우 다른 자료 구조를 사용하는 것보다 훨씬 빠르게 동작한다.
27-
- **더 간결한 코드**: 다양한 집합 연산들을 반복문 없이 한 줄에 작성이 가능하다.
28-
- **더 작은 메모리 사용량**: 비트마스크를 이용하는 코드들은 같은 데이터를 더 적은 메모리를 사용해 표현할 수 있다.
29-
- **연관 배열을 배열로 대체**: Boolean 값 배열을 키로 갖는 연관 배열 객체 Map<Boolean[], Integer>을 비트마스크를 이용하여 int[] 배열로 대체할 수 있다. 이를 통해 시간과 메모리를 절약할 수 있다.
30-
31-
## How to Use
32-
33-
- **비트 연산자**
34-
<table>
35-
<tr>
36-
<th>연산
37-
<th>코드
38-
<tr>
39-
<td>AND 연산</td>
40-
<td>a & b</td>
41-
</tr>
42-
<tr>
43-
<td>OR 연산</td>
44-
<td>a | b</td>
45-
</tr>
46-
<tr>
47-
<td>XOR 연산</td>
48-
<td>a ^ b</td>
49-
</tr>
50-
<tr>
51-
<td>NOT 연산</td>
52-
<td>~a</td>
53-
</tr>
54-
<tr>
55-
<td>Left Shift 연산</td>
56-
<td>a << b</td>
57-
</tr>
58-
<tr>
59-
<td>Right Shift 연산</td>
60-
<td>a >> b</td>
61-
</tr>
62-
- **비트마스크를 이용한 집합 구현**
63-
- **꽉 찬 집합**
64-
```java
65-
int fullSet = (1 << 20) - 1;
66-
```
67-
- **공집합**
68-
```java
69-
int emptySet = 0;
70-
```
71-
- **원소 추가**
72-
```java
73-
bitSet |= (1 << p);
74-
```
75-
- **원소의 포함 여부 확인**
76-
```java
77-
// & 연산의 결과 값이 0 또는 (1 << p) 라는 점을 주의하자.
78-
if (bitSet & (1 << p)) {
79-
System.out.println("원소가 포함되어 있음.");
18+
## 개요
19+
20+
`비트마스크(Bitmask)`에 대해 정리한 페이지입니다.
21+
22+
## 비트마스크 (Bitmask)
23+
24+
### 개념
25+
26+
`비트마스크(Bitmask)`란 정수의 이진수 표현을 자료 구조로 사용하는 기법을 말합니다.
27+
28+
<blockquote class="prompt-info"><p><strong><u>Info.</u></strong><br />
29+
<b>비트(Bit)</b>: 이진수의 한 자리를 비트(Bit)라고 합니다.<br />
30+
<b>최상위 비트(Most Significant Bit)</b>: 2<sup>N - 1</sup>에 해당하는 비트를 말합니다.<br />
31+
<b>최하위 비트(Least Significant Bit)</b>: 2<sup>0</sup>에 해당하는 비트를 말합니다.</p></blockquote>
32+
33+
### 특징
34+
35+
비트마스크의 특징은 다음과 같습니다.
36+
37+
- `간결한 코드`
38+
39+
다양한 집합 연산들을 반복문 없이 한 줄에 작성할 수 있습니다.
40+
41+
- `더 작은 메모리 사용량`
42+
43+
비트마스크를 이용하는 코드들은 같은 데이터를 더 적은 메모리를 사용해 표현할 수 있습니다. 특히 Boolean 값 배열을 키로 갖는 연관 배열 객체 `Map<Boolean[], Integer>`을 비트마스크를 이용하여 `int[]` 배열로 대체할 수 있어서 시간과 메모리를 절약할 수 있습니다.
44+
45+
- `시간 복잡도(Time Complexity)`
46+
47+
대부분의 비트마스크 연산은 `O(1)`의 시간 복잡도를 갖으므로 적절히 사용할 경우 다른 자료 구조를 사용하는 것보다 훨씬 빠르게 동작합니다.
48+
49+
### 비트 연산자
50+
51+
| 연산 | 코드 |
52+
| ---------------- | ------ |
53+
| AND 연산 | a & b |
54+
| OR 연산 | a \| b |
55+
| XOR 연산 | a ^ b |
56+
| NOT 연산 | ~a |
57+
| Left Shift 연산 | a << b |
58+
| Right Shift 연산 | a >> b |
59+
60+
### 비트마스크를 이용한 집합 구현
61+
62+
#### 꽉 찬 집합
63+
64+
```java
65+
int fullSet = (1 << 20) - 1;
66+
```
67+
68+
#### 공집합
69+
70+
```java
71+
int emptySet = 0;
72+
```
73+
74+
#### 원소 추가
75+
76+
```java
77+
bitSet |= (1 << p);
78+
```
79+
80+
#### 원소의 포함 여부 확인
81+
82+
```java
83+
// & 연산의 결과 값이 0 또는 (1 << p) 라는 점을 주의해야 합니다.
84+
if (bitSet & (1 << p)) {
85+
System.out.println("원소가 포함되어 있음.");
86+
}
87+
```
88+
89+
#### 원소의 삭제
90+
91+
```java
92+
bitSet &= ~(1 << p);
93+
```
94+
95+
#### 원소의 토글
96+
97+
```java
98+
bitSet ^= (1 << p);
99+
```
100+
101+
#### 두 집합에 대한 연산
102+
103+
```java
104+
int added = (a | b); // a와 b의 합집합
105+
int intersection = (a & b); // a와 b의 교집합
106+
int removed = (a & ~b); // a에서 b를 뺀 차집합
107+
int toggled = (a ^ b); // a와 b중 하나에만 포함된 원소들의 집합
108+
```
109+
110+
#### 집합의 크기
111+
112+
```java
113+
int bitCount(int x) {
114+
if (x == 0) return 0;
115+
return x % 2 + bitCount(x / 2);
116+
}
117+
```
118+
119+
| 컴파일러 또는 언어 | 집합의 크기 |
120+
| ------------------ | ---------------------------- |
121+
| gcc/g++ | \_\_builtin_popcount(bitSet) |
122+
| Visual C++ | \_\_popcnt(bitSet) |
123+
| Java | Integer.bitCount(bitSet) |
124+
125+
#### 최소 원소 지우기
126+
127+
```java
128+
bitSet &= (bitSet - 1);
129+
```
130+
131+
#### 2의 거듭제곱 값인지 여부 확인
132+
133+
```java
134+
// 2의 거듭제곱 값들의 이진수 표현에는 켜진 비트가 하나 밖에 없습니다.
135+
if ((num & (num - 1)) == 0) {
136+
System.out.println("2의 거듭제곱 값입니다.");
137+
}
138+
```
139+
140+
#### 모든 부분 집합 순회
141+
142+
```java
143+
for (int subset = bitSet; subset > 0; subset = ((subset - 1) & bitSet)) {
144+
// subset은 bitSet의 부분 집합
145+
// (subset > 0): 공집합은 방문하지 않습니다.
146+
}
147+
```
148+
149+
## Example
150+
151+
- <a href="https://github.com/HyunJinNo/Algorithm/blob/main/Number%20Theory/Sieve%20of%20Eratosthenes/Sieve_of_Eratosthenes.js" target="_blank">비트마스크를 사용하는 에라토스테네스의 체</a>
152+
153+
```javascript
154+
// 비트마스크(Bitmask)를 사용하는 에라토스테네스의 체의 구현
155+
156+
const n = 1000000; // n개의 원소
157+
158+
/**
159+
* Uint8Array와 비트마스크를 사용하여 메모리 사용량을 8분의 1로 줄인다.
160+
* 0: 합성수, 1: 소수
161+
*/
162+
const sieve = new Uint8Array(Math.floor((n + 7) / 8)).fill(255);
163+
164+
/**
165+
* 비트를 0으로 바꿔서 x가 소수가 아니라고 표시한다.
166+
* @param {number} x
167+
*/
168+
const setComposite = (x) => {
169+
sieve[x >> 3] &= ~(1 << (x & 7));
170+
};
171+
172+
/**
173+
* x가 소수인지 확인한다
174+
* @param {number} x 판정할 값
175+
* @returns {Boolean} 소수 여부
176+
*/
177+
const isPrime = (x) => {
178+
if (sieve[x >> 3] & (1 << (x & 7))) {
179+
return true;
180+
} else {
181+
return false;
80182
}
81-
```
82-
- **원소의 삭제**
83-
```java
84-
bitSet &= ~(1 << p);
85-
```
86-
- **원소의 토글**
87-
```java
88-
bitSet ^= (1 << p);
89-
```
90-
- **두 집합에 대한 연산**
91-
```java
92-
int added = (a | b); // a와 b의 합집합
93-
int intersection = (a & b); // a와 b의 교집합
94-
int removed = (a & ~b); // a에서 b를 뺀 차집합
95-
int toggled = (a ^ b); // a와 b중 하나에만 포함된 원소들의 집합
96-
```
97-
- **집합의 크기**
98-
```java
99-
int bitCount(int x) {
100-
if (x == 0) return 0;
101-
return x % 2 + bitCount(x / 2);
183+
};
184+
185+
setComposite(0);
186+
setComposite(1);
187+
for (let x = 2; x <= n; x++) {
188+
if (isPrime(x)) {
189+
for (let y = x * x; y <= n; y += x) {
190+
setComposite(y);
191+
}
102192
}
103-
```
104-
<table>
105-
<tr>
106-
<th>컴파일러 혹은 언어</th>
107-
<th>집합의 크기</th>
108-
</tr>
109-
<tr>
110-
<td>gcc/g++</td>
111-
<td>__builtin_popcount(bitSet)</td>
112-
</tr>
113-
<tr>
114-
<td>Visual C++</td>
115-
<td>__popcnt(bitSet)</td>
116-
</tr>
117-
<tr>
118-
<td>Java</td>
119-
<td>Integer.bitCount(bitSet)</td>
120-
</tr>
121-
</table>
122-
- **최소 원소 찾기**
123-
```java
124-
int firstElement = (bitSet & -bitSet);
125-
```
126-
<table>
127-
<tr>
128-
<th>컴파일러 혹은 언어</th>
129-
<th>최소 원소</th>
130-
</tr>
131-
<tr>
132-
<td>gcc/g++</td>
133-
<td>__builtin_ctz(bitSet)</td>
134-
</tr>
135-
<tr>
136-
<td>Visual C++</td>
137-
<td>__BitScanForward(bitSet)</td>
138-
</tr>
139-
<tr>
140-
<td>Java</td>
141-
<td>Integer.numberOfTrailingZeros(bitSet)</td>
142-
</tr>
143-
</table>
144-
- **최소 원소 지우기**
145-
```java
146-
bitSet &= (bitSet - 1);
147-
```
148-
- **2의 거듭제곱 값인지 여부 확인**
149-
```java
150-
// 2의 거듭제곱 값들의 이진수 표현에는 켜진 비트가 하나 밖에 없음.
151-
if ((num & (num - 1)) == 0) {
152-
System.out.println("2의 거듭제곱 값입니다.");
193+
}
194+
195+
for (let x = 2; x <= n; x++) {
196+
if (isPrime(x)) {
197+
console.log(x);
153198
}
154-
```
155-
- **모든 부분 집합 순회**
156-
```java
157-
for (int subset = bitSet; subset > 0; subset = ((subset - 1) & bitSet)) {
158-
// subset은 bitSet의 부분 집합
159-
// (subset > 0): 공집합은 방문하지 않는다.
199+
}
200+
```
201+
202+
- <a href="https://www.acmicpc.net/problem/1311" target="_blank">1311번: 할 일 정하기 1</a>
203+
204+
```javascript
205+
const path =
206+
process.platform === "linux" ? "/dev/stdin" : "./JavaScript/input.txt";
207+
const input = require("fs").readFileSync(path).toString().split("\n");
208+
209+
const n = Number(input[0]); // 사람과 일의 수, 1 <= n <= 20
210+
const cache = Array.from(Array(n), () => new Array(1 << n).fill(-1));
211+
const arr = [];
212+
213+
for (let i = 1; i <= n; i++) {
214+
arr.push(input[i].split(" ").map(Number));
215+
}
216+
217+
/**
218+
* 모든 일을 하는데 필요한 비용의 최솟값을 구하는 함수
219+
* @param {number} index (index)번째 사람
220+
* @param {number} visited 지금까지 한 일
221+
* @returns 최소 비용 값
222+
*/
223+
const solve = (index, visited) => {
224+
if (index >= n) {
225+
return 0;
226+
} else if (cache[index][visited] !== -1) {
227+
return cache[index][visited];
160228
}
161-
```
162229

163-
## Examples
230+
let result = Number.MAX_SAFE_INTEGER;
164231

165-
- <a href="https://github.com/HyunJinNo/Algorithm/blob/main/Number%20Theory/Sieve%20of%20Eratosthenes/Sieve_of_Eratosthenes.js" target="_blank">비트마스크를 사용하는 에라토스테네스의 체</a>
166-
- <a href="https://github.com/HyunJinNo/Algorithm/blob/main/Bitmask/GRADUATION.js" target="_blank">GRADUATION.js</a>
232+
for (let i = 0; i < n; i++) {
233+
if ((~visited & (1 << i)) === 1 << i) {
234+
visited |= 1 << i;
235+
result = Math.min(
236+
result,
237+
arr[index][n - 1 - i] + solve(index + 1, visited)
238+
);
239+
visited &= ~(1 << i);
240+
}
241+
}
242+
243+
cache[index][visited] = result;
244+
return result;
245+
};
246+
247+
console.log(solve(0, 0));
248+
```
249+
250+
## 참고 자료
251+
252+
- <a href="https://www.yes24.com/product/goods/8006522" target="_blank">알고리즘 문제 해결 전략 세트 | 구종만 | 인사이트(insight) - 예스24</a>

0 commit comments

Comments
 (0)