본문으로 건너뛰기
김신건의 로그

조화수 (Harmonic Number)

· 수정 · 📖 약 3분 · 1,125자/단어 #algorithm #math #harmonic-number #number-theory
harmonic number, 조화수, H_n, harmonic series, harmonic-number

정의

조화수 (Harmonic Number) H_n 은 자연수 n 에 대해 역수의 합 Σ_{k=1}^{n} 1/k 으로 정의. 점근적으로 H_n = ln n + γ + 1/(2n) - ... (γ ≈ 0.5772, 오일러-마스케로니 상수). 정수론과 알고리즘 분석에서 O(n log n) 의 log factor 가 사실은 H_n 에서 비롯.

문제 상황과 동기

n = 10^6 일 때 Σ_{k=1}^{n} ⌊n/k⌋ 또는 약수 배수 관계의 합.

  • naive (이중 루프): O(n²). n=10^5 면 10^10, 불가능.
  • 조화수 통찰: n/k 의 서로 다른 값은 O(√n) 개. 구간 나누기로 O(√n) 또는 O(n log n).

핵심 통찰: ⌊n/k⌋ 는 k 가 작을 때는 자주 변하지만, k 가 클 때는 천천히 변한다. 따라서 같은 몫을 갖는 구간 을 한 번에 처리할 수 있다.

시각화

Floor Sum 블록 분할 흐름

Σ ⌊n/k⌋ 의 블록 분할 과정. 같은 몫을 갖는 구간을 한 번에 처리:

flowchart TD
    A["l = 1, sum = 0"] --> B{"l <= n?"}
    B -->|아니오| G[합계 반환]
    B -->|예| C["q = n / l  (현재 몫)"]
    C --> D["r = n / q  (같은 몫의 구간 끝)"]
    D --> E["sum += q * (r - l + 1)"]
    E --> F["l = r + 1  (다음 블록 시작)"]
    F --> B

블록 개수는 최대 2 * sqrt(n) 이므로 전체 O(sqrt(n)).

핵심 아이디어

조화수 점화

H_0 = 0
H_n = H_{n-1} + 1/n

구간 나누기 (harmonic lemma)

⌊n/k⌋ 의 값이 같은 k 의 구간 [l, r]:

r = n / (n / l)

이 구간 내에서 ⌊n/k⌋ = q 로 일정. 합을 q × (r - l + 1) 로 O(1) 에 계산.

이 기법은 각 구간을 묶어서 O(√n) 번의 연산으로 모든 ⌊n/k⌋ 의 합을 구하게 해 준다.

약수 배수 합

Σ_{i=1}^{n} d(i) = Σ_{i=1}^{n} ⌊n/i⌋ = 2 Σ_{i=1}^{√n} ⌊n/i⌋ - (⌊√n⌋)²

조화수와 O(n log n) 복잡도

n 개의 원소에 대해 i 의 배수를 모두 방문하는 패턴:

for i = 1 to n:
    for j = i, 2i, 3i, ... <= n:
        f(j)

총 방문 횟수 = n/1 + n/2 + n/3 + ... + n/n = H_n * n = O(n log n)

에라토스테네스의 체, 약수 열거, 소수 판별 등에서 이 패턴이 나타날 때마다 O(n log n).

점근 근사

nH_nln(n)차이 (γ ≈ 0.5772)
102.9292.3030.626
1005.1874.6050.582
10007.4856.9080.577
10^614.39313.8160.577

H_n - ln(n) → γ 로 수렴. n 이 커질수록 근사 오차 0.

알고리즘

harmonic_sum(n):
    sum = 0
    for i = 1..n:
        sum += 1.0 / i
    return sum

harmonic_floor_sum(n):        # Σ ⌊n/i⌋
    sum = 0
    l = 1
    while l <= n:
        q = n // l
        r = n // q
        sum += q * (r - l + 1)
        l = r + 1
    return sum

구현

// H_n 계산 + harmonic floor sum O(sqrt(n))
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

double harmonic(int n) {
  double h = 0;
  for (int i = 1; i <= n; i++) h += 1.0 / i;
  return h;
}

ll floor_sum(ll n) {
  ll sum = 0;
  for (ll l = 1, r; l <= n; l = r + 1) {
      ll q = n / l;
      r = n / q;
      sum += q * (r - l + 1);
  }
  return sum;
}

int main() {
  int n; cin >> n;
  cout << fixed << setprecision(10);
  cout << "H_" << n << " = " << harmonic(n) << "\n";
  cout << "Sigma n/i = " << floor_sum(n) << "\n";
  return 0;
}
stdin
10
결과
H_10 = 2.9289682540
Sigma n/i = 27

복잡도

항목
H_n 직접 계산O(n) 시간, O(1) 공간
Floor sum (구간)O(√n) 시간, O(1) 공간
Σ d(i) (약수 개수 합)O(√n)

변형 / 활용

패턴설명복잡도
Σ ⌊n/i⌋floor sum, O(√n)구간 나누기
Σ i·⌊n/i⌋가중치 곱2중 구간
Σ d(i)약수 개수 합O(√n)
Σ σ(i)약수 합O(√n)
Divisor summatoryD(n) = Σ_{i≤n} ⌊n/i⌋정수론 핵심

2D floor sum (확장)

2차원 버전: Σ_{i=1}^{n} Σ_{j=1}^{m} ⌊(ai+bj+c) / d⌋. 주로 격자 점 개수 계산에 등장. 복잡도 O(log max(n,m)) 의 floor_sum 라이브러리 함수 (C++17 atcoder/math.hpp) 로 처리.

약수 개수 합 O(√n) 구현 (확장)

D(n) = Σ_{i=1}^{n} d(i)  (d(i) = 약수 개수)

= 2 * Σ_{i=1}^{floor(√n)} floor(n/i) - floor(√n)^2

이 공식으로 O(√n) 에 1 부터 n 까지 모든 약수 개수 합 계산.

함정

1. 부동소수점 정밀도

H_n 은 float/double 로 n ≥ 10^7 이면 오차 누적. 큰 n 에 대해서는 log(n) + γ 근사식 사용.

2. 64-bit 오버플로우

floor_sum(n) 에서 n=10^12 면 q * (r-l+1)long long 초과 가능. unsigned long long 또는 __int128 필요.

3. 구간 경계 실수

l = r + 1 에서 ln+1 이면 루프 종료. r = n / (n / l) 에서 n / l == 0 이면 0으로 나누기 방지: l > n 이면 break.

4. 에라토스테네스 체와 혼동

조화수의 O(n log n) 분석을 에라토스테네스 체 O(n log log n) 과 혼동하는 경우 있음. 체는 소수에만 방문하므로 log log n. 배수 전체 방문은 H_n * n = O(n log n).

WARNING

floor_sum 에서 n = 10^12 이면 q * (r - l + 1)long long 최대값 9.2 * 10^18 를 초과할 수 있다. q(r-l+1) 모두 O(sqrt(n)) 이므로 곱은 O(n), n = 10^12 이면 10^12 범위. long long 은 안전하지만 __int128 없이 추가 곱셈 있으면 오버플로우.

BOJ 연습 문제

번호제목정답률링크
BOJ 1222홍준 프로그래밍 대회25.6%kokoa-lab
BOJ 2247실질적 약수37.3%kokoa-lab
BOJ 1241머리 톡톡34.6%kokoa-lab
BOJ 2900프로그램-kokoa-lab

참고

이 글의 용어 (3개)
에라토스테네스의 체 (Sieve of Eratosthenes)algorithm
정의 에라토스테네스의 체 (Sieve of Eratosthenes) 는 1부터 N 까지의 모든 소수를 O(N log log N) 에 찾는 고대 그리스 알고리즘. 기원전 240 년…
Inclusion and Exclusion: 포함배제 원리algorithm
정의 포함배제 원리 (Principle of Inclusion-Exclusion, PIE) 는 합집합의 크기를 교집합의 크기들을 이용해 계산하는 조합론의 기본 원리입니다. $$ …
Mobius Function, Mobius Inversionalgorithm
정의 Mobius Function 은 양의 정수에 대한 수론 함수: Mobius Inversion (뫼비우스 역원) 은 합 / 컨볼루션 관계의 역변환. PS 에서는 GCD / 서…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기