공룡호가 사는 세상 이야기

1. 삽입정렬의 단점을 개선한 알고리즘 (Shell Sort)

1.1. 삽입정렬의 단점
: 이미 정렬된 배열이나 대충 정렬된 배열에 대해서는 매우 뛰어난 성능을 보이나, 그렇지 않은 경우에는 매우 느린 속도를 보인다. 이는 바로 인접한 요소와 비교를 하기 때문이다. 그래서 만약 역순 배열의 경우 제일 작은 키값이 제일 뒤에 있으므로 N번의 비교와 이동이 필요.

1.2. 개선책
: 'h'만큼의 간격'으로 떨어진 레코드를 삽입 정렬하는 방법.

void shell_sort(int a[], int n)
{
int i, j, k, h, v;

for (h = n / 2; h > 0; h /= 2;) // h <- h/2
{
for (i = 0; i < h; i++) //변이
{
for (j = i + h; j < n; j += h)
{
v = a[j];
k = j;

while(k > h-1 && a[k-h] > v)
{
a[k] = a[k-1]; //이동
k -= h;
}
a[k] = v; //삽입
}
}
}
}

2. 예를 들어 h가 10이라면 0,10,20,30,40...의 요소간의 삽입정렬을 하고 1씩 더한 1,11,21,31,41...의 요소간의 삽입정렬을 하는 식으로 h까지 i를 1씩 증가시키면서 h에 더하여 정렬하기를 반복한다.
정렬이 끝나면 h를 반으로 나누어 또다시 위의 과정을 반복하고,
h가 1이 되면 이미 배열은 거의 다 정렬되어 있는 상태여서 매우 빠른 속도로 삽입 정렬이 가능하다.

3. 입력 자료의 형태에 별로 상관없이 뛰어난 속도를 보인다. 최악의 자료배열인 역순 배열일 경우에도 그다지 큰 차이를 보이지 않는다.
실제로 가장 빠르다는 퀵소트의 경우 20,000개 정도의 정수를 정렬하기에는 힘이 든다.(내부 스택을 사용하는 재귀호출을 사용하므로, 메모리가 부족하기 때문) 하지만, 쉘 정렬은 추가로 메모리가 필요하지 않다는 장점.

4. 하지만, 또다시 개선하여 더욱 우수한 알고리즘을 만들 수 있다.
쉘정렬의 성능은 'h의 설정'에 따라 달라지는데, Robert Sedgewick의 연구결과 최선의 h 수열이 밝혀졌다.
h(n) = 3 * h(n-1) + 1, h(1) = 1 //괄호안의 n은 배열의 길이
즉, 1, 4, 13, 40, 121, 364, 1093

위 함수의 h를 설정하는 부분의 for문을 다음으로 대체하면, 그 성능은 눈에띄게 좋아진다.
for (h = 1; h < n; h = 3 * h + 1)

'프로그래밍' 카테고리의 다른 글

Merge Sorting Algorithm.  (0) 2006.01.10
Distribution Counting(분포수 세기)  (0) 2006.01.09
Indirect Insert Sorting Algorithm  (0) 2006.01.09
Insertion Sorting Algorithm.  (0) 2006.01.09
아파트 단지 번호 붙이기  (0) 2006.01.07

1. 간접 정렬이란 본래 레코드의 배열은 전혀 손대지 않고,
따로 만든 인덱스(index)의 배열을 조작하는 방법.

void inderect_insert_sort(int a[], int index[], int n)
{
int i, j, t;

for (i = 0; i < n; i++) //인덱스 배열 초기화
index[i] = i;
for (i = 1; i < n; i++) //삽입정렬
{
t = index[i];
j = i;

while (a[index[j-1]] > a[t] && j > 0) //삽입위치 찾음
{
index[j] = index[j-1] //인덱스 배열 조정
j--;
}
index[j] = t; //삽입
}
}

2. indirect_insert_sort()는 비교를 위해서는 a배열을 사용하지만, while 루프 내의 실제 교환 작업은 index배열을 사용한다.
이렇게 간접정렬을 하고 나면 실제로 a배열에는 아무 변동이 없다. 그러나 그 정렬된 순서는 index배열에 저장되어 있다.

3. 정렬된 상태라고 가정하고, 제일 첫번째 레코드가 무엇인지 알고자 한다면, a[index[0]]와 같이 하면 되고, i번째 레코드가 무엇인지 알려면 a[index[i]]와 같이 간접적으로 접근한다.

'프로그래밍' 카테고리의 다른 글

Distribution Counting(분포수 세기)  (0) 2006.01.09
Shell Sort Algorithm.  (0) 2006.01.09
Insertion Sorting Algorithm.  (0) 2006.01.09
아파트 단지 번호 붙이기  (0) 2006.01.07
ACM 로마숫자 문제 변형판  (0) 2006.01.07

1. 선택정렬과 함께 가장 많이 사용되는 정렬방식으로 선택정렬이 많은 비교와 적은 교환으로 특정 지워진다면, 삽입정렬은 반대로 적은 비교와 많은 교환으로 특정 지워진다.

2. 이미 정렬이 된 부분에 새로운 키를 적절한 장소에 삽입하는 동작을 반복적 수행.

3. i, j를 셋팅하고, i변수는 배열을 순서대로 카운트 한다. j변수는 i-1로 초기셋팅이 되어 i변수를 뒤에서 쫓아간다. i와 j변수가 각각 가리키고 있는 배열값을 비교하여 뒤에 있는 j변수 배열값이 i변수 배열값보다 크다면 i변수 배열값을 우측으로 옮기고, j값을 감소시킨다. while문을 사용하여 더이상 비교할 값이 없을때까지 비교하여, 모두 우측으로 이동하고 남는 자리에 처음에 비교대상이 되었던 값을 삽입하는 동작을 반복한다.

void insert_sort(int a[], int n)
{
int t, j;

for(int i=1; i<n; i++)
{
t = a[i];
j = i;

while(a[j-1] > t && j > 0)
{
a[j] = a[j-1];
j--;
}
a[j] = t;
}
}

삽입정렬은 입력자료에 굉장히 민감하다. 입력자료의 정렬된 정도에 따라 효율이 좋을 수도 있고 안 좋을 수도 있다.
그러나 삽입 정렬의 경우 작은 크기의 대충 정렬된 배열에 대해서는 제일의선택이 된다.

알고리즘을 개선하는 것은 어렵지 않다.
만약, 삽입 정렬할 레코드의 키값이 일정한 범위 내에 있다면 아래 기법을 고려해 볼 만 하다. 예를 들어 키값이 정수값인데 양의 정수만 다룬다면 이 배열의 제일 첫 요소 a[0]에 -1과 같은 값을 집어넣는다.
그리고, 실제의 자료들은 a[1] 이후에 저장하도록 한다.

그렇다면 다음의 insert_sort_1()함수와 같이 while루프의 판단문에서 j > 0과 같은 비교문을 하나 없앨 수 있다.

void insert_sort_1(int a[], int n)
{
int j, t;
for (i = 2; i<=n; i++)
{
t = a[2];
j = i;
while (a[j-1] > t) // j > 0이 없어졌다.
{
a[j] = a[j-1];
j--;
}
a[j] = t;
}
}

j > 0 이 없어진 것과 i의 변동 범위가 달라진 것 외에는 초기 함수와 다른 것이 없다.
a[0]는 루프의 경계를 벗어남을 방지하는 보초(sentinel)이다. a[0]에는 -1이 들어있고, 배열에는 양의 정수만 들어 있으므로, 예를 들어 가장 작은 수 1이 삽입될 곳을 찾기 위해 j값이 줄어든다 해도 a[0]의 값 -1보다는 1이 크므로 -1 뒤에 1이 삽입된다.

이렇게 a[0]는 경계의 벗어남을 막아주며, 또한 j > 0와 같은 비교문을 하나 없앰으로 인하여 N이 클 경우 이 기법의 효과는 두드러진다.

'프로그래밍' 카테고리의 다른 글

Shell Sort Algorithm.  (0) 2006.01.09
Indirect Insert Sorting Algorithm  (0) 2006.01.09
아파트 단지 번호 붙이기  (0) 2006.01.07
ACM 로마숫자 문제 변형판  (0) 2006.01.07
초점의 원리  (0) 2006.01.06

7 by 7 크기의 정사각형 모양의 지도가 있다.
지도에서 0은 집 없음, 1은 집 있음을 나타낸다.
단지란 왼쪽, 오른쪽, 위쪽, 아래쪽으로 연결되어 있는 집들의 집합이다.
예를 들어, 다음 지도에서는 세 개의 단지가 있다.


각 단지의 크기는 7, 8, 9이다. 입력 데이터가 주어지면,
단지의 개수와 각 단지의 크기를 출력하는 프로그램을 작성하시오.

<입력 예>
0 1 1 0 1 0 0
0 1 1 0 1 0 1
1 1 1 0 1 0 1
0 0 0 0 1 1 1
0 1 0 0 0 0 0
0 1 1 1 1 1 0
0 1 1 1 0 0 0

<출력 예>
3
7 8 9

Explanation :
#include <stdio.h>
#define n 7 //가로세로 크기
int apt[n][n] = {{0, 1, 1, 0, 1, 0, 0}, {0, 1, 1, 0, 1, 0, 1}, {1, 1, 1, 0, 1, 0, 1},//단지입력
{0, 0, 0, 0, 1, 1, 1}, {0, 1, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 0}, {0, 1, 1, 1, 0, 0, 0}};

int danji_num = 10; //단지번호(1단지는 11, 2단지는 12)
int cnt; //단지당 세대수

void cell_del(int x, int y) //찾은 세대는 지움(중복계산 방지)
{
apt[x][y] = danji_num;//단지 번호를 붙여 버린다. 1단지면 11, 2단지면 12...
}

int get(int x, int y) //해당 좌표값을 가져옴
{
if(x<0 || y<0 || x>=n || y>=n)
return 99; //0과 1이 아닌 아무숫자나 리턴(에러처리)
return apt[x][y]; //배열값을 다시 리턴(해당 좌표값을 가져온다)
}

void cell_cnt(int x, int y) //단지당 세대수 카운트
{
cell_del(x, y); //단지 삭제함수 호출

if(get(x, y-1) == 1) //상하좌우 검색(재귀)
{//좌
cnt++;
cell_cnt(x, y-1);
}

if(get(x, y+1) == 1)
{//우
cnt++;
cell_cnt(x, y+1);
}

if(get(x+1, y) == 1)
{//하
cnt++;
cell_cnt(x+1, y);
}

if(get(x-1, y) == 1)
{//상
cnt++;
cell_cnt(x-1, y);
}
}

void print(int *danji_cnt)
{//출력
printf("%d\n", danji_num-10);

for(int i=0; i<(danji_num-10); i++)
printf("\n%d ",danji_cnt[i]);
}

void main(void)
{
int danji_cnt[100]={0};

for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
if(apt[i][j] == 1) //세대가 있으면
{
danji_num++; //1단지는 11, 2단지는 12
cnt = 1; //이미 세대를 하나 찾았으므로 세대수 초기값은 1
cell_cnt(i,j); //단지내의 세대수를 카운트
danji_cnt[danji_num-11]=cnt;
}
}
}

print(danji_cnt); //결과출력
}

원래 문제는 file에서 읽어, file로 출력해야 하는데, 귀찮아서 빼버렸다.
이 프로그램의 핵심은 배열을 순서대로 카운트 하면서,
1과 0을 판별해 내고 1일때 또다시 현재 위치에서 상,하,좌,우에 단지가 있는지 검색하고, 있다면 이동하고, 이동한 위치에서 또다시 검색하는 것이 핵심이다.

예를들어 1을 찾아서 우측으로 이동 했다면, 이동한 자리를 기준으로 또다시 상,하,좌,우를 검색해야 하므로, 문제의 핵심은 재귀에 있다.

루프가 돌아가는 동안 읽었던 값에 표시를 해 둠으로써 다시 읽어버리는 오류를 미리 제거해 주며,
또한 루프를 빠져나왔다가 다시 들어갈때는 표시값을 변경함으로써,
표시값을 배열첨자로 사용하는 배열의 값이 달라지므로,

각 아파트 단지의 수는 그 배열의 전체크기가 되며,
각 단지의 세대수는 그 배열의 값이 되는 것이다.

진태형에게 재귀가 약하다고, 알고리즘 문제를 하나 부탁했더니 '하노이 탑'을 해보란다. 그 문제는 재귀 중에도 어려운 재귀에 속하고, 소스를 이미 봐 버려서 안된다고 했더니, 이 문제를 하나 내준다.

ACM에 출제된 문제를 변형한 거라며, 풀어보란다.
문제를 다 풀고, 원래 문제를 찾아 봤더니 별로 바뀐게 없다.
그냥 내도 될 것을... 대체... 이걸 변형이라고 내 준건지. ㅋㅋㅋ
형 성의를 봐서, 문제와 소스를 첨부한다. 흐흐.-.-a

대 부분의 사람들은 비교적 작은 수의 로마 숫자와 친숙하다.
i, v, x, l, c는 각각 10진수 1, 5, 10, 50, 100을 나타낸다.


그외의 다른 값들을 앞의 기호들을 합한 숫자로 나타낸다고 하자.
예를 들면, 숫자 3은 i가 3개 모여서 만들어진 것이다. 73은 l이 1개, x가 2개, i가 3개로 이루어져 있다. 즉 가장 적은 로마숫자의 합으로 숫자를 표기할 수 있다고 한다.

입력
입력은 1에서 100사이의 정수들로 결정되며, 0을 제외한 각각의 정수는 각각의 다른 기호들이 몇 개씩 필요한지를 결정한다.

출력
입력으로 받은 각각의 정수에 대해 입력 받은 정수와 그에 따른 각각의 필요한 기호의 수를 한 줄에 출력한다.

입력 예제
1
2
22
87

출력예제
1 : i 1, v 0, x 0, l 0, c 0
2 : i 2, v 0, x 0, l 0, c 0
22 : i 2, v 0, x 2, l 0, c 0
87 : i 2, v 1, x 3, l 1, c 0

Explanation :
#include <stdio.h>
#include <stdlib.h>

int roma[5]={0};
int n;

int input(void)
{//입력
printf("A Number of Page : ");
scanf("%d",&n);
return n;
}

bool check(void)
{//입력에러 처리
if(n < 0 || n > 100)
return false;
else
return true;
}

void print(int *sn)
{//출력
printf("\n%d : i %d, v %d, x %d, l %d, c %d\n\n",
*sn, *(roma + 0), *(roma + 1), *(roma + 2), *(roma + 3), *(roma + 4));
}


void cal(int n)
{//계산 (개노가다 한다. 입력제한이 있으므로 상관없다)
if(n >= 100)
{
roma[4] = (n / 100);
n = n % 100;
cal(n);
}

if(n >= 50)
{
roma[3] = (n / 50);
n = n % 50;
cal(n);
}

if(n >= 10)
{
roma[2] = (n / 10);
n = n % 10;
cal(n);
}

if(n >= 5)
{
roma[1] = (n / 5);
n = n % 5;
cal(n);
}

if(n > 0 && n < 5)
{
roma[0] = (n / 1);
n = n % 1;
cal(n);
}
if(n == 0)
{
return;
}
}

void main(void)
{
int sn = input();

if(check()==false)
exit(1);

cal(n);

print(&sn);
}

한 여름의 뜨거운 태양빛 아래에 종이를 하루 종일 놓아 두어도 종이는 불타오르지 않는다.
반면에 볼록렌즈를 이용해서 태양빛을 모으면 단 1분이 못 되어서 종이에 불이 붙고, 이내 종이 전체를 태워 버린다.

이 때 처음에 종이에 불을 붙이는 행위가 중요하다. 그런데 종이에 불을 붙이려면 태양빛을 볼록렌즈로 집중해야 한다.
이와 마찬가지로 실력을 증진시키기 위한 원리도 집중에 있다.

종이의 일부분이 일단 타면 나머지는 자연스럽게 타 버리듯이, 학습 또한 초점의 원리에 기초를 두고 있다.

초기에 무리하지 말라.
자신의 힘으로 깨뜨릴 수 있는 만큼의 분량만으로 시작하라.
이것이 핵짐적인 원칙이다.

촛불을 켤 수 있는 사람은 횃불을 켤 수 있고, 횟불을 켤 수 있는 사람은 봉화불을 켤 수 있다.

촛불을 켜는 것부터 시작하라.

한가지 덧붙이자면,
'진정한 고수는 말을 하지 않는다. 코드로 모든 것을 말한다.'

박진수 '프로그래밍 마인드'

20대의 가운데에 섰다.

세상 모든 것들이 내 것인 줄로만 알았던 20살.
무엇이든 다 할 수 있을 것만 같았고, 더 할수 없을 만큼 행복했다.
문학을 사랑했고, 조건 없이 세상에 나를 보여줄 수 있었다.
작은 것에 특별한 의미를 부여할 줄 알았고, 그로써 행복을 느낄줄도 알았다.

작은것은 작은 것일 뿐. 특별한 것은 따로 있었다.
내 안에 세상이 있는 줄로만 알았는데, 세상 속에 내가 있었다.
과연 그 무엇이라 할 지라도 조건없는 행위를 할 수 있을까.
쉽게 행복함을 느꼈던 것은, 내가 내 자신에게 속고 있었던 것은 아닐까.

2006년의 첫날, 4년간 내가 지나온 길을 차근차근히 돌아 보면서,
김수영의 '死靈'을 읽어 본다.
보이지 않는 누군가. 그렇지만 가장 나를 잘 알고 있는 그가.
내게 말한다. '너의 영(靈)은 죽어 있는 것이 아니냐' 라고.

반성하기 전에 고맙고 감사해야 한다.
하지만 고맙고 감사하는 따뜻한 마음은 현실에 안주하게 만든다.
마음만 갖자. 전하는 일은 좀 더 뒤에 해도 될 터이다.
중학교 2학년 2학기때, 우리는 이미 인생을 배웠다.
김현승의 지각(知覺) -행복의 얼굴- 을 통해서...

숫자가 변했을 뿐, 아무것도 변하지 않았다.
변해야 하는 것과, 변하지 말아야 하는 것.
구분할 줄 아는 지혜를.
넘어야 하는 것과, 절대 넘지 말아야 하는 것.
가릴 수 있는 절제를.

내가 현재 서 있는 위치와, 내 딛어야 하는 발의 방향이 어디인지.
올바로 알고, 나아갈 수 있는 날들이 되기를 소망한다.

'일상다반사' 카테고리의 다른 글

Another kind of the meaning.  (0) 2006.01.20
2005년을 보내며... with 원석  (0) 2006.01.13
마징가제트  (0) 2005.11.05
종소리  (0) 2005.07.22
우리 형,.  (0) 2005.06.05

프로그램을 작성하다 보면, 문자를 입력한 뒤에 개행문자를 입력하지 않고 자동으로 입력스트림에 있는 문자를 해당 변수로 가져갈 수 있는 방법이 궁금해진다.
getch() 를 사용하면 해결할 수 있는데, getch()는 리턴형이 int형이므로, 정수형을 리턴한다. getch는 문자를 받는 함수이므로 숫자를 입력하여도 문자로 인식한다.
그래서 '1'을 입력하면 정수형 '1'이 아닌 문자로 인식하고 정수로 변환, '49'를 리턴한다.

결국 리턴값이 중요한 것이 아니다.
예를 들어, int a = getch();를 하여 '1'을 입력했다면
a=49가 되는데, 메모리에 있는 값이 변하는 것이 아니다.
모든 것은 맞닿아 있으므로 형변환 등을 통해 입맛대로 간쳐서 드시면 될 터이다.

응용-> getch()를 사용하여 콘솔에서 암호를 입력받는 함수

int getpass(char * buffer, int buf_size)
{
int count = 0;
int c = 0;
while(1)
{
c = getch();
buffer[count] = (char)c;
count++;
if(c==13) //enter
break;
printf("*"); //password
if(count == buf_size-1)
break;
}
buffer[count]='\0';
printf("\n");
}

getch()라는 함수를 이용하면 키보드로 입력하는 것은 화면에 나오지 않는다는 점에 착안.
13이라는 숫자는 엔터키를 의미한다.
암호를 입력하고 나서 엔터를 치거나, 입력한 문자의 개수가 (버퍼크기-1)개일 경우는
입력을 종료한다.

cf) getch() function Declared in 'conio.h' or 'curses.h'

'프로그래밍' 카테고리의 다른 글

아파트 단지 번호 붙이기  (0) 2006.01.07
ACM 로마숫자 문제 변형판  (0) 2006.01.07
초점의 원리  (0) 2006.01.06
Simple Linked List, head와 tail의 연결과 메모리 누수  (0) 2005.12.30
객체지향의 철학  (0) 2004.11.21

<오류 소스코드>
#include
using namespace std;

class node
{
public:
int data;
node *next;
};

node head;
node tail;


void init_list(void)
{
*head = new node;
*tail = new node;

head->next = tail;
}

node* find_node(int key)
{
node *s;
s = head->next;
.
.
.
}

key값과 포인터를 갖는 구조체나 클래스를 정의한 다음,
node head, tail; 로 객체를 생성한다.

객체를 생성한 다음 초기화 하는 함수에서
접근이 가능하도록 포인터를 설정하고, new연산자로 공간을 할당한다.

하지만 저렇게 하였을 경우에는 심각한 에러를 발생시킨다.
다시 소스를 차근차근히 살펴보면, find_node 함수나 기타 함수 또는 어디서든 head나 tail에 접근할 수 없다.

이를테면 head에 접근하기 위해서는 *head로 접근을 시도해야 하는데, 해당 포인터를 init_list()함수 내에서 선언 해 버렸기 때문에, 함수가 종료되는 동시에 사라진다.(포인터변수 또한 변수임은 마찬가지)

그리하여, 다른 곳에서 head와 tail에 접근할 수 없을 뿐더러, 더욱 심각한 문제는 init_list()함수에서 선언했던 포인터변수는 사라지지만 new연산자로 할당한 2개의 공간(node)은 그대로 메모리를 차지한채 접근할 수 없는 상태로 허공을 떠돌게 된다.(메모리 누수)

그러므로 메모리할당은 함수 안에서 하되, head와 tail의 포인터 변수는 전역으로 설정해 주어야 한다.

node *head, *tail; // 전역

void init_list(void)
{
head = new node; // 함수가 종료되어도 사라지지 않는다.
tail = new node;
}

그리고 또한가지 그냥 지나칠 수 없는 점은
head와 tail의 연결 상태이다.원소가 아무것도 없는 리스트를 초기화 하고자 하여,
head->next = tail; 만 설정해 주게 되면 언뜻 아무 문제가 없어보인다.
하지만, while문 같은 루프의 내부에서 t=t->next;와 같은 문장이 있어서 계속 다음 노드로 이동하는 경우에 프로그래머의 실수로 루프를 끝내지 못할 경우 tail에 까지 이르게 되는데, 이 tail이 가르키는 곳을 정해주지 않아서 NULL이나 기타 엉뚱한 곳을 가르키고 있다면 t는 숨어버린다. 즉 다시 찾을 수 없다는 말이 된다.

이러한 결과는 결국 시스템이 다운을 가져온다. 대신에,
tail->next = tail; 이라는 한줄을 삽입하여, 꼬리 다음은 꼬리라고 순환적인 구조를 해 둘 경우 다운은 되지 않고 무한루프에 빠지게 된다.

무한루프는 Ctrl-Break로 멈출 수 있으며 디버그의 기능을 이용하여 현재의 상태를 점검해 볼 수 있다.
첨부파일 #1
첨부파일 #2

'프로그래밍' 카테고리의 다른 글

아파트 단지 번호 붙이기  (0) 2006.01.07
ACM 로마숫자 문제 변형판  (0) 2006.01.07
초점의 원리  (0) 2006.01.06
입력과 동시에 스트림에서 문자 가져오기 - getch();  (0) 2006.01.01
객체지향의 철학  (0) 2004.11.21

마징가제트

일상다반사2005. 11. 5. 16:24
지나간과거가내게짐이되어무겁게얹혀앞으로다가올미래가밝지않을것이라고말한다성공이란지금내가해야할모든일들을모두마친뒤에나를위해얼마나투자하는가에달렸다고그사람이말했다과연그게사실이라면지나간엿같은과거를짊어지는일또한내가할일이아닌가로또처럼한방에지나간과거가노력도없이청산될리만무하다나는대체왜그런과거들이한방에청산될거라생각했을까현재의삶에만족하지못하고허황된자만심에그저멋진인간이될것이라는병적인증세도버려야한다망할놈의파랑새증후군따위는이제집어치고달려야한다고민거리도아닌데술이나퍼마시고지나치게감상적이되어사실을지나치게확대하고정작중요한일은축소하는짓따위도이제그만고개들어다시앞을보고달리려면신발끈부터고쳐매야한다이제앞도뒤도돌아보지않고대충걷다뛰다쉬는인생은없다두고보자내훗날무적의마징가제트가되어너의앞에우뚝서서니네들의보잘것없는과거와그과거가만든초라한인생을마음껏비웃어주마내게비웃음당하고싶지않은너희들은나와같이마징가제트를꿈꾸는게어떠한가무적의마징가제트는되지못하더라도철인28호정도는될수있지않을까히히히

'일상다반사' 카테고리의 다른 글

2005년을 보내며... with 원석  (0) 2006.01.13
Adieu 2005, Welcome 2006  (1) 2006.01.01
종소리  (0) 2005.07.22
우리 형,.  (0) 2005.06.05
누구나 하는 말. 그러나,  (0) 2005.05.28