@senspond

>

개발기>App 개발기

사주 만세력 JAVA 백엔드 개발 - 2 - 오행의 상생/상극 관계를 표현, 천간지지 상극관계, 육친관계 분석

등록일시 : 2023-09-06 (수) 01:10
업데이트 : 2024-01-28 (일) 08:27
오늘 조회수 : 5
총 조회수 : 888

    JAVA 프로그래밍으로 사주명리학의 오행의 상생/상극 관계를 표현하고 천간과 지지 간의 상극 관계, 육친관계를 분석할 수 있도록 구현해 본 글입니다.

    지난번 글에 오행, 그리고 천간과 지지(지장간)을 자바 클래스로 상수화 하여 표현해봤다.

    @ToString
    @AllArgsConstructor
    @Getter
    @JsonFormat(shape = JsonFormat.Shape.OBJECT)
    public enum WuXing implements Atom {("목", "木", "#4CAF50"),("화", "火", "#F44336"),("토", "土", "#FFD600"),("금", "金", "#E0E0E0"),("수", "水", "#039BE5");
    
        private final String korean;
        private final String chinese;
        private final String color;
        @JsonIgnore
        private final YinYang yinYang = null;   // 오행은 음양이 없음
    }

    그런데 한가지 문제가 있다. 오행의 상생과 상극의 관계, 그리고 육친관계이다.

    오행의 상생과 상극관계 표현

    1. 모든 경우의 수를 하드코딩 하는 방법

    아무 생각없이 가장 쉽고 간단한 방법이다.

    오행은 5가지로 한 오행에서 다른 오행의 관계를 고려한 경우의 수의 5 x (5-1) = 20 으로 총 20개의 경우의 수가 필요하다.

    목(木) - 목(木) 같은 오행의 관계까지 생각한다면 총 25개의 경우의 수가 된다.

    그냥 25번 나열하면 된다.

    2. 그래프 자료구조를 이용해 구현하는 방법

    가장 간단하게 구현하는 방법은 각 정점 * 정점 사이즈의 인접행렬을 만들어 가중치로 관계성을 표현해 넣는 것이다.


    오행의 상생/상극 관계분석

    오행 그래프 클래스

    @Getter
    public class WuXingGraph{
        private final Type[][] matrix;
    
        @Getter
        @AllArgsConstructor
        public enum Type{
            DEFAULT(0),
            FORWARD_POSITIVE(1),
            REVERSE_POSITIVE(-1),
            FORWARD_NEGATIVE(2),
            REVERSE_NEGATIVE(-2);
            private final int weight; // 가중치
        }
    
        public WuXingGraph() {
            int size = WuXing.values().length;
            this.matrix = new Type[size][size];
            for(int i = 0; i < size; i++) Arrays.fill(this.edges[i], Type.DEFAULT);	// 초기화
        }
    }

    인접행렬은 보통 int 형으로 많이 구현하지만 총 5개의 상태값을 가지는 Enum 객체배열 형태로 구현을 했다.


    • FORWARD_POSITIVE : 상생(->) ex) 목이 화를 생함

    • REVERSE_POSITIVE : 상생(<-) ex) 목은 수로부터 생을 당함

    • FORWARD_NEGATIVE : 상극(->) ex) 금이 목을 극함

    • REVERSE_NEGATIVE : 상극(<-) ex) 금이 화로부터 극을 당함

    • DEFAULT : 초기값, 모든 관계를 다 넣게 되면 같은 오행인 경우로 구분됨


    WuXingGraph 클래스의 인접행렬에 정점간의 관계를 추가하는 add 메소드를 구현한다.

        public WuXingGraph add(WuXing from, WuXing to, boolean isSymbiosis) {
            matrix[from.ordinal()][to.ordinal()] 
    			= isSymbiosis ? Type.FORWARD_POSITIVE : Type.REVERSE_POSITIVE;
            matrix[to.ordinal()][from.ordinal()] 
    			= isSymbiosis ? Type.FORWARD_NEGATIVE : Type.REVERSE_NEGATIVE;
            return this;
        }

    자바 Enum 클래스에 기본적으로 정의된 ordinal 메소드를 통해 index 번호를 가져올 수 있다. 배열처럼 zero 인덱스 시작

    오행의 상생상극 정방향과 역방향을 생각해 add 를 한번 호출할때 양방향으로 값을 넣어준다.


    isSymbiosis 는 상생 관계인지 여부값이다. 예를 들어 다음과 같이 사용 할 수 있겠다.

    this.add(목,화,true).add(목,토,false);


    WuXingGraph 클래스의 인접행렬에서 두 정점을 가지고 값을 꺼내오는 메소드를 구현한다.

        public Type find(WuXing from, WuXing to) {
            return matrix[from.ordinal()][to.ordinal()];
        }


    오행의 관계를 정의하고 분석하는 클래스

    @Getter
    public class WuXingRelation {
        private final WuXingGraph wuXingGraph;
    
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        @ToString
        public static class Result {
            private WuXingGraph.Type relationship; // 오행 상생/상극 관계
            private Boolean isSamePolarity; // 오행의 극성이 같은지 여부
        }
    
        /**
         * 오행 그래프에 오행 상생,상극 연결정보
         */
        public WuXingRelation(){
            this.wuXingGraph = new WuXingGraph()
                    .add(WuXing., WuXing., true).add(WuXing., WuXing., false)
                    .add(WuXing., WuXing., true).add(WuXing., WuXing., false)
                    .add(WuXing., WuXing., true).add(WuXing., WuXing., false)
                    .add(WuXing., WuXing., true).add(WuXing., WuXing., false)
                    .add(WuXing., WuXing., true).add(WuXing., WuXing., false);
        }
    }

    테스트

    public class WuXingRelationTests {
    
        @Test
        public void test(){
           WuXingRelation wuXingRelation = new WuXingRelation();
    								
           System.out.println("[오행 상생상극 그래프 인접행렬 출력]");
          for(WuXingGraph.Type[] type : wuXingRelation.getWuXingGraph().getMatrix()){
               for (WuXingGraph.Type item : type){
                   System.out.printf("%2d\t", item.getWeight());
               }
               System.out.println();
           }
        }
    }

    객체 배열 형태 그대로 보여주면 보기 나쁘므로 int형 값으로 바꿔서 보여줌


    [오행 상생상극 그래프 인접행렬 출력]

    0 1 -1 -2 2

    2 0 1 -1 -2

    -2 2 0 1 -1

    -1 -2 2 0 1

    1 -1 -2 2 0


    해당 행렬을 통해 WuXingRelation 클래스에 서로 다른 오행과 서로다른 간지의 관계도를 비교분석하는

    서로 다른 오행, 간지의 관계를 도출하는 기능구현

        private String fromWuXing;
        private String toWuXing;
        private String fromGanJi;
        private String toGanJi;
    
        /**
         * 오행비교
         */
        public Result analyze(WuXing from, WuXing to) {
            this.fromWuXing = getMultiLang(from, false);
            this.toWuXing = getMultiLang(to, false);
            this.fromGanJi = null;
            this.toGanJi = null;
            WuXingGraph.Type findV = this.wuXingGraph.find(from, to);
            return new WuXingRelation.Result(findV, null);
        }
    
        /**
         * 간지 비교
         */
        public Result analyze(GanJi from, GanJi to) {
            Result result = analyze(from.getWuXing(), to.getWuXing());
            result.setIsSamePolarity(from.getYinYang().equals(to.getYinYang()));    // 음양이 서로 같은지 비교
            this.fromGanJi = getMultiLang(from, true);
            this.toGanJi = getMultiLang(to, true);
            return result;
        }


    getMultiLang 은 프로젝트 특성상 우리말(한자) 형식의 문자열을 자주 사용할 것 같아 최상위 인터페이스에 미리 구현을 해두었던 메소드이다.

    static <E extends BaseEnumAbstract> String getMultiLang
    							(E item, boolean useHighlight)
    {
         return useHighlight 
    		? String.format("\u001B[36m%s\u001B[31m(%s)\u001B[0m", 
    						item.getKorean(), item.getChinese())
            : String.format("%s(%s)", 
    						item.getKorean(), item.getChinese());
    }


    도출된 결과를 출력해주는 기능

    Result 응답값을 가지고 출력을 해주도록 구현을 했다.



    콘솔출력에 보기좋게 색상을 표현해주려고 \u001B[32m 같은 문자들이 반복되고 있는데 재사용성을 위해 수정이 필요하다.


    테스트

    public class WuXingRelationTests {
    
        private WuXingRelation wuXingRelation;
    
        @BeforeEach
        public void before() throws Exception {
            wuXingRelation =  new WuXingRelation();
        }
    
        public void print(Atom from, Atom to) {
            if (from instanceof GanJi && to instanceof GanJi) {
                wuXingRelation.print(wuXingRelation.analyze((GanJi) from, (GanJi) to));
            } else if (from instanceof WuXing && to instanceof WuXing) {
                wuXingRelation.print(wuXingRelation.analyze((WuXing) from, (WuXing) to));
            }
        }
    
        @Test
        public void test(){
           System.out.println("[오행 상생상극 그래프 인접행렬 출력]");
           for(WuXingGraph.Type[] type : wuXingRelation.getWuXingGraph().getMatrix()){
               for (WuXingGraph.Type item : type){
                   System.out.printf("%2d\t", item.getWeight());
               }
    
               System.out.println();
           }
    
           System.out.println("\n[오행 상생 관계 분석]");
           print(WuXing., WuXing.);
           print(WuXing., WuXing.);
           print(WuXing., WuXing.);
           print(WuXing., WuXing.);
           print(WuXing., WuXing.);
    
           System.out.println("\n[간지 상생 관계 분석]");
           print(Sky., Ground.);
           print(Sky., Sky.);
           print(Sky., Ground.);
           print(Sky., Ground.);
           print(Ground., Sky.);
           print(Ground., Sky.);
           print(Sky., Ground.);
           print(Ground., Ground.);
        }
    }


    로직을 통해 오행의 상생상극 관계를 확장해 간지(천간과 지지) 의 상생상극 관계와 음양이 다른지 여부를 파악 할 수 있게 되었으니, 이제 육친론을 적용하여 일간입장에서 어떤 간지를 봤을때 정재,편재, 정인,편인 ... 등 무엇인지 판별해 낼 수 있다.


    육친 관계분석

    오행의 상생/상극 관계를 구할 수 있도록 되었으니 기능을 더 확장해서 육친관계 분석을 할 수 있는 기능을 구현해본다.


    육친 타입

    @AllArgsConstructor
    @Getter
    @ToString
    public enum Blood implements BaseEnumAbstract {
        비견("비견","比肩", new Key(DEFAULT,false)),
        겁재("겁재","劫財", new Key(DEFAULT,true)),
    
        식신("식신","食神", new Key(FORWARD_POSITIVE,false)),
        상관("상관","傷官", new Key(FORWARD_POSITIVE,true)),
    
        정재("정재","正財", new Key(REVERSE_POSITIVE,false)),
        편재("편재","偏財", new Key(REVERSE_POSITIVE,true)),
    
        정관("정관","正官", new Key(REVERSE_NEGATIVE,false)),
        편관("편관","偏官", new Key(REVERSE_NEGATIVE,true)),
    
        정인("정인","正印", new Key(FORWARD_NEGATIVE,false)),
        편인("편인","偏印", new Key(FORWARD_NEGATIVE,true));
    
        private final String korean;
        private final String chinese;
        private final Key key;
    
        @Data
        @AllArgsConstructor
        @EqualsAndHashCode
        public static class Key{
            private Type type;
            private boolean isSamePolarity;
        }
    
        private static final Map<Key, Blood> map =
                Stream.of(values()).collect(toMap(Blood::getKey, v -> v));
    
        public static Blood from(WuXingRelation.Result result){
            return map.get(new Key(result.getRelationship(), result.getIsSamePolarity()));
        }
    }


    육친 관계클래스

    public class BloodRelation {
    
        private final WuXingRelation wuXingRelation;
    
        public BloodRelation(){
            this.wuXingRelation = new WuXingRelation();
        }
    
        public Blood analyze(GanJi from, GanJi to){
            WuXingRelation.Result result = wuXingRelation.analyze(from, to);
            return Blood.from(result);
        }
    }
    


    테스트

    public class BloodRelationTests {
    
        BloodRelation relation = new BloodRelation();
    
        public void print(GanJi from, GanJi to){
            String fromStr = getMultiLang(from, true);
            String fromWuXing =  getMultiLang(from.getWuXing(), false);
            String toStr = getMultiLang(to, true);
            String toWuXing =  getMultiLang(to.getWuXing(), false);
    //        System.out.println(rela.analyze(from, to));
            Blood blood = relation.analyze(from, to);
    //        System.out.println(blood);
            String bloodStr = getMultiLang(blood, true);
            System.out.printf("%s%s일간에게 %s%s는 %s에 해당합니다 %n",
                    fromStr,fromWuXing, toStr,toWuXing, bloodStr);
        }
    
        @Test
        public void test(){
            print(Sky., Sky.);
            print(Sky., Sky.);
            print(Sky., Ground.);
            print(Sky., Ground.);
            print(Sky., Sky.);
            print(Sky., Sky.);
            print(Sky., Sky.);
            print(Sky., Sky.);
            print(Sky., Sky.);
            print(Sky., Sky.);
        }
    }
    






    senspond

    안녕하세요. Red, Green, Blue 가 만나 새로운 세상을 만들어 나가겠다는 이상을 가진 개발자의 개인공간입니다.

    댓글 ( 0 )

    카테고리내 관련 게시글

    현재글에서 작성자가 발행한 같은 카테고리내 이전, 다음 글들을 보여줍니다

    @senspond

    >

    개발기>App 개발기

    • 사주 만세력 JAVA 백엔드 개발 - 1 - 음양과 오행, 천간과 지지 조회 API 개발

      사주명리학 이론을 바탕으로 음양오행, 천간과 지지를 JAVA 프로그래밍으로 표현해본 내용입니다.
        2023-09-05 (화) 10:50
      1. [현재글] 사주 만세력 JAVA 백엔드 개발 - 2 - 오행의 상생/상극 관계를 표현, 천간지지 상극관계, 육친관계 분석

        JAVA 프로그래밍으로 사주명리학의 오행의 상생/상극 관계를 표현하고 천간과 지지 간의 상극 관계, 육친관계를 분석할 수 있도록 구현해 본 글입니다.
          2023-09-06 (수) 01:10
        1. 사주 만세력 JAVA 백엔드 개발 - 3 - 만세력 DB 구축하기, 24절기, 절입조정, 서머타임, 야자시/조자시 개념

          프로그램 개발을 위한 만세력 DB 구축하기, 24절기, 절입조정, 서머타임, 야자시/조자시 개념을 정리해본 글입니다.
            2024-01-28 (일) 10:22
          1. 사주 만세력 JAVA 백엔드 개발 - 4 - 시주 계산로직 만들기

            대한민국 표준시 동경135도, 명리학에서 시주를 세우는 법에 대한 내용을 정리해보고 JAVA 프로그램을 통해 시주를 계산해본 내용입니다.
              2024-01-28 (일) 11:55