@senspond
>
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; // 오행은 음양이 없음
}
그런데 한가지 문제가 있다. 오행의 상생과 상극의 관계, 그리고 육친관계이다.
아무 생각없이 가장 쉽고 간단한 방법이다.
오행은 5가지로 한 오행에서 다른 오행의 관계를 고려한 경우의 수의 5 x (5-1) = 20 으로 총 20개의 경우의 수가 필요하다.
목(木) - 목(木) 같은 오행의 관계까지 생각한다면 총 25개의 경우의 수가 된다.
그냥 25번 나열하면 된다.
가장 간단하게 구현하는 방법은 각 정점 * 정점 사이즈의 인접행렬을 만들어 가중치로 관계성을 표현해 넣는 것이다.
@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.정);
}
}
안녕하세요. Red, Green, Blue 가 만나 새로운 세상을 만들어 나가겠다는 이상을 가진 개발자의 개인공간입니다.
현재글에서 작성자가 발행한 같은 카테고리내 이전, 다음 글들을 보여줍니다
@senspond
>