레이블이 알고리즘인 게시물을 표시합니다. 모든 게시물 표시
레이블이 알고리즘인 게시물을 표시합니다. 모든 게시물 표시

2018년 1월 18일 목요일

프로그래밍 때 골치 아픈 역삼각함수 처리법

아주 가끔 프로그래밍을 할 때가 있는데 역삼각함수만 나오면 결과가 꼬여서 골치 아프게 한다. 그래서 처리 방법을 정리 해 둔다. 역삼각함수는 0~90도 범위 안에서만 사용하면 아무 문제가 없다. 이 범위를 벗어나면 부호를 따져서 제대로 된 각도를 얻어야 한다.





삼각함수는 직각좌표계를 극좌표계로 바꾸면서 무한 회전(무한 각도)하면서 반복하는 값들로 만들어진 함수라 1대1 대응이 안 된다. 고로 역함수를 구할 때는 각도 범위를 제한할 수밖에 없다. 이 범위가 sin, cos, tan 모두 다르기 때문에 섞어 사용하면 계산 결과가 꼬인다. 그럼 어디가 문제인지 정말 찾기 힘들게 된다. 역함수를 쓸 때는 0~90도로 범위를 제한한다.

삼각함수를 쓸 경우는 시계회전(0~360)인지 좌우회전(-180~+180)인지만 결정해서 경계를 넘어갈 때 처리만 해 주면 문제없다. 표시는 각도로 하고 계산할 때는 라디안을 사용하는 게 아주 편하다. 이것 외엔 문제가 없다.

각도를 구해야하는 경우는 역삼함수를 써야 하는데 이 경우 사분면 결정을 해야 한다. 사분면에 따라 각도 보정을 해 주어야 한다. 또한 프로그램 할 때는 항상 특이 지점(0, 무한)에 대한 처리를 해 주어야 한다. 나누기 하는 숫자가 0인지 아닌지 등 말이다.





계산에 쓰이는 수학 좌표계와 컴퓨터 모니터 화면의 좌표계 차이로 계산이 번거롭다. 마우스로 입력을 받을 때도 마찬가지다. 수학에서 각도 방향과 시계의 각도 방향이 달라서 계산이 번거롭다. 이런 경우 표현 좌표와 계산 좌표 사이의 변환이 필요하다. 복잡한 계산은 수학 좌표계로 하고 마지막에 표현만 화면에 맞게 바꾼다. 보통 프로그램 할 때는 나중에 고치기 쉽도록 다음 3가지를 분리한다.


  1. 계산/프로그램 : 예) 게임 엔진
  2. 자료/데이터 : 예) 유닛 객체 데이터
  3. 표현/디자인 : 예) 유닛 애니메이션 이미지






매일 계란을 먹듯이
많은 생명이 태어나자마자 죽는다.
우리도 그처럼 이 땅에 버려진 것들이다.
죽은 후엔 영원한 평온이 있겠지.

2017년 1월 23일 월요일

컴퓨터 부동 소수점, 고정 소수점 이해, Visual Basic Macro에서 사용하는 데이터 형식

2026-08-12 수정

수학에서 말하는 수의 종류와 컴퓨터에서 표현법이다.

  • 자연수自然數 : 0 이상의 양의 정수, 예) 0, 1, 2, 3...
  • 정수整數 : 소수가 없는 정수, 예) –3, -2, -1, 0, 1, 2, 3
  • 소수小數 : 0.XXX로 1보다 작은 수, 예) 0.123, 0.01%
  • 실수實數 : 정수整數 + 소수小數, 예) 123.456, 3.141592
  • 고정固定 소수점小數點 표현 : 예) 정수부 3자리 + 소수부 3자리 = 123.456
  • 부동浮動 소수점小數點 표현 : 예) 가수부 3자리 x 지수부 3자리 = 0.123×10¹²³
  • 가수假數 : 소수점 이하 부분 = 소수 ↔ 진수眞數·정수整數
  • 지수指數 : 우측 어깨 위에 붙어 거듭 제곱을 나타내는 수

숫자를 표현할 수 있는 공간(칸)이 6개라고 하자. 여기서 고정 소수점이란 소수점의 위치가 고정 되어 있음을 뜻한다. 소수점 위 3자리, 소수점 아래 3자리처럼 말이다. 부동 소수점이란 소수점 위치가 가변적이란 얘기다. 부동浮動float플로우트는 떠다님을 의미한다. 다른 말로 지수 형태 표현이라고 할 수 있다. 소수점의 위치는 지수指數가 결정하고, 수의 정밀도는 가수가 결정한다. 가수假數란 가짜 수란 의미인데 소수점 이하를 의미한다. 그러니까 소수인 거지.

컴퓨터 메모리를 너무 어렵게 생각하지 말자. 우리가 초등학교 때 사용하던 원고지처럼 바둑판 격자가 그려진 공책을 상상하면 된다. 각 칸에는 글자 하나 적을 수 있다. 숫자를 적을 공간에 한계가 있는 것은 당연하다. 컴퓨터 메모리도 그러하다. 한 바이트라고 하는 것을 알파벳 문자가 들어가는 자리라고 생각하라. 


예를 들면 분해능 1000의 저울이 있다고 하자. 1g단위로 1kg까지 측정하는 저울이 있고, 1kg단위로 1톤까지 측정하는 저울이 있다. 실제 저울은 보통 1/100 정밀도이다. 여기서 저울 눈금에 표시되는 숫자는 가수에 해당한다. 눈금 간격이 정밀도(분해능)를 나타낸다. 그 뒤의 단위(g/kg)는 지수에 해당한다. 이건 규모(크기)를 나타낸다. 이 정도 설명해도 이해 못 하면 국정원 댓글 용역들이거나 일베충이다.

그런데 기억 공간이란 것은 한계가 있다. 공간(칸수) 제약이 있는 숫자 표시를 할 때는 2가지 개념이 있다. 측정기나 컴퓨터 속의 수를 너무 어렵게 생각하지 말자. 중앙이 0이고 좌우가 ±30㎝인 자를 상상하자. 이 자의 최소 눈금은 1㎜이다. 그럼 이렇게 정리 할 수 있다.

  • 표시 범위 : ±300㎜ → 자의 크기
  • 최소 눈금 : ±1㎜ → 정밀도/분해능

범위는 얼마나 큰 수, 작은 수를 표현할 수 있느냐이고, 눈금은 얼마나 섬세하게 나누어 표현할 수 있느냐이다. 이런 숫자 표현을 컴퓨터에선 비트 나열로 어떻게 할까? 그게 궁금하다. 2진수와 10진수를 바꾸는 개념이 필요하다. 몇 개의 숫자는 기억하자.

  • 1바이트(08비트) : 256개 정수 표현
  • 2바이트(16비트) : 6만5536개 정수 표현
  • 4바이트(32비트) : 42억9496만7296개 정수 표현
  • 8바이트(64비트) : 184경4674조4073억7095만51616개 정수 표현


Visual Basic Macro에서 사용하는 데이터 형식


컴퓨터에서 하나의 데이터 표현에 사용하는 기억 공간은 현재 8바이트(64비트)까지이다. 1바이트(8비트)에서 시작해서 2배씩 증가하는 것이 전통이다. 주어진 바이트 공간이 같아도 내용을 어떻게 해석하느냐의 차이가 있어 Data Type(자료 종류)이라 부른다.

  • 1바이트 : byte(자연수)
  • 2바이트 : integer(정수), boolean(논리)
  • 4바이트 : long(정수), single(실수)
  • 8바이트 : longlong(정수), double(실수), currency(화폐), date(시간)


대부분 컴퓨터 언어에선 전통적인 숫자 표현이다. 정수의 경우는 이해하기 쉽다. 고로 실수를 어떻게 표현하는지 분석해 볼 것이다. single, double, date는 부동 소수이고, currency가 고정 소수이다. 기타 흥미로운 자료형도 있다.

  • 14바이트 : decimal(10진수)
  • 16바이트 : variant(가변적)


decimal은 10진수를 표현한 것이다. 이건 단독 사용이 안 되고 variant와 함께 사용해서 다른 수를 10진수 형식으로 바꾼다. variant는 여러 형태의 데이터를 다 표현할 수 있도록 넓힌 공간이라 가장 많은 바이트를 차지한다. 추가된 2바이트는 자료 형식에 대한 정보이다. 고로 10진수 표현이 어떠한지 분석해 볼 것이다.

기타 object(객체), longptr(포인터) 등은 메모리 주소(위치)와 데이터 구조를 나타내는 자연수로 사람이 직접 건드릴 일이 없다. 데이터 구조란 여러 종류의 자료형을 묶어서 한 덩어리로 취급할 때 필요한 것이다. 고로 위치/크기/종류를 모두 구분해야 한다. 주소의 바이트 크기도 시스템(CPU + O/S)에 따라 자동으로 바뀌니까 신경 쓸 일 없다. 이와 유사한 자료 형식이 있다.

  • string(고정) : 문자열 길이(약 6만까지)
  • string(가변) : 10바이트 + 문자열 길이(약 20억까지)
  • variant(문자열) : 22바이트 + 문자열 길이(약 20억까지)


길이가 가변적인 자료형의 대표적이고 전통적인 것이 문자열이다. 길이가 고정된 문자열은 보통 단어 목록(사전)을 표현할 때 사용한다. 최대 한계 길이(약 6만까지 가능)를 정해서 사용한다. 단어 길이가 6만(65536자)이면 한글 250자 원고 (10행x25자) 240 페이지 분량, 500자 원고 (25행x20자) 120 페이지 노트 정도 된다. 여하튼 최대 단어 길이를 정하고 미리 공간을 확보하는 것이다. 이게 말이 단어지 이 정도면 문서 하나 정도가 들어간다.

길이가 가변적인 것은 보통 문장(문단)을 표현할 때 사용한다. 메모리는 절약 되지만 구현이 복잡하다. 문자열은 바이트의 배열이 된다. 고로 문자열 변수는 이 배열의 주소(위치)를 가리키게 되면서 문자열 길이도 표현해야 한다. 고로 4바이트(약 20억)는 길이, 나머지 4바이트는 주소(위치)를 표현하는 것 같은데 남은 2바이트는 뭔지 모르겠으나 컴퓨터가 알아서 할 일이다.

데이터를 비트로 표현하는 방법을 분석하기 위해 사용할 프로그램이다.


2진 부동 소수점 형식 분석


10진수로 된 실수를 2진수로 바꾸는 것은 정확하지 않고, 계산도 복잡하다. 고로 가수, 지수, 부호 비트를 구분해서 분석하려면 대가리를 좀 굴려야 한다. 2진수는 m×2ⁿ 형태이고 m(가수), n(지수)는 부호가 있다. n과 m의 변화를 보기 위해서 다음과 같은 수를 저장해 본 후에 분석한다.


m
n
10진양수
10진음수
1
0
1
-1
1
1
2
-2
1
2
4
-4
1
3
8
-8
1
4
16
-16
1
5
32
-32
1
6
64
-64
1
7
128
-128
1
8
256
-256
1
9
512
-512
1
-1
0.5
-0.5
1
-2
0.25
-0.25
1
-3
0.125
-0.125
1
-4
0.0625
-0.0625
1
-5
0.03125
-0.03125
1
-6
0.015625
-0.015625
1
-7
0.0078125
-0.0078125
1
-8
0.00390625
-0.00390625
1
-9
0.001953125
-0.001953125
0.1
0
0.5
-0.5
0.11
0
0.75
-0.75
0.111
0
0.875
-0.875
0.1111
0
0.9375
-0.9375
0.11111
0
0.96875
-0.96875
0.111111
0
0.984375
-0.984375
0.1111111
0
0.9921875
-0.9921875
0.11111111
0
0.99609375
-0.99609375
11
0
3 -3
111
0
7 -7
1111
 0
15 -15
11111
0
31 -31
111111
0
63 -63
1111111
0
127 -127
11111111
0
255 -255


간단한 비교 분석을 통해 다음과 같은 결론에 도달한다. 첫 비트는 부호, 그 다음은 지수, 그 다음은 1이 생략된 가수이다. 바이트의 배치 순서는 CPU에 따라 다른데 PC에서 사용하는 인텔 CPU는 하위 바이트를 먼저 기록한다. 그래서 거꾸로 배치해야 사람이 읽기 편하다. 지수는 비트 1자리만 늘어도 2배로 커지는 효과가 있다. 가수는 비트가 2배로 늘어야 2배 정밀도가 된다.

  • 32비트 실수 최소 눈금 : ±1.401298E-45 (2진수이기 때문에 이렇다)
  • 64비트 실수 최소 눈금 : ±4.94065645841247E-324 (2진수이기 때문에 이렇다)

최소 값일 때는 비트 모양이 지수 = 0, 가수 = 000...0001 꼴이다.








날짜와 시간 표현은 64비트 부동 소수점 형식을 빌려 사용하고 있다. 초→분→시→일→월→년으로 1초 시간에 해당하는 소수를 계속 누적하는 형태이다. 1일은 정수 1에 해당한다. 고로 1일을 60초 * 60분 * 24시 = 86400초로 나누면 단위 시간 1초에 해당하는 소수를 구할 수 있다. 365일은 평년, 366일은 윤년이다.

  • 최소 눈금 : 1초 =  0.0000115740740740740... (740무한 반복)
  • 최대 범위 : 1900년 1월 0일 자정(0시0분0초) ~ 약 9999년(1만년)까지





10진 고정 소수점 형식 분석


돈 계산할 때 사용하는 Currency 고정 소수점 형식은 정수와 다를 것 없다. 본질적으로 정수이다. 정수처럼 첫 비트가 부호이다. 양수=0, 음수=1이며 음수는 정수처럼 보수를 취한다. 10진수로 소수점 아래 4자리까지 표현하고 싶다면 10000에 해당하는 수를 1로 보는 방식이다. 다시 말하면 1만 배 곱하여 저장, 계산한 후에 10진수로 결과 표시할 때만 소수 4자리 앞에 살짝 소수점을 찍어 준다. 고로 비트 패턴으로는 정수와 소수의 구분이 없고 그냥 1만배가 된 정수이다.

  • 최소눈금 : ±0.0001 (10진수라서 최소 눈금을 1로 표현 가능)
  • 최대범위 : 약 ±2의 63승 ÷ 10000






10진 부동 소수점 형식 분석


Decicmal의 형식을 읽을 수는 없었으나 10진법이라는 것도 Currency처럼 최소 눈금을 정하고 정수처럼 취급하는 방법으로 보인다. 왜냐하면 최소 눈금은 10진수로 표현했을 때만 1로 끝나기 때문이다. 또한 최대 범위를 보면 약 12바이트가 가수로 사용된다. 고로 남은 2바이트가 부호와 지수가 될 것이다. 헌데 지수가 있다는 말은 부동 소수란 말이다.

  • 최소 눈금 : ±0.0000000000000000000000000001, 10진수 29자리 표시
  • 최대 범위 : 약 ±2의 96승(12바이트), 10진수 29자리 표시

이 방법은 가수는 일반 정수처럼 취급하고, 지수는 10진수로 보는 방식이다. 계산은 내부적으로 어떤 방식으로 알아서 할 것이고, 화면에 표시할 때만 소수점 위치를 살짝 결정해 찍어주는 방법이다. 이렇게 하는 이유는 계산의 정확도를 위함이다. 10진 소수를 2진 소수로 정확히 바꿀 수는 없다. 허나 정수에선 가능하다. 고로 2진수 1을 10진수 최소 눈금으로 잡은 것이다.

이 방법으로도 곱하기 나누기에선 문제가 없다. 가수는 서로 곱하고 나누고, 지수는 서로 더하고 빼면 된다. 문제는 더하기 빼기이다. 지수가 큰 쪽으로 자리수를 맞추어야 하는데 가수는 2진 정수인데 지수는 10진수를 표시하니 복잡하다. 뭐 알아서 잘 했겠지.

이제 정수로 실수를 표현하는 원리를 알았으니 직접 구현할 수도 있을 것이다. 대충 이렇게 마무리한다.






독재정부가 하는 짓은 동서고금 똑같다.
전쟁터에서 하는 짓은 동서고금 똑같다.
한 나라의 역사만 제대로 알아도 나머지 나라 역사를 안다. 외국사 = 한국사
그러니 독재자 이승만, 박정희, 김일성, 한국 전쟁에 대한 환상을 깨자.
우리가 학교에서 배운 한국사는 1920년부터 일본이 가르친 일제 식민 사학이다.
근현대사 또한 친일파 세력 때문에 제대로 교육 받았다고 할 수 없다.

2017년 1월 21일 토요일

Excel(엑셀)로 File Debugger(파일 디버거) 만들기 & 유니코드 파일 분석

옛날 DOS 시절에 사용하던 디버거 프로그램이 없어서 Excel로 만들었다. CPU, O/S, 컴퓨터 언어가 진화하면서 옛날 기계어(어셈블리어)를 사용할 필요가 없어졌다. 고로 CPU 명령어 해석은 불필요하니 파일 데이터만 분석하는 기능만 만들면 된다. Excel의 편리한 인터페이스와 계산 능력을 사용할 수 있어 훨씬 좋은 디버거를 만들 수 있다.





위 화면은 파일의 일부를 바이트 단위로 읽어 여러 방법으로 해석해서 보여주는 화면이다. 옛날 디버거를 사용해 보고 필요하겠다싶은 해석을 넣었다. 일부 바이트를 수정한 후에 써넣을 수도 있다. 그러니 원본 파일을 이름을 바꿔 보전한 후에 해킹을 해야 한다.

8비트 정수를 10진수, 16진수, 2진수로 나타낸다. 16진수는 디버거에서 많이 보는 형태이나 별로 쓸모가 없다. 때론 2진수 패턴을 분석할 필요가 많다. 그리고 16비트로 묶어 정수로 볼 필요도 있다. 문자열이 섞인 경우를 보기 위해 7비트 아스키 문자, 16비트 한글 완성형 문자로 묶어 표현한다. 먼저 기록된 바이트를 상위 바이트로 보는 것은 완성형 코드이고, 하위 바이트로 보는 것은 16비트 정수일 때다.

파일에 따라 때론 32비트 정수, 유니코드 문자 등으로 해석할 필요가 있는데 원리가 같으니 직접 우측 공백에 함수를 이용해 만들어 넣으면 된다. 이런 다양한 해석은 Excel 함수를 이용해서 직접 만들어 넣으면 된다. 





위 화면은 2개 파일의 헤더부분을 비교할 때 사용하는 화면이다. 같은 종류의 파일은 헤더(머리) 부분의 패턴이 거의 같다. 고로 기준이 되는 파일과 내용에 있어 약간의 차이가 나는 비교 파일에서 그 차이 나는 부분만 찾아 표시해 줘서 파일 구조 해석에 도움을 준다.

위와 같이 화면을 만든 후에 다음 코드를 모듈에 복사해 넣어 버튼에 연결시킨다. 화면에서와 같이 정확한 행렬(가로/세로) 위치에 만들어야한다. 그림에서와 같은 Test 파일을 만들어 동작 시험을 해 본다.


Public Sub ReadFile()
    '주소, 값 영역 지우기
    Range("A5:B5").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    '파일 이름, 주소, 읽을 수량 지정
    Filename = Worksheets("디버거").Cells(1, 4).Value
    ByteAddress = Worksheets("디버거").Cells(2, 4).Value
    ByteCount = Worksheets("디버거").Cells(3, 4).Value
    Dim ByteValue As Byte
    '내용 읽어서 배치, EOF보다는 LOF를 쓰는 것이 깔끔하다.
    Open Filename For Binary As 1
        For i = 0 To ByteCount - 1
            If ByteAddress + i > LOF(1) Then Exit For
            Get #1, ByteAddress + i, ByteValue
            Worksheets("디버거").Cells(5 + i, 1).Value = ByteAddress + i
            Worksheets("디버거").Cells(5 + i, 2).Value = ByteValue
        Next i
    Close 1
    '다음 주소 자동 계산
    Worksheets("디버거").Cells(2, 4).Value = ByteAddress + ByteCount
End Sub


Public Sub WriteFile()
    Filename = Worksheets("디버거").Cells(1, 4).Value
    Dim ByteValue As Byte
    Open Filename For Binary As 1
        i = 0
        ByteAddress = Worksheets("디버거").Cells(5 + i, 1).Value
        Do While ByteAddress <> ""
            ByteValue = Worksheets("디버거").Cells(5 + i, 2).Value
            Put #1, ByteAddress, ByteValue
            i = i + 1
            ByteAddress = Worksheets("디버거").Cells(5 + i, 1).Value
        Loop
    Close 1
End Sub


Public Sub CompareFile()
    '주소, 값 영역 지우기
    Range("A3:B3").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    Range("I3:J3").Select
    Range(Selection, Selection.End(xlDown)).Select
    Selection.ClearContents
    '파일 이름 지정
    FileName1 = Worksheets("비교").Cells(1, 4).Value
    FileName2 = Worksheets("비교").Cells(1, 12).Value
    '파일 길이 비교
    Dim ByteValue1 As Byte
    Dim ByteValue2 As Byte
    Open FileName1 For Binary As 1
    Open FileName2 For Binary As 2
    filelength1 = LOF(1)
    filelength2 = LOF(2)
    MaxLength = filelength1
    minlength = filelength2
    If filelength1 < filelength2 Then
        MaxLength = filelength2
        minlength = filelength1
    End If
    '헤더 부분 비교 차이 나는 부분만 표시
    r = 3
    For i = 1 To minlength
        Get #1, i, ByteValue1
        Get #2, i, ByteValue2
        If ByteValue1 <> ByteValue2 Then
            Worksheets("비교").Cells(r, 1).Value = i
            Worksheets("비교").Cells(r, 2).Value = ByteValue1
            Worksheets("비교").Cells(r, 9).Value = i
            Worksheets("비교").Cells(r, 10).Value = ByteValue2
            r = r + 1
        End If
    Next i
    For i = minlength + 1 To MaxLength
        If filelength1 = MaxLength Then
            Get #1, i, ByteValue1
            Worksheets("비교").Cells(r, 1).Value = i
            Worksheets("비교").Cells(r, 2).Value = ByteValue1
        End If
        If filelength2 = MaxLength Then
            Get #2, i, ByteValue2
            Worksheets("비교").Cells(r, 9).Value = i
            Worksheets("비교").Cells(r, 10).Value = ByteValue2
        End If
        r = r + 1
    Next i
    Close
End Sub


이걸 왜 만들었냐고? 옛날 게임 데이터 파일 해킹하려고. 요즘 것은 암호화 되어 있다.

자 이제 어떻게 사용하는지 보도록 하자. 사용법을 알면 감이 잡힐 것이다. 노트패드에서 숫자, 알파벳, 한글의 시작과 끝 문자를 입력해 넣고 4가지 표준으로 저장한다.


  1. ANSI(기존표준) = 영문 아스키 + 한글 완성형(한글 부족) + MS CP949(한글 보충)
  2. 유니코드(UTF-8) = 통신 목적으로 사용하며 기존 영문 아스키와 호환을 유지한다.
  3. 유니코드(UTF-16 little Endian) = 16비트로 세계 문자 표현. 하위 바이트 먼저 기록.
  4. 유니코드(UTF-16 Big Endian) = 16비트로 세계 문자 표현. 상위 바이트 먼저 기록.


※ Endian(엔디안)은 여러 바이트가 하나의 의미를 나타낼 때 바이트 저장 순서를 말한다. 예를 들어 123(백이십삼)을 저장(기록)할 때 기계에 따라 1, 2, 3 순서와 3, 2, 1 순서로 저장해서 구분이 필요한 것이다. IBM PC의 Intel CPU는 3, 2, 1 순서로 기록한다. 옛날 Mac PC 등 다른 컴퓨터 CPU는 1, 2, 3 순서로 기록할 수도 있다.





기존 파일 형식에선 문자 파일의 경우 아무런 예고도 없이 내용이 나온다. 왜냐하면 파일 확장자에 *.txt로 문자 파일임을 나타내기 때문이다. 계산해 보면 0~9, a~z, A~Z까지 숫자가 순서대로 할당되어 있다. 한글 완성형의 경우는 2바이트를 묶어 해석해야 한다. 이 때 상위/하위 바이트를 앞에 것으로 하느냐 뒤의 것으로 하느냐 정해야 하는데 문자의 경우 먼저 나온 것을 보고 뒤의 것을 해석하기 때문에 앞의 것을 상위 바이트로 본다. 16비트 정수 해석은 여기선 필요 없으니 무시한다.

※ 완성형의 경우 시작과 끝 글자의 순서가 뒤죽박죽인데 띨띨하게 완성형 만들 때 모든 한글을 반영하지 않았다. 약 2천 개만 등록했던 것이다. 그래서 MS사(社)에서 불쌍하다며 CP949란 보충 코드를 만들어 주다보니 이렇게 되었다.

유니코드 중에 UTF-8은 기존의 ANSI 문서 파일이 아님을 나타내기 위해서 첫 3바이트가 헤더 형태로 나온다. 그 다음은 아스키와 호환성을 유지하니 같다. 한글 코드가 3바이트로 할당 되어 있는데 이건 가변길이 코드이다. 1번 바이트의 특징을 보고 2번 바이트를 이어 해석하고, 2번째 바이트를 보고 3번 바이트를 이어 해석하는 방식이다. 그러다 보니 한글이 3바이트나 차지하게 되었다. 이건 유니코드를 공부해야 해석이 가능하다. 비트 패턴을 보고 해석해야 한다.

유니코드 중에 UTF-16 Little Endian 순서가 PC에서 표준인 것 같다. PC에선 16비트 정수의 경우 하위 바이트를 먼저 저장한다. 고로 문자 코드는 16비트 정수 해석과 정확히 일치한다. 기존 ANSI 파일이 아니며 UTF-16 Little Endian이란 헤더가 나온다. UTF-16 Big Endian과 정확히 반대 순서이다. 아스키(ASCII) 코드는 8비트에서 상위 바이트에 0을 더해 16비트로 그대로 확장했고, 한글의 경우 16비트 정수 해석 값이 그대로 문자 코드가 된다.

여기서 UTF-16 Big Endian과 UTF-8 가변 길이 코드를 비교해 보자. Big Endian은 우리가 숫자를 나열하는 방식과 같다. 좌측에 먼저 적는 숫자가 높은 자릿수이다. 낮은 자릿수는 가장 늦게 우측에 적는다. 사람이 기록하는 순서와 컴퓨터가 기록하는 순서가 같다. 이 방식이 인간에겐 더 자연스러운 순서이다.

가 = 1010 1100 - 0000 0000 = 1110 1010 - 1011 0000 - 1000 0000
힣 = 1101 0111 - 1010 0011 = 1110 1101 - 1001 1110 - 1010 0011

이제 알겠는가? UTF-8은 비트의 앞을 보고 뒤를 이어 해석하는 방식이다. 비트의 나머지 부분을 묶어 연결해서 UTF-16으로 만들어서 문자처리를 한다. 왜 이 짓을 하느냐? 아스키 코드와 호환성을 위해서다. 아스키 코드엔 옛날 구식 기계 동작을 제어하는 코드가 포함 되어 있어 그 코드를 피하기 위함이다. 일단 UTF-16으로 바꾸면 아주 쉽게 간단한 계산으로 한글의 초성, 중성, 종성을 분리해 낼 수 있다. 완성형은 이게 불가능하다.

첫 바이트 1110은 총 3바이트란 의미다. 1이 3번 나온다. 0은 그냥 구분용이다. 같은 방식으로 110일 경우는 총 2바이트, 11110일 경우는 총 4바이트란 의미다. 뒤에 이어지는 바이트 모두 10으로 시작한다. 만약 첫 비트가 0이면 그건 아스키 코드와 같다는 뜻이다.

대충 사용법은 알겠지? 16비트 정수 해석 순서를 바꾸려면 빈 셀에 계산식만 추가로 더 입력해 넣으면 된다.








인간처럼 똥 많이 싸고 쓰레기 많이 버리는 동물도 없을 거다.
불필요하게 많이 먹고, 불필요하게 많이 생산한다.
살기 위해 돈 벌어야 하고, 돈 벌려고 쓰레기를 만들어야 한다.
미래엔 진짜 1명만 일하고 9명이 백수인 세상이 올 것이다.
로봇(기계+컴퓨터)에 의한 복지국가 외엔 답이 없을 것이다.

2016년 7월 3일 일요일

2의 존재 이유 : 컴퓨터에서 2진법을 쓰는 이유

우린 왜 10진법을 쓸까? 손가락이 10개라서.
우린 왜 12진법도 쓸까? 1년이 12달이라서.
우린 왜 360도를 쓸까? 1년이 365일이라서.


2진법은 언제부터 나왔을까?


  • 기하학에선 작도만으로 선과 각을 정확히 1/2할 수 있다. 1/3, 1/5 등은 불가.
  • 컴퓨터에선 0(없다/짧다)과 1(있다/길다)로 모든 수를 표현한다.
  • 수학에선 각도를 2π 라디안으로 π의 2배, 1/2배로 표현한다.
  • 동양 주역의 음양 8괘에선 2진수를 사용한다.



2가 좋은 점은?


  • 짝수와 홀수를 구분해 준다. 소수(프라임넘버)는 홀수 중에만 있다.
  • 뭐든지 반을 접어 정확히 1/2로 나누기 좋다. 자와 각도기 만들기에 좋음.
  • 어떤 수든 1/2눈금씩 접근하면 근사시키기 편하다. 측정에 편리함.
  • 10진수 대신 2, 4, 8진수를 사용했더라면 더 계산이 편했을 것이다.
  • 24비트 = 3비트(8진수) × 8자리, 16비트 = 4비트(16진수) × 4자리


2가 가장 기본적인 수이다. 석기 시대엔 수가 간단했다. 0(없다), 1(뭔가 있다), 2(수가 있다), 3(너무 많다). 3이란 수는 그냥 많다는 의미다. 동양에서 1만을 그냥 많다는 의미로 사용하는 것과 같다. (옛날엔 1만이 모인 것을 보기 힘들었으니까.) 석기 시대엔 3 이상 알 필요가 없었다. 3은 그냥 과한 수였다. 여하튼 여러 분야에서 2는 가장 과학적이고 편한 숫자이다. 


왜 5와 2는 10진법과 친할까? 


  • 10 = 2×5, 2로 나누기가 쉽다. 5는 이를 위한 보조 숫자에 불과.
  • 주판(돈)에선 5점(원) 1개와 1점(원) 4개로 0~9를 표현한다.




수의 종류는?

  1. 자연수 : 0, 1, 2, 3...
  2. 소수(素數:흰.수) : 2, 3, 5, 7, 11, 13...
  3. 정수(整數:가지런한.수) : 0, ±1, ±2, ±3...
  4. 소수(小數:작은.수) : ±0.123...
  5. 유리수(有理數:이치.있는.수)=분수 : 정수/정수
  6. 무리수(無理數:무리한.수) : 순환없는무한소수
  7. 실수 : 정수.소수
  8. 복소수 : 실수+허수





중고딩을 위한 수학 문제




위와 같은 소득 곡선을 보이는 섬나라 후진국이 있다. 이 섬나라에서 인당 교육비는 월평균20만원, 인당 생활비는 월평균 20만원이라고 하자. (꼴찌는 생활비와 교육비로 수입을 모두 지출한다.) 이 나라가 노인 복지와 교육 복지를 위해 세금을 걷으려 하는데 노인연금과 교육세의 세율은 얼마로 하면 되겠는가? 정률세이다.

정답 : 겨우 6.666~%

풀이 : 
총 교육비 + 노인연금 = 20만원 x 4인 = 월80만원
80만원/1200만원 = 0.0666~
월1200만원 버는 평균 월급쟁이는 약80만원만 내면 된다.

복지가 안 되는 이유는 오직 부자들이 세금 내기 싫어서다. 다른 이유는 전혀 없다.

야경 국가(방어적 평화 국가)란 것은 정부를 축소(최소한의 공무원, 경찰, 군인, 소방관을 유지)해서 부자가 세금을 가장 적게 내는 망하기 직전의 허약한 나라를 말한다. 아담 스미스는 산업혁명으로 부강해지는 영국을 보고 국부론(부자나라 만들기)을 썼는데 거기서 야경국가를 주장하니 참으로 이상하다. 영국이 야경국가였다면 영국은 세계에 식민지를 건설하지도 못 했을 것이다. 그 유명한 영국 함대는 서민들 세금으로 만들었냐?

2016년 6월 11일 토요일

컴퓨터로 시험 문제 자동으로 만들기

컴퓨터로 시험 문제를 자동으로 만드는 문제이다. 당연히 채점(採點:캐다/점)을 컴퓨터가 하니 문제는 모두 객관(客觀:손님/관점)식이어야 하며 형식은 다음과 같다. 

  1. 선다형 : 문제 1 x 답 10 선택
  2. 연결형 : 문제 10 x 답 10 연결
  3. 단답형 : 글자 수 힌트 + 글자 힌트(어두/어미/초성/중성/종성 등)


선다형


답을 모를 때 적중할 확률은 10%이다. 
또한 가짜 답 9개를 나열하는 지면이 아깝다.
컴퓨터가 정답 외의 9개 가짜 답을 골라야 해서 골치 아프다. 
가짜 답 9개 중에 수험생이 아는 것이 많으면 적중할 확률은 높아진다. 
가짜 답 9개가 모두 가짜라는 것을 알면 공짜로 점수를 얻는다.



연결형


답을 모를 때 적중할 확률은 1%이다.
문제 하나에 답 하나를 나열해서 지면을 아낄 수 있다.
컴퓨터는 문제와 답의 순서만 섞으면 되니 편하다.
수험생이 아는 정답이 많을수록 나머지도 적중할 확률이 높아진다.
마지막 두 문제 중에 하나만 알아도 100점이라는 약점이 있다.
연결형 1개가 10지 선다형 10개와 맞먹는다.
앞의 선다형보다는 여러 면에서 좋은 방식이다.



단답형


답을 모를 때 적중할 확률은 거의 0%이다.
문제 하나에 답 하나 나열이라 역시 지면을 아낄 수 있다.
컴퓨터는 문제만 선택하면 되니 구현하기 가장 쉽다.
글자 수를 알려 주면 정답 외의 동의어를 고를 확률이 줄어든다.
한국어는 주로 어미가 변하니 마지막 글자만 힌트를 줘도 무방하다.
※ ~다, ~의/적, ~한/된/인, ~은/운/는/른, ~이/히/리/게 등 주로 형용사/동사/부사인 경우
3글자 이상인 경우는 첫 글자만 알려 줘도 무방하다.
설사 정답이 여러 단어라 하더라도 채점에 전혀 문제가 없다.
※ 여기선 종이 답안지가 아닌 키보드 입력이다.



종이 답안지에 한글 직접적 입력 방법


한글 자모 모두 5x5 격자 안에 점을 찍어 (사실 그리기로) 표시할 수 있다.
한자처럼 모아쓰기 대신 알파벳처럼 풀어쓰기를 한다. (약간 번거롭다)
옆으로 풀어쓰기가 뭐하면 받침만 모음 아래 행에 적어도 된다. (공간 낭비다)
그냥 묶어서 가장 복잡한 음절을 모두 표시하려면 9x13 격자가 필요하다. (공간 낭비)
컴퓨터 보고 읽으라고 인간이 친절하게 격자에 획을 정확하게 그어주기도 힘들다.





종이 답안지에 한글 간접적 입력 방법


삼성 휴대폰 방식으로 획과 음소를 숫자로 입력하는 방법도 있다.
0. 점/획 추가
1. 이/어/어/여/야
2. 으/오/우/요/유
3. 그/끄/크
4. 느/르/므
5. 드/뜨/트
6. 브/쁘/프
7. 스/쓰
8. 으/흐
9. 즈/쯔/츠
숫자를 중국 주판처럼 5점 1개와 1점 4개로 표시하면 5칸만 필요하다. (공간 절약)
그래도 “그/끄/크”, “이/아/야”처럼 방향과 획을 추가하기 위한 추가 숫자가 필요하다.
이건 자동 채점하기 위해서 사람이 개고생하는 거다.


주관(主觀:주인/관점)식은 채점자 마음대로라 지식을 검사하기에 부적합하다. 그래서 예술 계통이 문제다. 돈(뇌물)이 많아야 한다. 그래서 부잣집 자식만 예체능을 한다.

참가자가 정해졌을 때 가로 세로 좌석 계산

가끔 프로그램을 하다 보면 (또는 다른 이유로 비슷한 계산을 할 때) 가로, 세로를 정해 놓고 거기에 맞게 좌석을 배치해야 하는 경우가 있다. 예를 들어 병력을 사각형 모양으로 배치할 때 종횡비를 구하는 것이다. 이 때 참가자 N과 X가 결정되면 Y는 어떻게 계산해야 할까? 아래는 Excel서 할 수 있는 계산의 종류다.




나눗셈 = n/x
버림 = int(n/x)
반올림 = round(n/x, 0)
나머지 = mod(n, x)

간단하다. 나머지가 생기면 무조건 버림을 한 값에 1을 더하면 된다. 그럼 가로 세로를 같게 만들려면 어떻게? 제곱근을 구하면 되겠지만 간단하지 않다. 사람을 반으로 가를 수는 없으니까.




제곱근 = sqrt(n)

의외로 간단하다. 역시 제곱근을 구한 후에 나머지가 생기면 버림을 한 값에 1을 더하면 해결 된다. 숫자보단 이렇게 그림으로 생각하는 것이 결론에 더 빨리 도달한다.




생각 → 말 → 행동

자유롭게 생각하자. 생각은 자유다. 
주변에 말해서 알려야 한다. 독재자들이 가장 싫어해서 잡혀갈 수도 있다.
그리고 투표하고, (평화적) 시위하자. 그럼 세상이 바뀐다.
그래도 안 바뀌면 자연스럽게 신이 알아서 전쟁을 일으키실 것이다.

인샬라(뜻대로 하소서 = 될 대로 되라)

2015년 10월 4일 일요일

컴퓨터 속의 데이터 정렬 (Sorting) 방법



거품이 위로 올라가듯이 상승 중인 가벼운 물체는 바로 자기 위의 것과 자신의 무게를 비교한다. 자신이 가벼우면 자리를 바꾼다. 그 과정에서 모든 것과 비교를 하니 약 N²/2회의 비교를 해야 한다. 이미 정렬이 된 것도 비교 시간은 소모된다. 역순일 때는 자리바꿈도 약 N²/2회의 시간을 소모한다. 자리바꿈 1회는 3회 대입에 해당한다. 가장 무식하고 느린 방법이다.

a ↔ b (자리바꿈)
a → c (보관)
b → a (이동)
c → b (복구)



최소치를 찾아 순서대로 배치하는 방법이다. 최소치를 찾는 과정에서 역시 모든 것과 비교를 하니 약 N²/2회의 비교를 해야 한다. 이미 정렬이 된 것도 비교 시간은 소모 된다. 자리 바꿈 N회의 시간이 추가 된다. 자리바꿈 1회 = 3회 대입. 자리 바꿈 시간에서 거품 정렬보다 유리하다.



자기 자리를 찾아 삽입하는 과정에서 많은 양의 데이터 이동이 필요하다. 허나 부분적으로 정렬이 되어 있는 경우는 삽입 시간을 줄여준다. 이미 정렬이 된 것에는 삽입 시간은 없지만 비교 시간은 약 N²/2회가 소모 된다. 삽입이 없으니 선택 정렬보다 좀 빠르다. 반대로 역순인 경우는 비교 시간이 N회이고 삽입 시간이 약 N²/2회 소모 된다. 비교(뺄셈)나 이동(대입)이나 시간이 거의 비슷할 것이다. 자리바꿈은 3회 대입, 삽입은 1회 대입이라 선택 정렬보다 조금 빠르다.

a(1) → a(2) → a(3) → a(4) (삽입)
a(4) → temp (보관)
a(3) → a(4) (이동)
a(2) → a(3) (이동)
a(1) → a(2) (이동)
temp → a(1) (복구)





기준 값보다 큰 것들과 작은 것들로 양분하는 것을 반복하는 것인데 이 과정에서 다음 단계에선 앞 단계보다 비교할 상대가 줄어들어 비교 시간을 줄여준다. 약 N*log₂N회의 시간이 소모된다. 이런 것을 분할 점령법이라 한다. 정렬 상태일 때는 비교 시간만 소모 된다. 역순일 때는 자리 바꾸는 시간이 추가로 약간(N/2회) 소모 된다. 기준 값을 잠시 보관하기 위해서 마지막 값과 자리를 바꾼다. 그 후에 기준 값을 제자리에 박아 넣는다. 그렇지 않을 경우 기준치를 보관하는 장소가 배열 크기만큼 필요하다. 재귀 호출을 할 경우는 스택에 이 정도의 메모리를 차지하게 되니 불안정하다. 스택의 한계가 정렬할 수 있는 배열의 크기가 된다. 자기 자리에 박힌 기준 값은 다시는 움직일 필요가 없다. 가장 빠른 정렬법이다.



정수를 자릿수별로 정렬한다는 것이다. 100단위, 10단위, 1단위 순으로 비교를 한다는 말이다. 큰 단위를 나누고 그 속에서 작은 단위로 나누는 전략은 퀵 정렬과 같다. 퀵 정렬의 기준치 대신 기수를 이용한 것이다. 분할 점령법이라 비교 시간이 약 N*log₂N회로 준다. 컴퓨터에서 정수는 2진수이니까 비트의 자리수의 0과 1을 검사하거나 그 수의 이상과 이하로 나누면서 정렬할 수 있다. 역시 정수에 대해선 가장 빠르다.



2명씩 짝을 이뤄 승자와 패자를 가른다. 그 다음에 4명씩 짝을 이뤄 서열을 정한다. 그 다음에 8명씩 짝을 이뤄 서열을 정한다. 이렇게 부분적으로 서열을 정한 후에 삽입 정렬을 하면 속도가 빠른 것을 이용한 것이다. 삽입 정렬에서 비교 시간과 이동 시간은 배열 규모의 제곱에 비례한다. 고로 분할하여 미리 서열을 정하면 시간이 많이 절약 된다. 역순일 때 마지막에 대규모의 삽입정렬에선 50%는 순서가 잡혀 있기 때문에 N/2회 비교와 삽입이 필요하다.퀵 정렬보다 약간 느리다.

n = 8
8*8 = 64 (통으로 할 때의 시간)
4*4 = 16, 16*2 = 32 (2등분)
2*2 = 4, 4*4 = 16 (4등분)




합병, 병합 정렬은 약간 어설픈 분할 점령법이다. 셸 정렬과 흡사한 것이 2진 트리를 만들어 승자와 패자를 결정하는 식으로 부분적으로 정렬하는 것이다. 이웃집과 서열을 결정할 때는 삽입 정렬과 비슷하지만, 삽입 정렬을 하지 않고 서열 순서대로 서로 비교하여 새로운 목록을 만든다. 그러면 비교 시간도 삽입 정렬보다 짧고, 삽입 시간도 없다. 대신 작업용 메모리가 배열만큼 필요하다. 그래도 느리다.

if A(x) >= B(y) then
 A(x) 출력, x 증가
else
 B(y) 출력, y 증가
end if



힙 정렬은 선택 정렬과 비슷하게 최소치, 최대치를 골라낸다. 그런데 그 방법이 토너먼트 식이다. 토너먼트 방식이라고 해도 최소치, 최대치 고를 때는 약 N번의 비교를 해야 한다. 2진 트리를 구성해서 자기 엄마와 자식 사이에 비교를 통해 자리 바꾸기를 하여 최소치, 최대치를 정상(배열의 앞)으로 보낸다. 이것을 마지막 자리와 바꾸고 다시 N-1개를 가지고 2진 트리를 구성하여 다시 최소치, 최대치를 앞으로 뽑는다. 이 것을 반복한다. 마지막에 앞뒤의 순서를 뒤바꾼다. 이 복잡한 짓을 왜 하지? 그냥 선택 정렬과 다를 게 없어 보이는데? 이것도 느리다.



인터넷의 여러 실험 결과를 보면 퀵 정렬, 셸 정렬이 단순하면서도 가장 빠르다. 기수 정렬도 비슷한 효과를 낼 것이다. 이유는 정말 노골적으로 간단한 분할 점령 방식이라서 그렇다. 비교 회수가 일단 약 N²/2에서 N*log₂N으로 줄어든다. 다른 방법들은 복잡하기만 하지 그렇게 빠르지는 않다.






  • e=mc², 질량 에너지 보존의 법칙, 질량과 에너지의 총합은 일정하다.
  • 빈부 격차 보존의 법칙 = 부자와 거지의 재산의 총합은 일정하다.
  • 시공간 보존의 법칙 = 시간과 공간의 곱은 일정하다.
  • 품질 = 시간×비용
  • 행복 = 여가+소득, 인생 보존의 법칙.

질량 보존의 법칙은 잘 이해하고 있을 것이다. 에너지는 돈이고 질량은 물질이라고 하자. 돈은 물질로 바꿀 수 있다. 돈을 주고 물질을 샀으니 돈은 없어지고 물질이 생겼다.

부자가 늘어나면 거지도 늘어난다. 부자가 더 부유해지면 거지는 더 가난해진다. 부자의 돈은 세상에서 긁어모은 것이기 때문이다. 99명이 1명에게 돈을 몰아주는 것이다.

10명이 1시간 동안 할 일은 5명이 2시간, 2명이 5시간, 1명이 10시간을 해야 하는 노동량이다. 병렬처리를 하면 공간을 낭비하지만 시간은 절약된다. 직렬처리는 식간을 낭비하지만 공간이 절약된다. 컴퓨터에서도 메모리를 많이 쓰면 프로그램이 간단명료하고 빨라진다. 메모리 제약이 있으면 프로그램이 복잡하고 느려진다.

품질은 시간과 비용에 비례한다. 공기업 민영화(사유화)를 하면 이상하게 비용은 올라가는데 품질은 떨어진다. 손님과 하인을 동시에 착취해서 그렇다. 손님에겐 비싸게 팔고, 하인은 싸게 부려먹는 것이다.

노동 시간이 많으면 소득은 늘겠지만 여가시간이 줄어든다. 인생의 시간은 정해져 있으니 여가시간이든 노동시간이든 어느 한쪽으로만 사용할 수 있다.

2015년 1월 5일 월요일

압축, 암호 알고리즘 단순 무식한 이해

압축 암호 알고리즘을 인터넷에서 찾아 봐도 참 설명이 어렵다. 너무 수학적이고 학문적인 표현을 사용한다. 가장 유명하며 그래서 가장 많이 쓰이는 압축 방법 몇 개를 정리해 보겠다. 자기 데이터 압축하거나 암호화할 때 사용하면 되겠다. 압축 방법을 모르면 압축이 암호화 한 것과 다름없다. 


1. 코드(암호) 사전 : 문자/단어 → 숫자


누구에게 편지를 보낸다고 하자. 그런데 그 편지에 사용하는 단어는 99개를 넘지 않는다고 하자. 그럼 모든 단어를 십진수 2개 (00~99)로 나타낼 수 있을 것이다. 예를 들어 우리가 사용하는 단어가 6만 개 미만이라고 할 경우 16비트 정수로 단어를 대신할 수 있다. 그러면 모든 단어는 2바이트만 차지한다. 보통 한글 한 글자는 16비트(완성형, 조합형, 유니코드)로 나타내기 때문에 단어를 문자 하나로 압축한 효과가 있다. 또는 가변 길이를 사용해서 압축할 수도 있다.

  • 0xxx.xxxx = 128개의 사용 빈도가 높은 짧은 단어(1 ~ 2 글자 단어)
  • 1xxx.xxxx + 0xxx.xxxx = 1만6384개의 중간 길이 단어
  • 1xxx.xxxx + 1xxx.xxxx + 0xxx.xxxx = 209만7152개의 긴 단어

이 아이디어는 유니코드 가변 길이(UTF-8)와 비슷하고, 미디 파일에서 음의 길이 표현에서 사용하는 방법과 같다. 여하튼 2바이트만 가지고 거의 모든 단어를 대신할 수 있기 때문에 간단하게 압축이 된다. 문서 압축에 이용해 보시길. 헌데 단어 사전의 양이 너무 방대하다. 이 사전을 미리 상대가 알고 있어야 한다. 우리가 흔히 사용하는 단어는 1만 미만이다. 일상생활에선 몇 천개 수준의 단어만 사용한다.


2. RLE(Run Length Encoding) : 동일 내용 압축


이것은 주로 흑백 이미지나 색상과 명암이 단순한 만화, 문서 이미지 등에서 사용하면 좋다. 이런 자료들은 같은 내용이 반복되는 경우가 많다. 그래서 주행길이코드라고 부르는 것이다. 이 방식은 FAX에 이용하는 방식이다.

AAAAAAABBBBBBCCCCC = A7B6C5

아이디어는 위와 같은데 컴퓨터에선 모든 것을 2진수로 표현하기 때문에 데이터와 길이를 구분하는 표시가 있어야 한다. 그래서 첫 비트로 데이터와 길이를 구분한다고 하면 다음과 같이 된다. 만약 반복이 없는 부분이 나타나면 그대로 표현하는 것이 더 낫다. 그런 경우 첫 비트가 계속 0인 것만 나오겠지?

0xxx.xxxx = 128개의 데이터 구분
1xxx.xxxx = 최대 128까지의 길이


3. 허프만 부호화(Huffman coding) : 빈도 → 길이


이것은 문서나 프로그램에서 단어(코드) 사용 빈도가 높은 것은 짧은 코드로, 빈도가 낮은 것은 긴 코드로 대체하는 것이다. 예를 들어 2진수로는 다음과 같이 코딩할 수 있을 것이다. 마지막 0은 끝을 알리는 구분자이다. 이 방식은 보통 우리가 사용하는 압축 프로그램에서 사용한다. 앞의 코드 사전의 가변 길이와 비슷한 개념이다.

10 = 가장 빈도가 높은 것
110
1110
11110 = 가장 빈도가 낮은 것

이 아이디어는 이미 우리 언어에서 발견할 수 있다. 1~3 글자 단어는 아주 자주 사용하는 문법 단어들이고 4글자 이상은 비교적 자주 사용하지 않는다. GIF, PNG, JPEG 이미지 파일 압축도 이런 방식을 사용한다. 


4. 푸리에 변환 : 패턴/무늬/물결 압축


사진이나 음악처럼 반복 패턴(무늬/물결)이 있는 경우는 어떻게 하지? 예를 들어 다음과 같은 경우 말이다.

ABCABCABCABC = ABC4

반복성을 검사하는 방법은 무식한 비교 외엔 없다. A를 읽고 B와 비교하니 다르다. 그럼 AB를 기억하고 C를 읽어 AB와 비교하니 다르다. 그럼 ABC를 기억하고 다시 A를 읽으니 첫 글자가 같다. 그럼 나머지 BC도 읽어 붙여 비교한다. 그럼 ABC가 일치한다. 1회 반복이 된 것이다. 몇 번 반복 되었는지 계속 비교한다. 이런 짓을 해야 한다.

반복을 인식하려면 과거를 기억해야 하고 과거와 현재를 계속 비교해야 하기 때문에 계산양이 많다. 짧은 반복은 눈에 쉽게 보이는데 아주 긴 천문학적 수준의 반복은 반복인지 느끼지도 못 한다. 큰 반복을 파악하려면 장기간의 과거 기억이 필요하다.

소리나 이미지의 경우 주기적인 특성을 보이는데 이런 주기적 특성을 분석할 때 푸리에 변환을 사용한다. 매우 계산 시간이 많이 소모되는데 주기적인 데이터의 경우 그 주기만 파악하면 압축 효과가 있다. MP3나 JPEG, MPEG 등에서 사용한다. 그런데 우리가 이 짓을 해서 압축할 수준은 아니겠지? 그래도 몇 가지 비슷하게 하는 방법이 있다.

  1. 구간 평균을 뺀다.
  2. 음수와 양수가 교차하는 지점의 간격을 조사한다. 
  3. 주기성이 있다면 특정 간격들에서 빈도가 높게 나타난다.
  4. 주기성이 보이는 간격들을 모두 합한 길이가 반복 주기다.


5. 암호화 방법 = 내용 바꾸기 + 위치 바꾸기


압축을 푸는 방법을 알려 주지 않으면 그대로 암호가 된다. 옛날부터 사용하던 암호화 방법이 있다. 문자 하나를 대체하는 방법과 문자들의 자리 바꾸는 방법이다. 이 두 방법을 섞어 사용한다. 자리 바꾸기를 하려면 단위 블록을 정해야 한다.

가나다라마 = ABCDE (문자 바꾸기)
가나다라마 = 다나라가마 (자리 바꾸기)
가나다라마 = CBDAE (문자 바꾸기 + 자리 바꾸기)

위에서 5글자를 블록으로 정해서 문자 바꾸기와 자리 바꾸기를 한 경우이다. 문자 바꾸기는 코드 사전과 비슷한 느낌일 것이다.


6. 대칭 키 암호


문자를 바꾸거나 자리를 바꾸는 방식을 알려주는 정보(숫자)가 키(열쇠)이다. 암호를 만든 쪽과 암호를 푸는 쪽이 같은 키를 사용하면 대칭키 암호이다. 이 암호 키는 당연히 정보를 주고 받을 사람 둘만 알고 있어야 한다. 문제는 10명이 서로가 모르게 암호를 사용해서 통신할 경우 10*10/2-10/2=50개의 키가 있어야 한다는 것이다. 즉, 1명은 나머지 9명과의 암호 키를 가지고 있어야 어느 한 사람과 통신할 때 나머지 8명이 해독할 수 없게 된다. 양쪽이 같은 암호를 알고 있기 때문에 어느 한 쪽이 뚫리면 암호는 깨진다. 상대가 배신하면 내가 당하는 것이다.




7. 공개 키 암호


10명의 사람이 있을 때 각자 자신의 공개 키와 비밀 키 짝을 가지고 있다고 하자. 10명은 각자의 비밀 키만 기억하면 된다. 공개 키는 무두에게 공개한다. 그럼 암호화하는 쪽에선 상대의 공개 키로 암호화해서 보내면 받아 해독하는 쪽에선 자신의 비밀 키를 이용해서 암호를 푼다. 대칭 키와 비교해서 사람의 수에 비례한 공개 키만 알면 되기 때문에 편하다. 10명 모두 10명에 대한 동일한 공개 키를 가지고 있고 오직 1개 자신의 비밀 키만 외우면 된다.

공개 키 : ID, 주민 번호, 계좌 번호 같은 것
비밀 키 : PW, 일반 암호, PIN 번호 같은 것
PIN = Personal Identification Number = 숫자 암호(은행 암호)
일반 암호 : 문자+숫자+기호 조합


공개 키가 편한 점은 공개 키 목록을 잃어 버렸을 때 다른 한 사람에게 공개 키를 물어 볼 수 있다는 점이다. 대칭 키의 경우 자신이 가진 키 목록을 잃어버리면 9명 모두 찾아다니며 키를 수집해야 한다는 것이 불편하다.

공개 키가 가능해진 이유를 보면 큰 소수(prime number)의 곱을 구하기는 쉬워도 그 수를 다시 인수 분해하기는 어렵다는 점을 이용했다고 한다. 그러면 공개 키와 비밀 키는 다음과 같은 관계란 것이다. 공개키로 암호화 하고 공개키로 암호를 풀 수 없는데 비밀 키가 있어야 암호를 풀 수 있는 알고리즘이 있어야 가능한 방법이다. 그러니까 암호화 하는 키와 암호를 푸는 키가 달라야 한다.

  1. 공개 키 = 비밀 키1 x 비밀 키2
  2. 비밀 키2 = 공개 키 x 비밀 키1
  3. 비밀 키1 = 공개 키 x 다른 키2

우리가 구한 소수가 많아야 안전하겠다. 예를 들어 우리가 파악한 소수가 100개라고 한다면 그 조합은 100x100 = 1만개가 나온다. 여하튼 공개 키는 대칭 키보다 암호가 뚫릴 가능성이 더 높다. 공개 키에 비밀 키의 힌트가 담겨 있기 때문이다. 인수 분해만 성공하면 풀린다. 역시 키 자체도 비밀로 하는 것이 안전하다. 이 인수 분해를 하자고 양자 컴퓨터를 개발하고 있다고 하는데 잘 안 되는 거 같다.



모두가 가질 수 있고 가져야 하는 것에는 뭐가 있을까?


옷, 식량, 집, 교육, 의료, 에너지, 통신, 대중교통 등이다.

국가는 모든 국민에게 이것을 제공하려고 노력해야 한다. 이건 생존과 관련 있고 삶에 필수적인 것들이다. 그리고 모두에게 나눠 줄 수 있는 것이다. 교육은 지식을 나누는 것이라서 얼마든지 공짜로 나눌 수가 있다. 의료는 병든 사람만 필요한데 병든 사람 중에 부자만 병원에 간다면 의사들도 돈을 못 벌 것이다. 그런 사회에선 의료비는 졸라 비싸면서 혜택 받는 사람은 적어진다. 에너지는 식량과 별로 다를 것이 없다. 모든 활동엔 에너지가 필요하다. 그래서 이 시대 식량처럼 저렴한 것이 에너지다. 통신망과 교통망은 돈이 많이 들어가기 때문에 원래 기업보다 돈이 가장 많은 국가에서 제공해야 한다. 이런 것을 민영화(사유화) 하면 국민만 손해 본다.


모두가 가질 수도 없고 모두가 가지면 문제가 되는 것은?


자동차, 배, 비행기, 황금, 미녀 등이다.

황금, 미녀는 그 수가 적기 때문에 모두가 가질 수 없는 것이다. 그리고 생존에 꼭 필요한 것도 아니다. 자동차, 배, 비행기를 모두가 가진다고 해 보라. 이것들이 할 일 없이 놀 때는 주차난이 발생하고 일 하러 나갈 때는 도로, 바다, 하늘이 막힌다. 그래서 이런 것들은 대중교통으로 제공해야 옳은 것이다. 도시 내부의 순환, 도시 사이의 연결엔 대중 교통이 담당하고, 그 외의 대중교통이 통하지 않는 곳에서 직업적으로 운송을 담당하는 사람들에게만 허용해야 한다. 보통 시골에서 자기 자동차를 소유하고 도시에선 전철, 버스, 택시를 타야 정상인데 거꾸로 된 것 같다.


사유화보다 공유화하는 쪽이 바람직한 것은?


농지, 어장, 광산, 공장, 시장, 은행 등 일터이다.

일터는 사적인 공간이 아니다. 사유물이 될 경우 사회 문제를 발생시킨다. 사장이 돈과 힘을 이용해서 여직원을 강간하는 경우 등, 빈부격차를 심화하고 시장을 어지럽힌다. 운영은 민간에게 맡기더라도 소유는 국가가 해야 바람직하다. 즉, 민간에게 대여하고 대여료를 세금 형식으로 받는 것이다. 이렇게 하면 사유재산이 아니기 때문에 국가가 주도적으로 빈부격차를 해소할 수 있고, 모든 사람이 놀고 먹지 않고 일을 하게 되며, 자본주의 시장 논리에도 부합 된다.

이건 사유재산이 없고 공동 생산, 공동 분배하는 공산주의와 다르다. 사적인 물건과 공간에만 사유재산을 인정하고 공적인 공간과 물건은 국유화 되어 있지만 그 것을 운영함에는 경쟁이 있기 때문에 자본주의와 같다. 공산주의가 무너졌다고 해서 긴장을 풀고 옛날 마르크스 시절의 자본주의로 후퇴하면 경제 불황만 계속 될 것이다. 80년대 자본주의(유럽/미국)가 공산주의(소련)를 이길 수 있었던 이유는 빈부격차가 심하지 않은 건전한 자본주의였기 때문이다. 80년대 한국 경제 성장도 이들 유럽/미국의 경제 성장 때문이지 박정희, 전두환의 공이 아니다.

2014년 11월 4일 화요일

0의 존재 이유 - 컴퓨터 분야의 배열 첨자가 0부터 시작하는 이유

2025-03-28 수정


컴퓨터 프로그램을 공부해 보면 배열 첨자가 0부터 시작함을 알 것이다. 왜 이렇게 할까? 그건 계산이 편하기 때문이다. 위의 그림처럼 100개의 영화 좌석에 번호를 준다고 하자. 그럼 열과 행 번호에서 좌석 번호를 뽑거나, 반대로 좌석 번호를 주면 열과 행을 찾으려고 할 때 어느 쪽이 계산이 편한가?

기타 분야에서도 0이 있고 없고에 따라 계산의 편리함이 달라진다. 서양식 층수 개념에선 층의 바닥을 기준으로 삼고 지면을 0으로 잡는다. 한국식은 공간에 번호를 주는데 0이란 개념이 없다. 지하 1층은 서양이나 한국이나 같다. 지상 1층은 다르다. 서양은 우리의 2층을 1층으로 본다. 땅은 0층이다. 지상과 지하가 있기 때문에 0이 있어야 계산이 편하다. 구간과 경계 어느 것을 기준으로 삼든 음과 양이 나타나면 0을 주어야 계산이 편하다.

음과 양의 개념이 있으면서 0이 없어 계산이 불편한 대표적인 경우가 년도이다. 년도는 구간에 번호를 붙인다. AD와 BC로 넘어갈 때 0년이란 것이 없어서 ±1년 보정을 해야 한다. 그래서 0년이란 것이 필요 없을 것 같지만 층수처럼 있어야 계산이 편하다.

나이에선 음양이 없기 때문에 기준을 0으로 하든 1로 하든 문제는 없다. 여기서도 서양식 나이 개념과 한국식 나이 개념이 차이가 난다. 우린 구간에 번호를 붙이고, 서양은 경계에 붙인다. 우린 무조건 태어나면 1살이지만 서양은 개월로 따진다. 어느 쪽을 따르든 문제는 없지만 국가 기관이나 은행 등 대부분 서양식을 따른다.

자를 생각해 보자. 우리가 사용하는 자의 눈금 표시가 바로 정상적인 것이다. 자는 경계에 숫자가 붙어 있지 구간에 숫자가 붙어 있지 않다. 마찬가지 원리로 우리가 뭔가에 번호를 붙인다고 했을 때 처음이나 기준을 0번으로 붙이는 게 효율적이다. 여기서 0번은 기준이란 의미이지 無무를 의미하지 않는다. 0은 2가지 의미가 있다. 기준, 없음. 수학에선 3가지 역할을 한다. 기준, 자릿수 표시, 없음.

앞에서도 말 했지만 어디에 붙이든 상관없이 0이 있어야 편하다. 우리가 10대, 20대, 30대란 말을 할 때 나이의 앞자리 숫자가 1, 2, 3인지 보고 말하는데 이건 0대가 있다는 의미다. 즉, 1~9세는 0대에 속하는 것이다. 00, 01, 02, 03, 04, 05, 06, 07, 08, 09세 말이다. 그런데 0대란 말이 좀 이상하니까 다음과 같이 부른다.
  • 영유아(0~5세): 신생아, 영아, 유아
  • 아동(6~9세): 미취학 아동, 초등 저학년, 어린이
이 나이는 아직 두뇌가 발달하지 못 해서 판단력이 떨어진다. 어른이 보살펴 주지 않으면 사고 난다. 10살이 넘어야 스스로 판단해서 피하거나 도망갈 수 있다.



이공계 사람들은 머리는 좋은데 이공계, 외국어 지식만 있으면 노예 교육을 받은 것 외엔 아무것도 아니다. 옛날로 말하면 의사, 통역사, 공학자 등 전문가는 모두 중인 계급에 속했다. 이건 지금도 마찬가지다. 인문 사회 과학 지식도 있어야 노예 상태에서 벗어난다. 정치, 경제, 법, 역사, 병법, 무술 등은 왕족/귀족들만 공부했다. 종교, 철학 등도 알아야 사이비에 안 빠진다. 

여기서 역사는 일제 시대 많이 왜곡 되어서 믿을 건 못 되고, 기록이 충분한 조선 시대 이후 근현대사만 공부하면 된다. 특히 근현대사 모르면 바보 된다. 똥인지 된장인지 찍어 먹어 봐야 알 정도라면 헛똑똑이 아닌가? 어제의 악마가 오늘 천사 옷을 입고 나타나도 알아 봐야 당하지 않는다. 이 모든 것이 언론 선동과 세뇌 교육의 잘 못이다. TV와 신문(조중동)과 학교 선생들을 너무 믿지 말라. 학교 역사 교육은 일제 식민 사학이 뿌리다. 

공무원, 판검사, 군인, 경찰, 교수들 모두 일제 시대 친일파 부일배 선배, 선생에게 배웠다. 남한은 친일파의 나라다. 다른 나라에선 좌파 우파가 연합하여 매국노 처벌을 했는데, 한국에선 친일파가 우파, 극우로 둔갑하여 독재 정부를 만들어 좌파 사냥을 하고 있다. 한국 극우는 일본 극우의 망령이다. 한국의 진짜 우파는 민족주의자나 민주주의자이다. 독재 찬양하는 것들은 사이비 개독교 친일파이다.