사칙연산 (Arithmetic Operations)
정의
사칙연산 (Arithmetic Operations) 은 덧셈, 뺄셈, 곱셈, 나눗셈의 정확한 구현을 다루는 PS 태그. 큰 수 표현 (large integer), 오버플로우 (overflow), mod p 사칙연산, 부동소수점 정밀도 (floating point precision) 등 기본 연산의 함정을 포함.
정수 타입 범위 (int: -2^31 ~ 2^31-1, long long: -2^63 ~ 2^63-1), 부동소수점 정밀도 (double`: 15자리), mod 연산의 음수 처리 등은 모든 PS 문제에 영향.
문제 상황과 동기
PS 에서 사칙연산 태그는 다음 네 실수를 피하기 위한 검증.
- 오버플로우:
int a = 2e9; int b = 2e9; int c = a + b;→ c 는 음수. long long 필요. - 큰 수: N 이 10^18 이상이면 long long 도 부족. Python
int또는 BigInteger, 또는 mod 연산으로 우회. - 나눗셈의 정수 몫:
5 / 2 = 2(정수 나눗셈). 부동소수점 변환 조심. - mod 음수: C++/Java 에서
(-5) % 3 = -2(Python 은 1). 음수 처리 필요. - 부동소수점 오차:
0.1 + 0.2 != 0.3(IEEE 754). epsilon 비교.
핵심 통찰: 타입 범위를 미리 파악 하고, 중간 계산 오버플로우 를 피한다.
실무 / PS 위치: 큰 수 라이브러리, 암호학 (RSA), 정밀 수치 계산 (금융, 물리 시뮬레이션).
시각화
핵심 아이디어
정수 타입 범위
| 타입 | 범위 | 비고 |
|---|---|---|
int (32bit) | -2^31 ~ 2^31-1 | 약 -2·10^9 ~ 2·10^9 |
long long (64bit) | -2^63 ~ 2^63-1 | 약 -9·10^18 ~ 9·10^18 |
Python int | 무제한 | GMP 기반 가변 길이 |
Java BigInteger | 무제한 | 느림 (O(N^2) 곱셈) |
오버플로우 방지
// WRONG
int a = 2'000'000'000, b = 2'000'000'000;
int c = a + b; // overflow, c < 0
// RIGHT
long long a = 2'000'000'000, b = 2'000'000'000;
long long c = a + b; // OK
곱셈도 마찬가지: 10^5 × 10^5 = 10^10 > int max.
mod 연산 음수 처리
int mod(int a, int M) {
return ((a % M) + M) % M;
}
C++/Java 에서 a % M 가 음수일 때 + M 필요. Python 은 자동 양수.
부동소수점 비교
const double EPS = 1e-9;
bool eq(double a, double b) {
return abs(a - b) < EPS;
}
a == b 는 부동소수점에서 거의 항상 틀림. epsilon 비교.
큰 수 (Python)
a = 10**100
b = 10**100
c = a + b # 자동으로 무제한 정밀도
Python 3 의 int 는 임의 정밀도 (arbitrary precision). 단, 시간이 길다 (곱셈 O(N log N) 또는 O(N^2)).
알고리즘
안전한 덧셈/곱셈 (C++)
safe_add(a, b, M):
if a + b overflows:
use long long or mod M
return (a + b) mod M
safe_mul(a, b, M):
return ((long long)a * b) % M
정수 나눗셈 올림
ceil_div(a, b): # a, b > 0
return (a + b - 1) / b
또는 (a - 1) / b + 1. 단, a=0 일 때 조심.
mod 음수 처리
mod(a, M):
r = a % M
if r < 0: r += M
return r
구현
// 오버플로우 방지, mod 음수 처리
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll safe_mod(ll a, ll M) {
return ((a % M) + M) % M;
}
ll safe_add(ll a, ll b, ll M) {
return safe_mod(a + b, M);
}
ll safe_mul(ll a, ll b, ll M) {
return safe_mod(a * b, M);
}
ll ceil_div(ll a, ll b) { // a, b > 0
return (a + b - 1) / b;
}
int main() {
ll a, b, M;
cin >> a >> b >> M;
cout << a << " + " << b << " = " << a + b << "\n";
cout << a << " * " << b << " = " << a * b << "\n";
cout << a << " + " << b << " mod " << M << " = " << safe_add(a, b, M) << "\n";
cout << a << " * " << b << " mod " << M << " = " << safe_mul(a, b, M) << "\n";
cout << a << " / " << b << " (floor) = " << a / b << "\n";
cout << a << " / " << b << " (ceil) = " << ceil_div(a, b) << "\n";
ll neg = -17;
cout << neg << " % " << M << " (C++) = " << neg % M << "\n";
cout << neg << " mod " << M << " (safe) = " << safe_mod(neg, M) << "\n";
}1000000000 2000000000 10000000071000000000 + 2000000000 = 3000000000
1000000000 * 2000000000 = 2000000000000000000
1000000000 + 2000000000 mod 1000000007 = 999999986
1000000000 * 2000000000 mod 1000000007 = 951432069
1000000000 / 2000000000 (floor) = 0
1000000000 / 2000000000 (ceil) = 1
-17 % 1000000007 (C++) = -17
-17 mod 1000000007 (safe) = 999999990복잡도
| 항목 | 값 |
|---|---|
| 정수 덧셈/뺄셈 | O(1) (고정 길이), O(N) (큰 수, N=자릿수) |
| 정수 곱셈 | O(1) (고정 길이), O(N^2) ~ O(N log N) (큰 수) |
| mod 연산 | O(1) (정수), O(N^2) (큰 수) |
| 부동소수점 연산 | O(1) (하드웨어 지원) |
Python int 곱셈은 Karatsuba / Toom-Cook / FFT 로 O(N^1.58) ~ O(N log N). 길이 10^6 자릿수 이상은 느림.
변형 / 활용
| 기법 | 설명 | 복잡도 |
|---|---|---|
| 고정 소수점 | 정수로 10^6 배하여 정밀도 유지 | O(1) |
| 분수 (Fraction) | 분자/분모 쌍으로 정확한 유리수 표현 | O(log N) (GCD) |
| BigInteger | Java/C++ 큰 수 라이브러리 | O(N^2) 곱셈 |
| Python int | 무제한 정밀도 정수 | O(N log N) ~ O(N^2) |
| __int128 | GCC 128bit 정수 (출력 불가, 내부 계산만) | O(1) |
함정
1. int 오버플로우
int a = 100000, b = 100000;
int c = a * b; // overflow, 10^10 > 2^31
곱셈 전에 long long 으로 캐스팅: (long long)a * b.
2. 나눗셈 올림 (ceiling)
int ceil_div(int a, int b) {
return (a + b - 1) / b; // a, b > 0
}
(a - 1) / b + 1 은 a=0 일 때 틀림.
3. mod 음수 (C++/Java)
-17 % 5 = -2 // C++/Java
-17 % 5 = 3 // Python
C++/Java 는 ((a % M) + M) % M 또는 Java 8+ Math.floorMod(a, M).
4. 부동소수점 ==
double a = 0.1 + 0.2;
if (a == 0.3) { /* never true */ }
if (abs(a - 0.3) < 1e-9) { /* correct */ }
IEEE 754 오차로 0.1 + 0.2 = 0.30000000000000004. epsilon 비교 필수.
5. long long 도 부족한 경우
N=10^18 의 제곱은 10^36, long long (10^18) 범위 초과. Python 또는 mod 연산으로 우회.
BOJ 연습 문제
| 번호 | 제목 | 정답률 | 링크 |
|---|---|---|---|
| BOJ 1000 | A+B | 58.3% | kokoa-lab |
| BOJ 1001 | A-B | 55.7% | kokoa-lab |
| BOJ 1008 | A/B | 36.2% | kokoa-lab |
| BOJ 1271 | 엄청난 부자2 | 41.5% | kokoa-lab |
| BOJ 2338 | 긴자리 계산 | 48.9% | kokoa-lab |
| BOJ 10757 | 큰 수 A+B | 26.4% | kokoa-lab |
| BOJ 15740 | A+B - 9 | 29.1% | kokoa-lab |
참고
이 글의 용어 (3개)
- 분류 모델 지표: Confusion Matrix, Precision, Recall, F1ml
- 정의 분류 (classification) 모델의 성능을 정량화하는 지표들입니다. 이진 분류를 기준으로 하며, multi-class 로 확장하는 방법도 함께 다룹니다. 핵심 출발점…
- 수학 (Mathematics)algorithm
- 정의 수학 (Mathematics) 은 문제 해결에 필요한 모듈러 산술 (modular arithmetic), 최대공약수/최소공배수 (GCD/LCM), 거듭제곱 (exponent…
- 임의 정밀도 / 큰 수 산술 (Arbitrary Precision)algorithm
- 정의 임의 정밀도 (Arbitrary Precision) 또는 큰 수 산술 (Big Integer / Bignum) 은 고정된 바이트 한계(32-bit, 64-bit) 없이 메모…
💬 댓글