본문으로 건너뛰기
피드

Monus와 힙 — 차이만 저장하면 됨이라는 대수적 통찰

backend 약 7분
vote
0
댓글
북마크

Monus(부분 뺄셈을 지원하는 모노이드)를 이용해 pairing heap을 구현하는 아이디어를 설명한 Haskell 포스트. 핵심 통찰은 '절댓값 대신 부모와의 차이를 저장하라'는 것으로, 이를 통해 힙 불변식을 타입 수준에서 강제하고 전체 가중치 수정을 O(1)로 만들 수 있음. 이 패턴을 Phases 어플리케이티브, 안정 정렬을 위한 Key monus, 모나딕 힙으로까지 확장함.

  • 1

    Monus는 순서가 모노이드 연산으로 정의되는 대수 구조로, x ≤ y ⟺ ∃z. y = x • z

  • 2

    힙에서 절댓값 대신 부모와의 차이를 저장하면 힙 성질이 타입 수준에서 강제되고 전체 가중치 수정이 O(1)이 됨

  • 3

    Max/Last 세미그룹 래퍼로 일반 Ord 키를 monus 힙에서도 사용 가능

  • 4

    Phases 어플리케이티브를 pairing heap으로 구현하면 임의의 전순서 키로 효과를 재정렬할 수 있음

  • 5

    Key monus(키 + Int 오프셋)로 안정 정렬을 달성하며, 오프셋 정보가 완전히 로컬하게 유지됨

Monus가 뭔지부터

Monus는 부분 뺄셈을 지원하는 모노이드임. 정확히는 순서가 붙은 모노이드(ordered monoid)인데, 그 순서가 모노이드 연산 자체로 정의됨:

x ≤ y  ⟺  ∃z. y = x • z

"x가 y보다 작거나 같다"는 말은 "x와 y 사이의 간격 z가 존재한다"는 뜻임. 즉, 그 간격이 반드시 원소로서 실재해야 함.

왜 그룹(group)은 안 되냐면 — 정수 (ℤ, +, 0) 같은 그룹에서는 음수가 있어서 어떤 x, y에 대해서도 항상 z를 찾을 수 있음. 그러면 순서가 흐릿해짐. Monus는 자연수 (ℕ, +, 0) 같은 양의 모노이드에서 자연스럽게 나타남.

이진 뺄셈 연산 은 다음처럼 파생됨:

x ∸ y = z   (y ≤ x이고 x = y • z인 경우)
       0    (그 외)

힙과의 연결 고리

힙 성질은 이것임: 모든 부모 노드의 가중치 ≤ 자식 노드의 가중치.

보통 구현은 각 노드에 절댓값(absolute weight) 을 저장함. 그런데 모노이드 구조가 있는 가중치를 쓸 때는 다른 관점이 가능함.

힙 성질 자체가 "자식 가중치 = 부모 가중치 + 어떤 차이" 를 보장하니까, 자식 노드엔 절댓값 대신 부모와의 차이(difference)만 저장해도 됨.

이 방식의 장점 두 가지:

  1. 타입 수준에서 힙 성질 위반 불가 — 잘못된 트리를 아예 표현할 수 없음
  2. 전체 가중치 일괄 변경이 O(1) — 루트 노드 하나만 바꾸면 트리 전체에 반영됨

Haskell 구현 — Pairing Heap with Monus

저자는 Monus 타입클래스를 정의하고 pairing heap을 구현함. Pairing heap을 선택한 이유는 구현이 단순하면서도 성능이 좋기 때문임 (포인터 기반 힙 중 실험적으로 가장 빠른 편).

핵심은 두 힙을 합칠 때 키 비교 후 자식의 키를 yk ∸ xk 로 저장하는 것. 절댓값이 아닌 부모와의 차이를 저장함으로써 힙 성질이 구조적으로 강제됨.

popMin에서도 이 구조가 빛남 — 루트를 꺼낼 때, 나머지 서브힙들 전체에 루트 가중치를 "더해줘야" 하는데, 차이만 저장하는 방식 덕분에 루트에만 더하면 됨 → O(1).

Max와 Last Monus — 일반 Ord 힙 복원

Monus가 없는 일반 키로도 쓰고 싶을 때는 MaxLast 세미그룹 래퍼로 해결함. 이 둘은 퇴화(degenerate)해 보이지만 Monus 법칙을 만족함. Last는 불필요한 비교를 줄여서 더 효율적임.

Phases — 자유 대수적 페어링 힙

Phases 어플리케이티브(Easterly 2019)는 효과(effect)들을 키 기준으로 재정렬하는 어플리케이티브 트랜스포머임. 원래는 정수 단계(integer phases)만 지원했는데, 저자는 이를 임의의 전순서 키로 일반화함.

구조는 pairing heap을 그대로 응용하되, 각 노드에 순수 값 대신 효과적 계산(effectful computation) 을 담음. 실행하면 키 순서대로 효과가 발생하고, 순수 값의 원래 구조는 보존됨.

안정적 정렬을 위한 Key Monus

문제: pairing heap 기반 정렬은 불안정(unstable) 함. 같은 키를 가진 효과들의 순서가 보장되지 않음 → 어플리케이티브 법칙 위반.

해결책: Key k = (k, Int) 형태의 새 Monus. 키가 같을 경우 원래 위치의 오프셋으로 타이브레이킹함.

중요한 점은 이 오프셋이 완전히 로컬 하다는 것 — 전체 시퀀스를 한 번 훑어서 인덱스를 붙이는 게 아니라, 각 노드가 자신과 이웃 간의 상대적 차이만 알면 됨. 차이를 저장하는 패턴이 여기서도 그대로 적용됨.

힙 크기를 추적해서 오프셋을 부여하고, 차이 저장 덕분에 O(1)로 전체 오프셋 조정 가능.

모나딕 힙 — Free Monad Transformer

저자의 2021년 논문에서 나온 Search 타입은 free monad transformer 기반의 모나딕 힙임. list 모나드와 (,) k 펑터(writer 모나드)를 레이어로 쌓은 구조.

이 타입에서는 대수적으로 차이를 저장해야 의미론적으로 올바름 — 레이어들을 join으로 합칠 때 가중치가 제대로 누적되려면 각 레이어가 절댓값이 아닌 차이를 담고 있어야 함.

결론 — 핵심 원리 하나

"절댓값을 저장하지 말고 차이를 저장하라."

이게 이 포스트 전체를 관통하는 주제임. Monus는 이 패턴을 대수적으로 형식화하는 도구. 가중치가 monus 구조를 가질 때 적용 가능하고, 적용하면:

  • 불변식(invariant)이 타입 수준에서 강제됨
  • 전역 조작이 O(1)로 줄어듦
  • 안정 정렬까지 같은 아이디어로 확장됨

그래프 탐색, 정렬, 효과 순서 제어 등 순서 기반 알고리즘 전반에 유용한 패턴임.

같은 '차이를 저장한다'는 패턴이 힙 구조, 안정 정렬, 모나딕 레이어 합성까지 일관되게 관통함. 대수 구조가 자료구조 설계 원칙과 이렇게 자연스럽게 맞물리는 예시가 드문데, monus가 그 접점을 깔끔하게 포착함.

댓글

댓글

댓글을 불러오는 중...

backend

잘못된 추상화보다 중복이 낫다는 샌디 메츠의 고전 조언

샌디 메츠는 중복을 없애려다 잘못된 추상화를 만들면 코드가 조건문과 파라미터로 부풀어 더 위험해진다고 말한다. 이미 틀어진 추상화는 억지로 보존하지 말고, 다시 호출부에 인라인해서 중복을 되살린 뒤 현재 요구사항에 맞는 새 구조를 찾는 편이 빠르다는 주장이다.

backend

리눅스 커널, 6년·360개 넘는 패치 끝에 strncpy 제거

리눅스 커널이 오랫동안 버그의 원인이던 strncpy API 사용을 Linux 7.2에서 제거했어. NUL 종료 동작이 직관적이지 않고 불필요한 zero-fill로 성능 문제도 있던 API를 6년 동안 약 362개 커밋으로 걷어낸 작업임.

backend

덕디비는 왜 빠를까: 서버 없는 분석 엔진의 내부 구조 뜯어보기

DuckDB가 단일 바이너리, 인프로세스 실행, 컬럼형 저장, 최적화 패스, Parquet 푸시다운으로 빠른 분석 쿼리를 처리하는 방식을 깊게 설명한 글이다. 6GB Parquet 파일을 노트북에서 바로 SQL로 읽는 경험 뒤에 어떤 설계가 깔려 있는지 따라간다.

backend

피지독, 포스트그레스를 수평 확장시키겠다고 550만 달러 투자 유치

피지독은 포스트그레스 앞단에 프록시를 두고 샤딩과 라우팅을 처리해 수평 확장을 가능하게 하겠다는 오픈소스 프로젝트다. 이미 프로덕션에서 초당 200만 건이 넘는 쿼리를 처리하고, 확인된 규모만 20테라바이트 이상을 샤딩했다고 밝히며 550만 달러 투자를 공개했다.

backend

펜타시스템, EDB 포스트그레SQL로 국내 엔터프라이즈 DB 전환 시장 공략

펜타시스템테크놀러지가 EDB와 파트너 계약을 맺고 국내에 EDB 포스트그레SQL 기반 데이터 플랫폼을 공급한다. 기존 상용 DBMS 정책 변화로 비용 부담이 커진 기업들을 겨냥해, 오픈소스 기반 엔터프라이즈 데이터 플랫폼 전환 수요를 잡겠다는 전략이다. 금융, 공공, 제조, 유통, 클라우드, AI 데이터 분석 환경까지 적용 범위를 넓히려는 움직임이다.