간지 (干支) : 십간(十干)과 십이지(十二支)를 합쳐서 60주기로 하는 수사입니다.

발상

60간지를 나열한 자료는 쉽게 인터넷 검색으로 구할 수 있지만, 그걸 계산하는 건 많지 않습니다.

요즘 Java를 열심히 배우고 있는 입장에서, 기존에 하던 C#를 까먹어버리면 곤란한 관계로, C#을 오랜만에 활용하여 실력을 썩히지 않도록 노력해야겠다고 다짐했습니다.

그리하여 타임어택 같은 느낌으로 도전하였습니다.

30분 안에 기능 하나는 완성해야 하지 않겠습니까!

접근

수학적인 기존의 지식을 활용하는 것도 유용해보이지만, 배열의 특성을 최대한 활용해보기로 했습니다.

언제 다시 보더라도 쉽게 이해할 수 있는, 구현이 어렵지 않은 코드를 지향하였습니다.

우선, 간지를 각각 배열로 적어봅시다.

static string[] gap = new string[] { "갑", "을", "병", "정", "무", "기", "경", "신", "임", "계" };
static string[] ja = new string[] { "자", "축", "인", "묘", "진", "사", "오", "미", "신", "유", "술", "해" };

어렵지 않습니다.

구현

연도를 간지로 변환

연도를 간지로 바꿔봅시다. 생각하기 편하게 올해(2017, 정유년)를 기준으로 생각해봅니다.

올해와 목표년도의 차이를 구하고 단순히 각각의 배열 길이로 나누어 나머지를 구하면 됩니다. 그 다음 올해의 간지만큼 더해주면 끝이죠.

하지만 여기서 의외로 연도를 간지로 바꾸는데는 구현에 약간의 애를 먹었는데, 앞으로는 문제 없이 구하는데 거꾸로는 엉뚱한게 나왔던 것입니다.

고민하다가 원인은 음수 처리에 있었다는 걸 알았습니다.

private static string YearToGapja(int i) {
            int cha = i - 2017;
            //기준점을 2017년으로 잡아봅니다.
            int gap_cha = cha % gap.Length;
            int ja_cha = cha % ja.Length;

            gap_cha = (gap_cha + 3) % gap.Length;
            ja_cha = (ja_cha + 9) % ja.Length;

            if (gap_cha < 0) {
                gap_cha = gap.Length + gap_cha;
            }
            if (ja_cha < 0) {
                ja_cha = ja.Length + ja_cha;
            }

            return gap[gap_cha] + ja[ja_cha];
        }

음수 처리로 한 번 더 길이를 더해주면 끝에서부터 음수로 세어간 위치를 구할 수 있습니다. 이렇게 하면 몇 년이고 틀리지 않고 다 구할 수 있죠.

아, 여기서 옥에티가 있군요. 기원전을 지원하는 것입니다. 그냥 해서는 0년이 포함되어버리죠.

private static int MinusToBC(int i) {
    if(i < 0) { //
       return ++i;
    }else if(i == 0) {
       return int.MinValue;
    }
    return i;
}

이렇게 음수에 한해서는 입력값을 실제보다 1 더해주면 됩니다.

0을 입력한 경우를 별도로 구분하기 위해 int.MinValue로 넣어줬습니다. 이는 Int32 범위의 최하 숫자를 가져올 수 있는데, 이걸로 무효한 숫자로 처리하면 크게 유용성에 간섭받지 않고 잘 확인할 수 있습니다.

int.MinValue와 같으면 계산을 포기한다는 구절을 넣는 것으로 충분하죠.

한편, Console.ReadLine()을 통해 입력값을 받는 만큼, 이것이 유효한 숫자인지 판별할 필요가 있습니다.

do {
    Console.Write("희망 서력 년도(기원전=>마이너스) 또는 갑자 입력: ");
    input = Console.ReadLine();
} while ((!int.TryParse(input, out i) && !isGapja(input)) || input == "0");

이를 통해 input이 숫자가 아니거나, 갑자가 아닌 경우 또는 0을 입력한 경우 모두 다시 읽으려고 시도하겠군요.

int.TryParse는 Parse 실패시에 out i 값이 0이라고 합니다.

private static bool isGapja(string input) {
            if (input.Length == 2) {
                if(!(gap.Contains(input[0].ToString()))) {
                    return false;
                }
                if (!(ja.Contains(input[1].ToString()))) {
                    return false;
                }
                return true;
            }
            return false;
        }

위에서 등장한 IsGapja라는 함수를 만들어두면 반복문을 돌 때 input의 간지 유효성을 확인하고 실제 갑자를 갖고 있는지 확인할 수 있습니다.

결과 확인

여기까지 하면 결과가 시원하게 나옵니다.

희망 서력 년도(기원전=>마이너스) 또는 갑자 입력: 2017
결과: 정유

희망 서력 년도(기원전=>마이너스) 또는 갑자 입력: 2002
결과: 임오

희망 서력 년도(기원전=>마이너스) 또는 갑자 입력: 1988
결과: 무진

와우. 이렇게 하였더니 목표 30분보다 10분 빠르게 끝났네요.

하지만 15분 냉장고를 부탁해 컷에는 실패했습니다. 다음에는 분발합시다!

간지를 연도로

그 다음으로 간지를 연도로 변환해보겠습니다. 60년마다 돌아오니까 환갑이라고 하죠. 아무래도 여러 번 있을 것이고, 시작 년도와 끝 년도도 중요하겠죠.

유연한 적용을 위해서 일부러 시작 년도와 끝 년도를 매개변수로 받았습니다.

그리고 이제 끝없는 고민을 합니다. 위의 간지 변환 사례를 역으로 방정식을 세우면 그만일까요?

아무래도 머리가 핑핑돌고 문제가 생겼을 때 어떤 방식을 써야할지 수알못에게 두려움이 비칩니다.

이럴 때는 컴퓨터를 믿고 왕도를 걸어보기로 합니다. 사실 목표 간지와 기준 간지의 차이만큼을 알고 있으면 그 +/- 60만 반복하면 결과가 됩니다.

그러나 목표 간지와 기준 간지 차이를 구하기엔, 쉽지 않습니다. 계산식을 더 많이 고민하면 되겠지만요.

수학자가 아닌 프로그래머에겐, 코드가 더 많은 답을 줍니다.

private static string[] EnumGapja() {
            string[] s = new string[60];
            for(int i = 0; i < 60; i++) {
                s[i] = gap[i % gap.Length] + ja[i % ja.Length];
            }
            return s;
        }

이렇게 하면 위 전역변수를 활용하여 60갑자가 뚝딱 완성됩니다.

나머지가 끊임없이 찬조출연하더군요.

private static int[] GapjaToYear(string input, int min, int max) {
            string[] eg = EnumGapja();
            int ifrom_gapja=Array.IndexOf(eg, input);

            //서기 4년은 갑자년.
            //4*60n=갑자
            //4*60n+ifrom_gapja=갑자+ifrom_gapja
            List ilist = new List();
            for (int i = 4 + ifrom_gapja; i < max; i+=60) {
                if(i < min) {
                    continue;
                }
                ilist.Add(i);
            }

            return ilist.ToArray();
        }

이제 한없이 길어지던 코드가 한결 간결해졌습니다.

단순히 60갑자 목록에서 index의 차이만 알게 되는 것으로 추가해야할 년도를 알 수 있습니다.

갑자년이 서기 4년이라고 합니다. 이 갑자년과의 차이 = index라는 걸 쉽게 예측할 수 있으니까요.

min년부터 max년까지 반복문을 돌리면 그만이겠습니다.

희망 서력 년도(기원전=>마이너스) 또는 갑자 입력: 무자
28년 88년 148년 208년 268년 328년 388년 448년 508년 568년 628년 688년 748년 808년 868년 928년 988년 1048년 1108년 1168년 1228년 1288년 1348년 1408년 1468년 1528년 1588년 1648년 1708년 1768년 1828년 1888년 1948년 2008년 2068년 2128년 2188년 2248년 2308년 2368년 2428년 2488년 2548년 2608년 2668년 2728년 2788년 2848년 2908년 2968년 3028년 3088년 3148년 3208년 3268년 3328년 3388년 3448년 3508년 3568년 3628년 3688년 3748년 3808년 3868년 3928년 3988년 4048년 4108년 4168년 4228년 4288년 4348년 4408년 4468년 4528년 4588년 4648년 4708년 4768년 4828년 4888년 4948년 5008년 5068년 5128년 5188년 5248년 5308년 5368년 5428년 5488년 5548년 5608년 5668년 5728년 5788년 5848년 5908년 5968년 6028년 6088년 6148년 6208년 6268년 6328년 6388년 6448년 6508년 6568년 6628년 6688년 6748년 6808년 6868년 6928년 6988년 7048년 7108년 7168년 7228년 7288년 7348년 7408년 7468년 7528년 7588년 7648년 7708년 7768년 7828년 7888년 7948년 8008년 8068년 8128년 8188년 8248년 8308년 8368년 8428년 8488년 8548년 8608년 8668년 8728년 8788년 8848 년 8908년 8968년 9028년 9088년 9148년 9208년 9268년 9328년 9388년 9448년 9508년 9568년 9628년 9688년 9748년 9808년 9868년 9928년 9988년

🙂

전체 소스

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Gapja {
    class Program {
        //4 5 6 7 8 9 0 1 2 3
        static string[] gap = new string[] { "갑", "을", "병", "정", "무", "기", "경", "신", "임", "계" };
        static string[] ja = new string[] { "자", "축", "인", "묘", "진", "사", "오", "미", "신", "유", "술", "해" };
        static void Main(string[] args) {
            //2017 = 정유년 = 4/9

            while (true) {
                string input = ""; int i = 0;
                do {
                    Console.Write("희망 서력 년도(기원전=>마이너스) 또는 갑자 입력: ");
                    input = Console.ReadLine();
                } while ((!int.TryParse(input, out i) && !isGapja(input)) || input == "0");
                i = MinusToBC(i);
                if(i != int.MinValue) {
                    Console.WriteLine("결과: {0}", YearToGapja(i));
                } else {
                    var result = GapjaToYear(input, 1, 10000);
                    foreach(var r in result) {
                        Console.Write("{0}년 ",r);
                    }
                }
                Console.WriteLine();
            }
        }

        private static bool isGapja(string input) {
            if (input.Length == 2) {
                if(!(gap.Contains(input[0].ToString()))) {
                    return false;
                }
                if (!(ja.Contains(input[1].ToString()))) {
                    return false;
                }
                return true;
            }
            return false;
        }

        private static int MinusToBC(int i) {
            if(i < 0) { //
                return ++i;
            }else if(i == 0) {
                return int.MinValue;
            }
            return i;
        }

        private static string YearToGapja(int i) {
            int cha = i - 2017;
            //기준점을 2017년으로 잡아봅니다.
            int gap_cha = cha % gap.Length;
            int ja_cha = cha % ja.Length;

            gap_cha = (gap_cha + 3) % gap.Length;
            ja_cha = (ja_cha + 9) % ja.Length;

            if (gap_cha < 0) {
                gap_cha = gap.Length + gap_cha;
            }
            if (ja_cha < 0) {
                ja_cha = ja.Length + ja_cha;
            }

            return gap[gap_cha] + ja[ja_cha];
        }

        private static string[] EnumGapja() {
            string[] s = new string[60];
            for(int i = 0; i < 60; i++) {
                s[i] = gap[i % gap.Length] + ja[i % ja.Length];
            }
            return s;
        }

        private static int[] GapjaToYear(string input, int min, int max) {
            string[] eg = EnumGapja();
            int ifrom_gapja=Array.IndexOf(eg, input);

            //서기 4년은 갑자년.
            //4*60n=갑자
            //4*60n+ifrom_gapja=갑자+ifrom_gapja
            List ilist = new List();
            for (int i = 4 + ifrom_gapja; i < max; i+=60) {
                if(i < min) {
                    continue;
                }
                ilist.Add(i);
            }

            return ilist.ToArray();
        }
    }
}