[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
---
|
||||
id: cs-big-o-practical
|
||||
title: Big-O 실전 — 실제 성능 / 측정 / 함정
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [cs, algorithm, big-o, vibe-coding]
|
||||
tech_stack: { language: "TS", applicable_to: ["Backend", "Frontend"] }
|
||||
applied_in: []
|
||||
aliases: [Big O, time complexity, space complexity, amortized, hidden constant]
|
||||
---
|
||||
|
||||
# Big-O 실전
|
||||
|
||||
> Big-O 가 알고리즘의 핵심 지표. **그러나 hidden constant + cache locality + 입력 크기 도 중요**. O(N) 가 O(log N) 보다 느릴 수 있다 (작은 N).
|
||||
|
||||
## 📖 핵심 개념
|
||||
- O(1): 상수.
|
||||
- O(log N): 이진 검색.
|
||||
- O(N): linear scan.
|
||||
- O(N log N): merge sort, sort.
|
||||
- O(N²): nested loop.
|
||||
- O(2^N), O(N!): 거의 사용 X (작은 N).
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### 자주 쓰는 자료구조 복잡도
|
||||
```
|
||||
Array (JS):
|
||||
push/pop: O(1) amortized
|
||||
shift/unshift: O(N) — 큰 시작 element 이동
|
||||
index access: O(1)
|
||||
indexOf: O(N)
|
||||
|
||||
Map / Set (V8):
|
||||
get/set/has: O(1) average (hash collision 시 worst O(N))
|
||||
iteration: insertion order
|
||||
|
||||
Object:
|
||||
property access: O(1) average
|
||||
|
||||
LinkedList:
|
||||
prepend/append: O(1)
|
||||
index access: O(N)
|
||||
|
||||
Heap (priority queue):
|
||||
insert/pop: O(log N)
|
||||
|
||||
Binary search:
|
||||
on sorted array: O(log N)
|
||||
|
||||
Sort:
|
||||
Array.sort: O(N log N) (TimSort in V8)
|
||||
```
|
||||
|
||||
### Common 변환
|
||||
```ts
|
||||
// O(N²) — nested
|
||||
for (const x of arr) {
|
||||
for (const y of arr) {
|
||||
if (x.id === y.parent) ...;
|
||||
}
|
||||
}
|
||||
|
||||
// O(N) with Map
|
||||
const byId = new Map(arr.map(x => [x.id, x]));
|
||||
for (const x of arr) {
|
||||
const parent = byId.get(x.parent);
|
||||
if (parent) ...;
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// O(N²) — array.includes
|
||||
for (const x of arr) {
|
||||
if (otherArr.includes(x.id)) ...; // O(M) per loop
|
||||
}
|
||||
|
||||
// O(N+M)
|
||||
const set = new Set(otherArr);
|
||||
for (const x of arr) {
|
||||
if (set.has(x.id)) ...; // O(1)
|
||||
}
|
||||
```
|
||||
|
||||
### Hidden constant
|
||||
```
|
||||
O(log N) binary search:
|
||||
- ~20 comparison for 1M items
|
||||
- 매 comparison = 비교 + index calc
|
||||
|
||||
O(N) linear with simd:
|
||||
- 1M cmp = 100ns 같은 cache friendly
|
||||
|
||||
→ 작은 N (<100) = 상수 차이 큼.
|
||||
N = 100K → log N (17) vs N = 차이 명확.
|
||||
```
|
||||
|
||||
### Cache locality
|
||||
```ts
|
||||
// ✅ Sequential access — cache 친화
|
||||
for (let i = 0; i < arr.length; i++) sum += arr[i];
|
||||
|
||||
// ❌ Random — cache miss
|
||||
for (const i of shuffled) sum += arr[i];
|
||||
// → 5-20x 느림
|
||||
```
|
||||
|
||||
### Amortized
|
||||
```ts
|
||||
// Array.push: 보통 O(1).
|
||||
// 가끔 capacity 확장 → 모두 copy = O(N).
|
||||
// 평균 O(1) (amortized).
|
||||
|
||||
// Hash map resize: 같은 원리.
|
||||
```
|
||||
|
||||
### 작은 vs 큰 N
|
||||
```
|
||||
N = 10: O(N²) = 100, OK.
|
||||
N = 1000: O(N²) = 1M, 느려질 수 있음.
|
||||
N = 1M: O(N²) = 10^12, 절대 X. O(N log N).
|
||||
N = 1G: O(N) 도 비쌈. 분산.
|
||||
```
|
||||
|
||||
→ 입력 크기 모르면 안전하게 좋은 알고리즘.
|
||||
|
||||
### 측정
|
||||
```ts
|
||||
const t = performance.now();
|
||||
const result = myFunction(input);
|
||||
console.log('took', performance.now() - t, 'ms');
|
||||
|
||||
// 더 정확
|
||||
const COUNT = 10000;
|
||||
let total = 0;
|
||||
for (let i = 0; i < COUNT; i++) {
|
||||
const t = performance.now();
|
||||
myFunction(input);
|
||||
total += performance.now() - t;
|
||||
}
|
||||
console.log('avg', total / COUNT, 'ms');
|
||||
```
|
||||
|
||||
### Common O(N log N)
|
||||
```ts
|
||||
// Sort
|
||||
arr.sort((a, b) => a.value - b.value);
|
||||
|
||||
// 그 후 binary search
|
||||
function find(target: number): number | undefined {
|
||||
let lo = 0, hi = arr.length - 1;
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (arr[mid].value === target) return arr[mid];
|
||||
if (arr[mid].value < target) lo = mid + 1;
|
||||
else hi = mid - 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
→ Sort 한 번 + N 검색 = O(N log N). N 검색 X = O(N²).
|
||||
|
||||
### Top-K (heap)
|
||||
```ts
|
||||
import Heap from 'heap-js';
|
||||
|
||||
function topK(arr: number[], k: number): number[] {
|
||||
const minHeap = new Heap<number>();
|
||||
for (const x of arr) {
|
||||
minHeap.push(x);
|
||||
if (minHeap.size() > k) minHeap.pop(); // 가장 작은 제거
|
||||
}
|
||||
return minHeap.toArray(); // top K
|
||||
}
|
||||
|
||||
// O(N log K) — N log N (sort) 보다 빠름.
|
||||
```
|
||||
|
||||
### Sliding window
|
||||
```ts
|
||||
// 합 N 길이 array 의 K 길이 sub 의 max
|
||||
function maxSumWindow(arr: number[], k: number): number {
|
||||
let sum = 0;
|
||||
for (let i = 0; i < k; i++) sum += arr[i];
|
||||
let max = sum;
|
||||
for (let i = k; i < arr.length; i++) {
|
||||
sum += arr[i] - arr[i - k]; // O(1) update
|
||||
max = Math.max(max, sum);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
// O(N) — 매번 sum 다시 X.
|
||||
```
|
||||
|
||||
### Two pointer
|
||||
```ts
|
||||
// Sorted array — sum = target?
|
||||
function twoSum(arr: number[], target: number): [number, number] | null {
|
||||
let lo = 0, hi = arr.length - 1;
|
||||
while (lo < hi) {
|
||||
const sum = arr[lo] + arr[hi];
|
||||
if (sum === target) return [lo, hi];
|
||||
if (sum < target) lo++;
|
||||
else hi--;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
// O(N).
|
||||
```
|
||||
|
||||
### Dynamic programming
|
||||
```ts
|
||||
// Fibonacci O(2^N) → O(N) memo
|
||||
const memo = new Map<number, number>();
|
||||
function fib(n: number): number {
|
||||
if (n <= 1) return n;
|
||||
if (memo.has(n)) return memo.get(n)!;
|
||||
const r = fib(n - 1) + fib(n - 2);
|
||||
memo.set(n, r);
|
||||
return r;
|
||||
}
|
||||
```
|
||||
|
||||
### Graph
|
||||
```ts
|
||||
// BFS — O(V + E)
|
||||
function bfs(start: Node) {
|
||||
const visited = new Set<Node>();
|
||||
const queue = [start];
|
||||
while (queue.length > 0) {
|
||||
const node = queue.shift()!; // ❌ O(N) shift
|
||||
// 또는 더 나은 = deque 또는 index
|
||||
if (visited.has(node)) continue;
|
||||
visited.add(node);
|
||||
queue.push(...node.neighbors);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
→ Array shift 가 O(N). 큰 BFS = deque library.
|
||||
|
||||
### When to optimize
|
||||
```
|
||||
1. Profile first — hot path 만.
|
||||
2. Big-O 가 일반 답.
|
||||
3. Cache / locality 가 micro-opt.
|
||||
4. Algorithm > implementation.
|
||||
|
||||
"Premature optimization is the root of all evil." (Knuth)
|
||||
But: O(N²) → O(N) 는 premature 가 아님.
|
||||
```
|
||||
|
||||
### V8 (JavaScript) 특이
|
||||
```
|
||||
.shift() / .unshift(): O(N).
|
||||
.push() / .pop(): O(1) amortized.
|
||||
|
||||
Object property access:
|
||||
- 같은 shape: O(1)
|
||||
- 다른 shape mix: 느림 (위 V8 문서)
|
||||
```
|
||||
|
||||
### Memory complexity
|
||||
```ts
|
||||
// O(N²) memory
|
||||
const matrix: number[][] = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
matrix[i] = [];
|
||||
for (let j = 0; j < N; j++) matrix[i][j] = ...;
|
||||
}
|
||||
|
||||
// 큰 N (10K+) = OOM 가능.
|
||||
```
|
||||
|
||||
### Algorithm 선택 cheat sheet
|
||||
```
|
||||
Search:
|
||||
Sorted: binary search O(log N)
|
||||
Unsorted: linear O(N) 또는 hash set O(1)
|
||||
|
||||
Sort:
|
||||
General: TimSort (V8) O(N log N)
|
||||
Small (<10): insertion O(N²) but 빠름
|
||||
Distinct integers: counting / radix O(N)
|
||||
|
||||
Top-K:
|
||||
K << N: heap O(N log K)
|
||||
K close to N: sort O(N log N)
|
||||
|
||||
Group:
|
||||
reduce / Map O(N)
|
||||
|
||||
Distinct:
|
||||
Set O(N)
|
||||
|
||||
Combinations: backtracking O(2^N) — 작은 N 만.
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 입력 크기 | 알고리즘 |
|
||||
|---|---|
|
||||
| <100 | 어떤 거나 OK |
|
||||
| <10K | O(N²) 일부 가능 |
|
||||
| <1M | O(N log N) 안정 |
|
||||
| <1B | O(N) 또는 분산 |
|
||||
| 1B+ | 분산 + sampling |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **`Array.includes` in loop**: O(N²). Set / Map.
|
||||
- **`arr.shift()` 반복**: O(N²). Index 또는 deque.
|
||||
- **모든 곳 micro-opt**: 가독성 잃음.
|
||||
- **Big-O 무시 + V8 trick**: 알고리즘 우선.
|
||||
- **Sort 매번**: cache.
|
||||
- **Recursion + 작은 N — overhead 가 cost**: iterative.
|
||||
- **JSON.parse 큰 string**: O(N) but 큰 constant. stream parser.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- Big-O 우선 — 그 후 micro.
|
||||
- Set / Map = 80% 개선의 답.
|
||||
- Profile → hot path.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[Perf_V8_Optimization]]
|
||||
- [[CS_Bloom_Filter]]
|
||||
- [[CS_Probabilistic_Data_Structures]]
|
||||
Reference in New Issue
Block a user