어떤 수를 허용하는지 먼저 정한다
노션의 연산과 수의 구조와 수의 구조를 바탕으로 다시 구성했다. 원문은 『이득우의 게임수학』을 읽으며 남긴 기록이다. 여기서는 설명과 예제를 새로 쓰고, 연산 법칙이 성립하는 조건을 보완했다.
2 - 5를 계산하면 -3이 나온다. 정수에서는 허용되는 결과지만 자연수에서는 벗어난다. 계산을 논할 때는 집합과 연산을 함께 정해야 한다.
| 집합 | 표기 | 원소의 예 | 주의할 점 |
|---|---|---|---|
| 자연수 | 0, 1, 2, … | 이 글은 0을 포함한다. 문헌마다 약속이 다르다. | |
| 정수 | …, -2, -1, 0, 1, 2, … | 소수 부분을 갖는 수는 제외한다. | |
| 유리수 | , | 두 정수의 비이며 분모는 0이 아니다. | |
| 실수 | 유리수, , | 수직선의 점을 나타낸다. | |
| 복소수 | , | 실수부와 허수부를 갖는다. |
이 포함 관계는 다. 프로그래밍 언어의 int, float는 유한한 표현 범위를 가지므로 위 집합 전체와 같지 않다.
닫힘과 연산 법칙
집합 의 이항연산은 인 함수다. 두 입력과 결과가 모두 에 속한다. 익숙한 계산 규칙을 더 작은 집합에 적용할 때 결과가 집합 안에 남는지 확인하는 것을 닫힘의 확인이라고 한다.
자연수의 덧셈은 닫혀 있지만 뺄셈은 그렇지 않다. 정수의 나눗셈도 1 / 2에서 정수를 벗어난다. C# 정수 나눗셈이 0을 반환하는 것은 별도의 언어 규칙이며 유리수 나눗셈과 다르다.
| 성질 | 식 | 실수에서의 예와 반례 |
|---|---|---|
| 교환법칙 | 덧셈·곱셈은 성립한다. 뺄셈은 3-1 != 1-3이다. | |
| 결합법칙 | 덧셈·곱셈은 성립한다. 뺄셈은 (5-2)-1 != 5-(2-1)이다. | |
| 분배법칙 | 곱셈이 덧셈에 대해 분배된다. |
이항연산이라는 이유만으로 세 법칙이 모두 성립하지 않는다. 특히 분배법칙은 두 연산의 관계다. 실수에서 성립한 법칙을 반올림이 있는 float 계산에 그대로 적용해서도 안 된다.
항등원과 역원
항등원은 연산해도 원소를 바꾸지 않는 값이다. 덧셈의 항등원은 0, 곱셈의 항등원은 1이다.
역원은 연산 결과를 항등원으로 되돌리는 값이다.
3의 덧셈 역원은 -3이고 곱셈 역원은 1/3이다. 정수 집합에는 1/3이 없어서 모든 0이 아닌 원소의 곱셈 역원을 제공하지 못한다. 정수 1, -1에는 정수인 곱셈 역원이 있다.
0은 곱셈 역원이 없다. 어떤 수를 곱해도 0이므로 1을 만들 수 없기 때문이다. 0 / 0에도 유일한 답을 줄 수 없다.
체는 두 연산을 갖춘 구조다
체(field)는 덧셈과 곱셈에 대해 다음 조건을 만족하는 집합이다.
- 덧셈은 닫혀 있고 교환·결합법칙, 항등원 0, 모든 원소의 역원을 갖는다.
- 0을 제외한 원소의 곱셈은 닫혀 있고 교환·결합법칙, 항등원 1, 각 원소의 역원을 갖는다.
- 곱셈은 덧셈에 대해 분배된다. 이다.
유리수·실수·복소수는 통상적인 덧셈·곱셈에 대해 체다. 정수는 곱셈 역원 조건을 만족하지 않는다. 뺄셈과 나눗셈은 각각 덧셈 역원과 곱셈 역원으로 표현한다.
사원수는 곱셈 순서를 바꾸면 결과가 달라질 수 있다. 따라서 이 글에서 정의한 가환 체에는 해당하지 않는다. 회전용 단위 사원수는 쿼터니언 글에서 따로 다룬다.
실행 예제: 나머지 연산에서 역원 찾기
체가 반드시 무한 집합인 것은 아니다. 정수를 5로 나눈 나머지 0, 1, 2, 3, 4에 대해 덧셈과 곱셈 뒤 다시 나머지를 취하면 유한체를 얻는다. 예를 들어 2 × 3 = 6 ≡ 1 (mod 5)이므로 2의 역원은 3이다.
다음 전체 코드를 field.mjs로 저장하고 Node.js에서 node field.mjs로 실행한다. 작은 정수만 다루므로 JavaScript 정수 정밀도 범위 안에 있다. 일반적인 큰 정수 모듈러 연산 구현은 아니다.
import assert from 'node:assert/strict';
const mod = (a, m) => ((a % m) + m) % m;
function inverse(a, m) {
for (let b = 1; b < m; b++) {
if (mod(a * b, m) === 1) return b;
}
return null;
}
assert.equal(mod(-1, 5), 4);
assert.equal(inverse(0, 5), null);
assert.deepEqual([1, 2, 3, 4].map(a => inverse(a, 5)), [1, 3, 2, 4]);
assert.equal(inverse(2, 6), null);
assert.equal(mod(2 * 3, 6), 0);
for (let a = 0; a < 5; a++) {
assert.equal(mod(a + 0, 5), a);
assert.equal(mod(a + mod(-a, 5), 5), 0);
if (a !== 0) assert.equal(mod(a * inverse(a, 5), 5), 1);
for (let b = 0; b < 5; b++) {
assert.equal(mod(a + b, 5), mod(b + a, 5));
assert.equal(mod(a * b, 5), mod(b * a, 5));
for (let c = 0; c < 5; c++) {
assert.equal(mod(mod(a + b, 5) + c, 5), mod(a + mod(b + c, 5), 5));
assert.equal(mod(mod(a * b, 5) * c, 5), mod(a * mod(b * c, 5), 5));
assert.equal(mod(a * (b + c), 5), mod(a * b + a * c, 5));
}
}
}
console.log('mod 5: all checks passed');
검사가 통과하면 마지막 문장이 출력된다. 6으로 나눈 나머지는 2의 곱셈 역원이 없으므로 체가 아니다. 유한한 몇 개의 일반 실수를 검사해서 모든 실수의 법칙을 증명할 수는 없지만, 이 예제의 mod 5 집합은 원소 전체를 순회한다.
실습
- 자연수에서 덧셈의 항등원과 모든 덧셈 역원을 제공할 수 있는지 판단한다.
- mod 7에서 3의 곱셈 역원을 찾는다.
- mod 8에서 2의 역원이 없는 이유를 짝수·홀수로 설명한다.
- 실수에서 가 되는 과정을 분배법칙으로 전개한다.
풀이
- 이 글의 자연수는 0을 포함하므로 항등원은 있다. 양수의 음수는 집합 밖이어서 모든 덧셈 역원을 제공하지 못한다.
- 이므로 5다.
- 2에 정수를 곱한 값은 짝수다. 8로 나눈 나머지도 짝수라서 1이 되지 않는다.
- 다. 실수 곱셈의 교환법칙으로 이므로 가운데 두 항을 합칠 수 있다. 비가환 곱셈에서는 이 단계를 그대로 쓸 수 없다.
확인한 범위와 다음 글
예제의 mod 5 전체 조합과 mod 6 반례를 Node.js에서 실행했다. 이는 수학 연산을 설명하는 학습 코드이며 프로젝트의 게임 로직을 이식한 코드는 아니다. AI를 사용해 원문 대조, 새 설명·예제 작성과 검사를 진행했다.
다음 수직선과 부동소수점에서는 수학적 실수와 실제 계산 사이의 차이를 다룬다. 전체 순서는 게임 수학 읽기 순서에서 확인할 수 있다.