↓ 본문으로 건너뛰기

Möbius 함수의 정의와 활용

·1 분
nyan101
작성자
nyan101
목차

얼마 전 코드포스에서 흥미로운 문제를 접했다. 대회 당시에는 풀지 못했는데, 에디토리얼을 읽어보던 중 뫼비우스 함수(Möbius function)에 대한 언급이 있어 이에 대해 좀더 자세히 알아보았다.

Möbius function의 정의

뫼비우스 함수(Möbius function)는 자연수 n에 대해 다음과 같이 정의된다. (\(p\)는 소수)

$$ \mu(n) = \begin{cases} (-1)^k & \mathrm{if\,\,\,\,} n=p_1p_2\cdots p_k \, (p_i \neq p_j)\\ 0 & \mathrm{if\,\,\,\,} p^2 | n \end{cases} $$

다시 말해, n이 서로 다른 k개 소수들의 곱이면 \(\mu(n)\)은 \( (-1)^k \)이, 중복 소인수가 있으면(=어떤 제곱수를 약수로 가지면) 0이 되는 함수라고 할 수 있다. (이때 \(\mu(1)=1\))

뫼비우스 함수의 정의를 살펴보면 두 자연수 a,b에 대해 다음 성질이 성립함을 쉽게 알 수 있다.

$$ \mu(ab) = \mu(a)\mu(b) \,\,\,\,\mathrm{if\,}\gcd(a,b)=1 $$

이밖에도 다양한 성질들이 있지만 이번 글에서는 알고리즘적 측면에서의 활용에 집중하기로 한다.

포함-배제의 원리

다음 벤 다이어그램을 살펴보자.

전체 합집합의 넓이는 어떻게 구할 수 있을까? 색칠을 해보면 다음 식을 어렵지 않게 구할 수 있다.

$$ \begin{array}{cl} |A \cup B| & = &|A| + |B| - |A \cap B|\\ |A \cup B \cup C| & = &|A| + |B| + |C| - |A \cap B| - |B \cap C| - |C \cap A| + |A \cap B \cap C| \end{array} $$

같은 원리를 확장하면 n개 집합에 대해 다음을 얻을 수 있다.

$$ \begin{aligned}\\{\biggl |}\bigcup _{i=1}^{n}A_{i}{\biggr |}&{}=\sum _{i=1}^{n}\left|A_{i}\right|-\sum _{i,j\,:\,1\leq i이를 포함-배제의 원리라고 한다.

약수 관계에서의 포함-배제의 원리

이제 이를 약수-배수 관계에서 생각해보자. 자연수 n이 주어졌을 때 \(S\_n\)을 정의할 수 있고, m이 n의 배수일 때 \(S\_m \subset S\_n\) 이 성립한다고 하자. 다시 말해, 각 집합끼리의 포함관계가 약수-배수 관계에 따라 정해지는 상황이다. 잘 와닿지 않는다면 간단히 \(S\_n\)을 (max 이하의 자연수들 중) \(n^2\)의 배수들의 집합1이라고 가정하고 읽어보자. 모든 자연수 n에 대해 \(\|\bigcup \_{i=2}^{n}S\_{i}\|\) 를 구하려면 어떻게 해야 할까?

먼저 다음과 같은 성질을 알 수 있다.

  • 자연수 \(a\)에 대해 \(S\_a\)를 고려했으면 \(S\_{a^2}, S\_{a^3}\) 등은 고려할 필요가 없다
  • \(\bigcup \_{i=2}^{n}S\_{i}\) 은 소수 \(p\_i\)들만을 고려한 \(\bigcup \_{p\_i} S\_{p\_i}\) 와 같다.

그렇다면 포함-배제의 원리에 따라 \(\left\|\bigcup \_{i=2}^{n}S\_{i}\right\|\)는 다음과 같이 구할 수 있다.

  1. 소수 \(p\_i\)들에 대해 \(\sum\_i \left\| S\_{p\_i} \right\| \) 를 구해 더한다
  2. \(i < j\) 에 대해 \(\sum\_{i뺀다
  3. \(i < j < k\) 에 대해 \(\sum\_{i더한다
  4. (이하 반복)

고려해야 할 경우의 수가 작으면 \(O(2^n)\) 복잡도를 가지는 알고리즘으로 전체 경우의 수를 구할 수 있다. 그러나 수가 증가할수록 모든 경우를 고려하는 복잡도는 크게 증가한다. 뫼비우스 함수를 이용하면 이와 같은 포함-배제를 비교적 간단히 처리할 수 있다.

$$ \left|\bigcup _{i=2}^{n}S_{i}\right| = -\sum_{i=2}^{n} \mu(i)|S_{i}| $$

여기서는 단순히 집합의 크기만을 고려했지만 \( S = \bigcup \_{i=2}^{n}S\_{i} \)에 대해 소속 원소의 함수값들의 aggregation을 구하는 경우, 즉 \(\sum\_{x \in S} f(x)\) 나 \(\prod\_{x \in S} f(x)\) 를 구하는 상황으로도 어렵지 않게 확장할 수 있다.

프로그래밍 언어로의 구현 (C++)

마지막으로 mu의 계산에 대해 알아보자. 주 원리는 다음과 같다.

  • 모든 자연수 s는 \(m \times p\) 형태로 표현할 수 있다. (p는 s의 가장 작은 소인수)
    • 이때 m이 p의 배수이면 s는 제곱수를 인자로 가진다.

그러면 n 이하의 소수 리스트 vector<int> pvec을 이용해 다음과 같이 int mu[n] 배열을 채울 수 있다.

int  mu[n+1];
bool isPrime[n+1];
vector<int> pvec;

fill(isPrime, isPrime+n+1, true);
fill(mu, mu+n+1, -1);

mu[1] = 1;
for(int i=2;i<=n;i++)
{
    if(isPrime[i])
        pvec.push_back(i);

    for(int p : pvec)
    {
        if(i*p > n) break;
        
        isPrime[i*p] = false;
        
        if(i%p == 0)
        {
            mu[i*p] = 0;
            break;
        }
        mu[i*p] = mu[i]*mu[p];
    }
}

연습문제

  • [BOJ 1557번] 제곱 ㄴㄴ : 본문에서 예시로 든 상황과 정확히 같다. “K 이하의 제곱 ㄴㄴ수"가 아닌 “K번째 제곱 ㄴㄴ수"임에 유의하자.
  • [Codeforces] D. Steps to One (Round #548, Div.2)
    포함배제를 O(2^n)에 진행할 수도 있지만 뫼비우스 함수와 Linearity of Expectation을 이용해 해결하는 풀이가 존재한다. “마지막 (1이 아닌) gcd가 k의 배수였을 때, 길이의 기대값”을 생각해보고 k=6인 상황은 k=2인 경우와 k=3인 경우 양쪽으로 해당될 수 있다는 점을 염두에 두면서 풀어보자.


  1. 단순히 “n의 배수들의 집합"이라고 할 수도 있지만, 그 경우 \(\bigcup \_{i=2}^{n}S\_{i}\)이 지나치게 단순해지므로 그보다는 약간 복잡한 정의를 선정. ↩︎

관련 글

소수 판별 튜링머신

·3 분
이번 SCTF(Samsung CTF)의 700점짜리 문제로 튜링머신 설계가 나왔다. 총 4개의 문제 중 3개는 일반적인 오토마타 교과서에서 다루는 언어들(ex: \(L=\{ 0^n1^n | n \geq 0 \}\))이었지만 마지막 단계인 \(L=\{0^p | p \, \mathrm{\,is\,prime}\}\)가 다소 많은 시간을 잡아먹었다. 사실상 소수 판별을 하는 튜링 머신을 설계하라는 문제였는데, 기억상 다른 교재들에서도 이에 대한 명확한 실례는 나와있지 않아 따로 정리했다. 아래 등장하는 그래프의 화살표에서 A/B . P는 “현재 state에서 알파벳 A를 읽었을 때, 테이프에 B를 쓰고 P방향(L, 또는 R)으로 이동한다"라는 의미를 가진다. 이제 필요한 서브루틴을 하나씩 구성해 보자.

Coq 02 - 자연수 덧셈의 결합법칙 증명

·5 분
대부분의 reference가 그렇듯이 읽다보면 쉽게 지루해진다. 지루함을 덜하고 Coq에 익숙해지기 위해 일단 예제를 조금씩 따라해보면서 익혀가기로 했다. 자바를 처음 배울 때 public static void main(int argv, char **argc)가 정확히 뭔지는 몰라도 “Hello World” 부터 찍어보는 그런 마음으로 진행해봤다. CoqIDE 실행 프로그램을 처음 실행하면 다음과 같은 화면을 볼 수 있다. 크게 3가지 창으로 나뉘어져 있으며, 왼쪽은 코드 작성을, 오른쪽 위/아래는 각각 Proof 보조/결과 출력을 담당하는 역할이었다.

Coq 01 - Introduction

·6 분
<Interactive Theorem Proving and Program Development>라는 책을 추천받아 조금씩 읽어보기로 했다. 첫 장에서는 Coq에 대한 간단한 안내를 진행한다. 책의 순서를 따라가면서 하나씩 알아보자 타입 먼저 Coq에서는 Gallina라는 언어를 사용해 요구사항(Specification)을 서술한다. 대부분의 다른 프로그래밍 언어들과 마찬가지로 타입은 declaration과 typing rule에 의해 정해진다.