본문 바로가기
develop

구구단을 무시하지 마라

by 찬이 2004. 1. 6.

written by 김시찬 (chanywa), 2004-01-06
Homepage : http://chanywa.com
Email : chany@chanywa.com


구구단은 문법기초 배울 때나 하는 것??


C 언어를 제일 처음 시작하면서 변수나 함수의 구조를 배운다. 이때는 보통 널리 알려진 "Hello, world!!"라는 문장을 출력해보는 것으로 시작한다. 어쩌다 보니 이 문장이 프로그래밍 과정에서 빠지지 않는 유명한 문구가 되어 버렸다.

그 다음으로 간단한 제어문과 출력문을 배운 후에 프로그램같은 프로그램으로, 구구단을 만들어본다. C 언어 과정에서 구구단을 짜보지 않은 사람 또한 드물 정도로 단골손님이다.

그런데 이 구구단을 제대로 짜보고 넘어가는 사람은 의외로 드물다. 문법을 처음 배우는 시점이라서 프로그램을 작성한다는게 힘들기는 하다. 그 때문에 배우는 사람들 중 절반이상은 이미 만들어진 것을 보고 베껴보는 정도로 넘어가고, 그 나머지 중의 절반은 포기를 하며, 나머지 인원들은 75~80%의 사람들에게 배포(?)할 목적으로 아주 허덥한 구구단을 작성한다.

초보라서 그런 것인지, 실력없는 사람들이 너무 뛰어난 코드로 인해서 눈에 띄지 않도록 하기 위한 배려인지는 모르겠으나 아무튼 효율적인 코드를 작성해보는 사람은 드문 것이 사실이다.


소스코드가 눈앞에 그려지는가?


다음의 프로그램을 작성한다고 상상해보라.

문제 1. for문 2개와 printf() 한개만으로 구구단 2단~9단까지 출력하는 프로그램을
          작성하라.
문제 2. for문 1개만으로 구구단 2단~9단까지 출력하는 프로그램을 작성하라.
문제 3. 위의 문제 1, 2번을 가로로 3단씩 출력하도록 수정하라.

머릿속에 소스코드가 그려지는가? 아니면 낙서가 그려지는가? -_-;;

위의 문제들이 모두 쉽게 머릿속에 그려진다면, 초보과정을 충분히 마쳤다고 할 수 있다. 하지만 자칭 수업 열심히 들었다고, 공부좀 했다고 하더라도 코드들이 머릿속을 헤엄쳐다니고 있다면 다시금 공부할 것을 적극 권장한다.


초보자의 눈높이

아무래도 구구단을 짜려면 for 혹은 while과 같은 순환문과 printf()와 같은 출력함수가 필요하겠다. 그렇다면 제어문과 출력문을 모두 배운 사람만이 구구단 프로그램을 작성할 수 있다는 얘기다.
하지만... 틀렸다. 순환문을 몰라도 작성할 수 있다. ^________^

이 글을 읽는 사람들 중에 설마... 하며 눈치를 챈 사람도 있을 것이다. 바로 2단부터 9단까지 총 72개의 printf()를 사용하면 문제는 해결된다. 무식한 방법이라고??? 천만의 말씀, 만만의 콩떡~!

void main(void)
{
    printf("2 x 1 =  2\n");
    printf("2 x 2 =  3\n");
    .....
    /* 68개의 코드 중간생략 ^^;; */
    .....
    printf("9 x 8 = 72\n");
    printf("9 x 9 = 81\n");
}

일반적으로 C 문법을 배우는 과정에서 문법들을 활용하고자 하는 차원에서 구구단을 작성한다. 그렇기 때문에 가능한 소스가 간결해 보이면서도 배운 것들을 활용할 수 있도록 작성하기 마련이다. 그리고 실제 대부분의 선생이나 교수들도 무조건 간결하게 그리고 여러가지 배운 것을 사용한 소스에만 점수를 높게 준다.

하지만, 위의 소스는 메모리용량을 더 소모하더라도 최고의 속도를 낼 수 있게 하기 위한 고난이도(?)의 작품이다. 일반적으로 소스의 라인수가 적을수록 좋은 코드라고 착각하기 쉽다. 말 그대로 착각일 뿐이다.

위의 프로그램은 printf()가 72번 호출될 뿐, 그 어떤 작업도 필요하지 않다.


대량생산 버전의 구구단

아마 대부분의 구구단이 아래와 같은 방식으로 작성되었을 것이다. 맞고 틀리고 할 것도 없다. 순환문이랑 출력문 배웠을 때 구구단 짜라고 하면 이것 밖에는 없다. 나름대로 조금 더 배웠다 싶으면 입력문을 추가해서 작성할 뿐, 별반 차이는 없다.

void main(void)
{
    int i, j;
    for( i = 2; i < 10; i++ )
        for( j = 1; j < 10; j++ )
            printf("%d x %d = %2d\n", i, j, i * j );
}

필자가 이 코드에 변화를 주어서 좀 다르게 해보려 했지만, 더 이상의 방법이 떠오르질 않을 정도로 완벽(?)에 가까운 대중적인 소스코드다.

그렇다면, 문제 3번을 적용시켜보자. 원래 기본 소스 자체가 너무나 알려진 것이고, 일반적으로 머릿속에 쉽게 그려질 수 있는 것이라 문제 3번의 적용 또한 아주 간단하다.

하지만 3단씩 출력시에는 for()문 세개를 사용하는 것이 일반적이기 때문에, for()문을 두개만 사용하기 위해서는 생각하는 기준을 약간 달리해야 한다.

void main(void)
{
#define MULTIPLICAND ( i + ( j - 1 ) % 3 )
#define MULTIPLIER ( 1 + ( j - 1 ) / 3 )

    int i, j;
    for( i = 2; i < 10; i+=3 )
    {
        for( j = 1; j < 10; j++ )
        {
            /* 2단부터 9단까지므로 10단 제외 */
            if( MULTIPLICAND < 10 )
                printf("%d x %d = %2d\t", MULTIPLICAND, MULTIPLIER,
                                          MULTIPLICAND * MULTIPLER );
            if( j % 3 == 0 ) printf("\n");
        }
    }
}

도스용 컴파일러라면 gotoxy()도 사용가능하지만, 그런것을 제외하고 단지 가로 먼저 출력되는 콘솔환경이라는 가정하에서 작성해본 것이다. 부랴부랴 만든 것이라 마음에 안들지도 모르겠다.

난이도를 조금 더 높여보자

가끔 미친 척하고 이상한 문제를 내는 경우가 있다. for문을 단 1개만 사용해서 구구단을 작성하라는 문제가 바로 대표적이다.
위에서 언급한 대량생산 버전인 구구단 소스에 익숙한 사람들은 for문 한개를 사용하라고 하면 제법 당황한다. 왜냐면 구구단에서는 두 개의 숫자가 각각 증가하여야 하는데 순환문을 1개만 사용하라니까 갑갑하고 답답해서 미칠 지경에 이른다.

필자가 여러분들에게만 특별히 소스를 공개한다.
짠~!!!!!!!!!!

void main(void)
{
    int i;
    for( i = 2; i < 10; i++ )
        printf("%d x 1 = %2d\n%d x 2 = %2d\n%d x 3 = %2d\n" \
               "%d x 4 = %2d\n%d x 5 = %2d\n%d x 6 = %2d\n" \
               "%d x 7 = %2d\n%d x 8 = %2d\n%d x 9 = %2d\n",
                i, i*1, i, i*2, i, i*3, i, i*4, i, i*5, i, i*6,
                i, i*7, i, i*8, i, i*9 );
}

으아~~ 응애예요~~
정말 멋지지 않은가? ^^

사실 인터넷 같은 곳엘 뒤적거리다보면 만날 수 있는 소스 중의 하나인 것은 사실이다. 어쩌면 자신의 생각과 똑같다고 생각한 사람도 있을 것이다. 조금은 무식한 듯 보이면서도 문제해결은 가능한 소스임에는 틀림없다.

하지만 점수가 달린 경우라면 얘기가 다르다. 앞에서도 언급했듯이 가급적이면 소스크드가 간결해 보이도록 하는 것을 원하는 경우가 많기 때문이다. 그리고 다른 사람들이 봐서도 "아하~"라는 말이 나오는 소스가 점수를 잘 받기 마련이다.

void main(void)
{
    int i;
    for( i = 20; i < 100; i++ )
    {
        if( i % 10 == 0 ) continue; /* 0을 곱하는 경우 제외 */
        printf("%d x %d = %2d\n", i/10, i%10, (i/10)*(i%10) );
    }
}

위 소스에 그럭저럭 만족하는가? "뭐 이 정도야..."라고 대수롭게 여기고 넘어갈진 모르겠지만, 정확히 눈에 익히고 넘어가지 않는 이상에는 며칠이나 몇달 후에 머릿속에 남아 있는 것은 없다.
이 소소를 3단으로 출력하려면 머리가 조금 아파진다. 하지만 별다른 알고리즘은 아니고, 단지 계산만 잘 해보면 답이 나온다. 이걸 외우려고 하진 말기 바란다. 필자가 대충 끄적여 본 소스인데다, 막말로 이런거 외워봤자 돈 안된다. 그냥 경험삼아 재미삼아 한번 보기 바란다.

void main(void)
{
#define MULTIPLICAND ( i / 30 * 3 + 1 + i % 3 )
#define MULTIPLIER ( i % 30 / 3 + 1 )
    int i;
    for( i = 0; i < 90; i++ )
    {
        if( MULTIPLIER < 10 )
        {
            printf("%2d x %2d = %2d", MULTIPLICAND, MULTIPLIER,
                                      MULTIPLICAND * MULTIPLIER );
            if( i % 3 == 2 ) printf("\n");
            else             printf("\t");
        }
        else if( MULTIPLIER == 10 && i % 3 == 2 )
            printf("\n");
    }
}


다양하게 생각하자

이전에 나왔던 소스들은 모두가 선행조건을 중심으로 문제를 해결한 방식이다. 이걸 연역적 방법이라고 해야하나... 아무튼 원인이 되는 수식 만드는 것을 먼저하고, 그런 후에 그에 따른 결과가 출력되도록 생각하는 방식이다. 사람들이 일반적으로 생각하는 방식이기도 하다.

조금전에 보여준 소스는 그와는 반대로 귀납적 방법에 의한 구현으로 되어 있다. 즉, 원하는 결과값을 대상으로, 그것을 얻기 위한 방법을 만든 것이다. 수식을 먼저 만들 생각을 한다면 a x b 가 되니까 두개의 숫자를 증가시킬 방법을 찾게 된다. 그래서 for문의 개수가 2개부터 시작하는 것이다.

반면에 결과값을 놓고 생각하게 되면 이것은 1차원 배열 하나일 뿐이다. 그렇기에 for문 1개만으로도 해결이 가능하게 된 것이다. 결과는 같지만 접근 방법에 따라서 구현 방법 또한 달라지게 되는 것이다.

하지만 여기서 말하는 귀납적 방법이란 것이 프로그래밍을 학문적으로 따졌을 때 말하는 것과는 차이가 있을 수 있다. 필자는 단지 문제접근 방법에 있어서의 차이점을 말하고자 하는 것이다.
프로그래머는 똑같은 사물이나 현상을 보더라도, 그것을 구현하고자 할 때에는 전혀 다른 방식으로 접근을 해야 하는 경우가 많다. 상식적인 방법으로는 구현이 불가능하거나 혹은 엄청난 비효율을 가져올 수 있기 때문이다. 특히 인공지능이 관련된 분야에서는 더더욱 그러하다.


초보 프로그래머들이여~ 화이팅!!


누구나 한번쯤은 해보면서도 무시를 했던 구구단 프로그램에 대해서 다시 한 번 살펴보았다. 자신의 실력에 실망을 한 사람도 있을 것이고, 혹은 자신의 뛰어난 감각에 놀란 사람도 있을 것이다. 간혹 필자의 실력에 실망한 사람도 있을지도 모른다 ㅡ.ㅡ;;

형편없는 실력이지만, 한번쯤 짚고 넘어가보고자 했던 부분에 대해서 구구단을 빌어 주절주절거려보았다. 당장의 큰 도움은 되지 못할지라도, 앞으로 프로그래머로서 성장하게 될 여러분들 인생의 줄기가 뻗을 방향을 조금이나마 바로 잡아줄 수 있는 글이 되었으면 한다.

의사나 변호사처럼 평생 공부를 하고 연마해야하는 직업이 프로그래머임에도 우리나라에서의 대접은 그렇지 않다. 앞으로 어려운 일들이 많더라도 자신의 실력에 실망하지말고, 한번쯤 반대편에서 생각해볼 수 있는 여유를 가지기 바란다.

대한민국, 초보 프로그래머~ 화이팅!!!!  <끝>
 

'develop' 카테고리의 다른 글

논리적 오류 해결하기  (0) 2004.05.05
독학인가, 강습인가  (0) 2004.04.27
for문 날씬하게 사용하자  (2) 2004.04.19
어떤 언어를 선택해야 하나  (2) 2004.01.08
TC, BC, VC의 차이점  (1) 2004.01.08

댓글