
八字合婚

我们通常所说的“合八字”是中国传统婚配的一种方法,基于生辰八字(即出生年、月、日、时的天干地支)进行匹配分析。其理论基础主要来源于中国古代的命理学,包括阴阳五行、天干地支、刑冲克害等概念。
具体来说,合八字通常会看以下几个方面:
1.五行互补:分析双方八字中五行的旺衰,看是否能够互相补充。
2.天干地支相合:包括天干五合(甲己合、乙庚合、丙辛合、丁壬合、戊癸合)和地支六合(子丑合、寅亥合、卯戌合、辰酉合、巳申合、午未合)等。
3.刑冲克害:避免双方八字中出现严重的刑(如子卯刑)、冲(如子午冲)、克(如金克木)、害(如子未害)等。
4.神煞:比如看双方八字中是否有不利于婚姻的神煞,如“孤辰寡宿”、“桃花”等。
5.大运流年:分析双方的大运和流年,看婚姻的走势。
使用Python实现合八字的分析流程和可视化是可行的,但需要注意以下几点:
1.八字计算:首先需要将公历生日转换为农历生日,然后根据农历生日计算出八字。这需要农历转换和天干地支计算的功能。
2.五行分析:计算出八字后,需要提取出天干地支对应的五行,然后进行五行数量统计和旺衰分析。
3.合婚规则:根据上述的合婚规则,编写相应的判断逻辑。
4.可视化:可以使用图表来展示双方的五行分布、天干地支的相合相冲情况等。
下面是一个大致的步骤:
1.八字计算:可以使用lunardate或zhdate等库将公历转换为农历,然后根据农历年份、月、日、时计算天干地支。注意,年的分界是以立春为界,月的分界是以节气为界,时的分界是以子时(23:00-1:00)为界。
2.五行分析:每个天干和地支都对应五行,可以建立映射表。
3.合婚分析:根据规则编写分析函数,比如检查双方八字中是否有天干五合、地支六合,以及刑冲克害等。
4.可视化:可以使用matplotlib或plotly等库进行图表绘制,比如绘制双方的五行柱状图,或者用网络图表示天干地支的相合相冲关系。
由于八字合婚的规则比较复杂,而且不同流派可能有不同的侧重点,因此实现一个完整的合婚系统需要大量的传统命理学知识。

那么我们使用Python具体实现

# 八字合婚理论及Python实现方案
# pip install matplotlib numpy pandas sxtwl # 中国农历库
"""
八字合婚分析系统 - 完整版本
自动保存结果到桌面"合婚结果"文件夹
"""
import datetime
import os
import sys
import warnings
import json
from typing import Dict, Tuple, List
import matplotlib.pyplot as plt
import numpy as np
from collections import Counter
from pathlib import Path
# 忽略matplotlib的警告
warnings.filterwarnings('ignore')
# 设置中文字体 - 解决中文显示问题
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'KaiTi', 'FangSong']
plt.rcParams['axes.unicode_minus'] = False
class FileManager:
"""文件管理类 - 处理文件保存路径"""
@staticmethod
def get_desktop_path():
"""获取桌面路径(跨平台)"""
home = Path.home()
# Windows
if sys.platform == 'win32':
desktop = home / 'Desktop'
# macOS
elif sys.platform == 'darwin':
desktop = home / 'Desktop'
# Linux/Unix
else:
desktop = home / 'Desktop'
if not desktop.exists():
# 一些Linux发行版可能使用不同的桌面路径
desktop = home / '桌面'
return desktop
@staticmethod
def create_result_folder():
"""创建合婚结果文件夹"""
desktop = FileManager.get_desktop_path()
result_folder = desktop / '合婚结果'
# 创建文件夹(如果不存在)
if not result_folder.exists():
result_folder.mkdir(parents=True, exist_ok=True)
print(f"已创建文件夹: {result_folder}")
return result_folder
@staticmethod
def get_unique_filename(base_name, extension=".png"):
"""获取唯一的文件名"""
result_folder = FileManager.create_result_folder()
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{base_name}_{timestamp}{extension}"
filepath = result_folder / filename
# 如果文件已存在,添加计数器
counter = 1
while filepath.exists():
filename = f"{base_name}_{timestamp}_{counter}{extension}"
filepath = result_folder / filename
counter += 1
return filepath
@staticmethod
def save_json(data, filename_base="合婚结果"):
"""保存JSON格式的分析结果"""
result_folder = FileManager.create_result_folder()
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{filename_base}_{timestamp}.json"
filepath = result_folder / filename
# 确保目录存在
filepath.parent.mkdir(parents=True, exist_ok=True)
# 保存JSON文件
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
print(f"分析结果已保存为JSON文件: {filename}")
return filepath
@staticmethod
def save_text_report(report_text, filename_base="合婚报告"):
"""保存文本报告"""
result_folder = FileManager.create_result_folder()
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{filename_base}_{timestamp}.txt"
filepath = result_folder / filename
# 确保目录存在
filepath.parent.mkdir(parents=True, exist_ok=True)
# 保存文本文件
with open(filepath, 'w', encoding='utf-8') as f:
f.write(report_text)
print(f"文本报告已保存: {filename}")
return filepath
class ChineseCalendar:
"""农历和节气计算类(简化版)"""
# 1900-2100年的农历数据(简化版)
LUNAR_DATA = [
0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977,
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970,
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950,
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0,
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0,
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6,
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570,
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0,
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5,
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530,
0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45,
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0
]
# 节气数据(简化版)
SOLAR_TERMS = {
1: (5, 20), 2: (4, 19), 3: (6, 21), 4: (5, 20), 5: (6, 21), 6: (7, 22),
7: (7, 23), 8: (8, 23), 9: (8, 23), 10: (9, 23), 11: (8, 22), 12: (7, 22)
}
class EightCharacters:
"""八字计算类 - 使用更准确的算法"""
# 天干地支映射
TIANGAN = ["甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸"]
DIZHI = ["子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥"]
# 生肖对应地支
SHENGXIAO = ["鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪"]
# 五行属性
WUXING_MAP = {
'甲': '木', '乙': '木', '丙': '火', '丁': '火', '戊': '土', '己': '土',
'庚': '金', '辛': '金', '壬': '水', '癸': '水',
'子': '水', '丑': '土', '寅': '木', '卯': '木', '辰': '土', '巳': '火',
'午': '火', '未': '土', '申': '金', '酉': '金', '戌': '土', '亥': '水'
}
# 相生相克关系
WUXING_SHENGKE = {
'木': {'生': '火', '克': '土', '被生': '水', '被克': '金'},
'火': {'生': '土', '克': '金', '被生': '木', '被克': '水'},
'土': {'生': '金', '克': '水', '被生': '火', '被克': '木'},
'金': {'生': '水', '克': '木', '被生': '土', '被克': '火'},
'水': {'生': '木', '克': '火', '被生': '金', '被克': '土'}
}
# 地支藏干
DIZHI_CANGGAN = {
'子': ['癸'],
'丑': ['己', '癸', '辛'],
'寅': ['甲', '丙', '戊'],
'卯': ['乙'],
'辰': ['戊', '乙', '癸'],
'巳': ['丙', '庚', '戊'],
'午': ['丁', '己'],
'未': ['己', '丁', '乙'],
'申': ['庚', '壬', '戊'],
'酉': ['辛'],
'戌': ['戊', '辛', '丁'],
'亥': ['壬', '甲']
}
# 月支对应表(根据节气划分)
MONTH_ZHI = {
1: '丑', 2: '寅', 3: '卯', 4: '辰', 5: '巳', 6: '午',
7: '未', 8: '申', 9: '酉', 10: '戌', 11: '亥', 12: '子'
}
# 月干计算表(根据年干)
MONTH_GAN_TABLE = {
'甲': ['丙', '丁', '戊', '己', '庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁'],
'乙': ['戊', '己', '庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己'],
'丙': ['庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛'],
'丁': ['壬', '癸', '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'],
'戊': ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸', '甲', '乙'],
'己': ['丙', '丁', '戊', '己', '庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁'],
'庚': ['戊', '己', '庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己'],
'辛': ['庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛'],
'壬': ['壬', '癸', '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'],
'癸': ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸', '甲', '乙']
}
def __init__(self, year, month, day, hour, minute=0, is_solar=True):
"""
初始化八字对象
Args:
year: 年
month: 月
day: 日
hour: 时(0-23)
minute: 分
is_solar: 是否为阳历(默认是)
"""
self.year = year
self.month = month
self.day = day
self.hour = hour
self.minute = minute
self.is_solar = is_solar
# 计算八字
self.bazi = self.calculate_bazi()
# 计算五行分布
self.wuxing = self.analyze_wuxing()
# 计算生肖
self.shengxiao = self.get_shengxiao()
# 计算日主(日柱天干)
self.rizhu = self.bazi['日柱'][0]
self.rizhu_wuxing = self.WUXING_MAP.get(self.rizhu, '')
def calculate_bazi(self) -> Dict[str, Tuple[str, str]]:
"""计算八字(改进的算法)"""
# 获取年柱
year_gan, year_zhi = self.get_year_pillar()
# 获取月柱(需要节气判断)
month_gan, month_zhi = self.get_month_pillar(year_gan)
# 获取日柱
day_gan, day_zhi = self.get_day_pillar()
# 获取时柱
hour_gan, hour_zhi = self.get_hour_pillar(day_gan)
return {
'年柱': (year_gan, year_zhi),
'月柱': (month_gan, month_zhi),
'日柱': (day_gan, day_zhi),
'时柱': (hour_gan, hour_zhi)
}
def get_year_pillar(self) -> Tuple[str, str]:
"""计算年柱"""
# 以立春为界划分年份(简化处理:以2月4日为界)
year = self.year
if self.month < 2 or (self.month == 2 and self.day < 4):
year -= 1
year_gan_idx = (year - 4) % 10
year_zhi_idx = (year - 4) % 12
return self.TIANGAN[year_gan_idx], self.DIZHI[year_zhi_idx]
def get_month_pillar(self, year_gan: str) -> Tuple[str, str]:
"""计算月柱(考虑节气)"""
# 简化处理:直接使用月份
month = self.month
month_zhi = self.MONTH_ZHI.get(month, '寅') # 默认寅月
# 根据年干获取月干
if year_gan in self.MONTH_GAN_TABLE:
month_gan = self.MONTH_GAN_TABLE[year_gan][month - 1]
else:
# 默认计算
month_gan_idx = (2 + month - 1) % 10
month_gan = self.TIANGAN[month_gan_idx]
return month_gan, month_zhi
def get_day_pillar(self) -> Tuple[str, str]:
"""计算日柱 - 使用更准确的公式"""
# 参考公历日期计算日柱的公式
y = self.year
m = self.month
d = self.day
# 处理1月和2月
if m <= 2:
y -= 1
m += 12
# 世纪数
C = y // 100
# 世纪年数
Y = y % 100
# 计算日干支基数
# 这个公式在1900-2100年间基本准确
G = 4 * C + C // 4 + 5 * Y + Y // 4 + 3 * (m + 1) // 5 + d - 3
# 日干
day_gan_idx = G % 10
if day_gan_idx == 0:
day_gan_idx = 10
# 日支
day_zhi_idx = G % 12
if day_zhi_idx == 0:
day_zhi_idx = 12
return self.TIANGAN[day_gan_idx - 1], self.DIZHI[day_zhi_idx - 1]
def get_hour_pillar(self, day_gan: str) -> Tuple[str, str]:
"""计算时柱"""
# 时支(子时23:00-1:00为子时)
hour = self.hour
if hour % 2 == 0:
hour_zhi_idx = (hour // 2) % 12
else:
hour_zhi_idx = ((hour + 1) // 2) % 12
hour_zhi = self.DIZHI[hour_zhi_idx]
# 根据日干计算时干
# 日上起时口诀:甲己还加甲,乙庚丙作初,丙辛从戊起,丁壬庚子居,戊癸何方发,壬子是真途
hour_gan_table = {
'甲': ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸', '甲', '乙'],
'乙': ['丙', '丁', '戊', '己', '庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁'],
'丙': ['戊', '己', '庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己'],
'丁': ['庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛'],
'戊': ['壬', '癸', '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸'],
'己': ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸', '甲', '乙'],
'庚': ['丙', '丁', '戊', '己', '庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁'],
'辛': ['戊', '己', '庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己'],
'壬': ['庚', '辛', '壬', '癸', '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛'],
'癸': ['壬', '癸', '甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']
}
if day_gan in hour_gan_table:
hour_gan = hour_gan_table[day_gan][hour_zhi_idx]
else:
# 默认计算
hour_gan_idx = (2 * hour_zhi_idx) % 10
hour_gan = self.TIANGAN[hour_gan_idx]
return hour_gan, hour_zhi
def get_shengxiao(self) -> str:
"""获取生肖"""
year_zhi = self.bazi['年柱'][1]
zhi_idx = self.DIZHI.index(year_zhi)
return self.SHENGXIAO[zhi_idx]
def analyze_wuxing(self) -> Dict[str, any]:
"""分析五行分布(包括地支藏干)"""
wuxing_count = {'金': 0, '木': 0, '水': 0, '火': 0, '土': 0}
# 计算天干五行
for pillar in self.bazi.values():
gan, zhi = pillar
# 天干五行
if gan in self.WUXING_MAP:
wuxing = self.WUXING_MAP[gan]
wuxing_count[wuxing] += 1
# 地支本气五行
if zhi in self.WUXING_MAP:
wuxing = self.WUXING_MAP[zhi]
wuxing_count[wuxing] += 1
# 地支藏干五行
if zhi in self.DIZHI_CANGGAN:
for canggan in self.DIZHI_CANGGAN[zhi]:
if canggan in self.WUXING_MAP:
wuxing = self.WUXING_MAP[canggan]
wuxing_count[wuxing] += 0.5 # 藏干权重较低
# 转换为整数(便于显示)
wuxing_count_int = {k: int(v * 2) for k, v in wuxing_count.items()}
# 找出最强和最弱的五行
strongest = max(wuxing_count, key=wuxing_count.get)
weakest = min(wuxing_count, key=wuxing_count.get)
return {
'distribution': wuxing_count,
'distribution_int': wuxing_count_int,
'strongest': strongest,
'weakest': weakest,
'total': sum(wuxing_count.values())
}
def get_bazi_string(self) -> str:
"""获取八字字符串"""
bazi_str = ""
for pillar, (gan, zhi) in self.bazi.items():
bazi_str += f"{gan}{zhi} "
return bazi_str.strip()
def get_wuxing_string(self) -> str:
"""获取五行分布字符串"""
wx = self.wuxing['distribution_int']
return f"金{wx['金']} 木{wx['木']} 水{wx['水']} 火{wx['火']} 土{wx['土']}"
class MarriageAnalyzer:
"""合婚分析器 - 包含更多传统合婚规则"""
# 生肖六合
SHENGXIAO_LIUHE = {
'鼠': '牛', '牛': '鼠', '虎': '猪', '兔': '狗',
'龙': '鸡', '蛇': '猴', '马': '羊', '羊': '马',
'猴': '蛇', '鸡': '龙', '狗': '兔', '猪': '虎'
}
# 生肖三合
SHENGXIAO_SANHE = {
'鼠': ['龙', '猴'],
'牛': ['蛇', '鸡'],
'虎': ['马', '狗'],
'兔': ['羊', '猪'],
'龙': ['鼠', '猴'],
'蛇': ['牛', '鸡'],
'马': ['虎', '狗'],
'羊': ['兔', '猪'],
'猴': ['鼠', '龙'],
'鸡': ['牛', '蛇'],
'狗': ['虎', '马'],
'猪': ['兔', '羊']
}
# 生肖六冲
SHENGXIAO_LIUCHONG = {
'鼠': '马', '牛': '羊', '虎': '猴', '兔': '鸡',
'龙': '狗', '蛇': '猪', '马': '鼠', '羊': '牛',
'猴': '虎', '鸡': '兔', '狗': '龙', '猪': '蛇'
}
# 生肖相刑
SHENGXIAO_XING = {
'鼠': '兔',
'牛': '羊', '羊': '牛',
'虎': '蛇', '蛇': '虎',
'马': '马',
'鸡': '鸡',
'猪': '猪'
}
# 天干五合
TIANGAN_WUHE = {
'甲': '己', '己': '甲',
'乙': '庚', '庚': '乙',
'丙': '辛', '辛': '丙',
'丁': '壬', '壬': '丁',
'戊': '癸', '癸': '戊'
}
# 地支六合
DIZHI_LIUHE = {
'子': '丑', '丑': '子',
'寅': '亥', '亥': '寅',
'卯': '戌', '戌': '卯',
'辰': '酉', '酉': '辰',
'巳': '申', '申': '巳',
'午': '未', '未': '午'
}
@staticmethod
def analyze_compatibility(bazi1: EightCharacters, bazi2: EightCharacters) -> Dict:
"""综合合婚分析"""
results = {
'基本信息': {
'八字1': bazi1.get_bazi_string(),
'八字2': bazi2.get_bazi_string(),
'生肖1': bazi1.shengxiao,
'生肖2': bazi2.shengxiao,
'日主1': f"{bazi1.rizhu}({bazi1.rizhu_wuxing})",
'日主2': f"{bazi2.rizhu}({bazi2.rizhu_wuxing})",
'五行分布1': bazi1.get_wuxing_string(),
'五行分布2': bazi2.get_wuxing_string()
},
'生肖配对': MarriageAnalyzer.analyze_shengxiao(bazi1, bazi2),
'日干关系': MarriageAnalyzer.analyze_rigan(bazi1, bazi2),
'五行互补': MarriageAnalyzer.analyze_wuxing_complement(bazi1, bazi2),
'天干地支合': MarriageAnalyzer.analyze_ganzhi_he(bazi1, bazi2),
'纳音五行': MarriageAnalyzer.analyze_nayin(bazi1, bazi2),
'详细建议': {}
}
# 计算综合评分
scores = []
for key in ['生肖配对', '日干关系', '五行互补', '天干地支合']:
if'score'in results[key]:
scores.append(results[key]['score'])
if scores:
total_score = int(sum(scores) / len(scores))
results['综合评分'] = {
'分数': total_score,
'评级': MarriageAnalyzer.get_rating(total_score),
'星级': MarriageAnalyzer.get_stars(total_score)
}
# 生成详细建议
results['详细建议'] = MarriageAnalyzer.generate_advice(results)
return results
@staticmethod
def analyze_shengxiao(bazi1, bazi2):
"""分析生肖配对"""
sx1 = bazi1.shengxiao
sx2 = bazi2.shengxiao
score = 50
relationships = []
description = ""
# 六合
if sx1 in MarriageAnalyzer.SHENGXIAO_LIUHE and MarriageAnalyzer.SHENGXIAO_LIUHE[sx1] == sx2:
score = 95
relationships.append("六合")
description = "最佳配对,相互吸引,相处融洽,婚姻美满"
# 三合
elif sx1 in MarriageAnalyzer.SHENGXIAO_SANHE and sx2 in MarriageAnalyzer.SHENGXIAO_SANHE[sx1]:
score = 85
relationships.append("三合")
description = "良好配对,互相促进,感情稳定"
# 六冲
elif sx1 in MarriageAnalyzer.SHENGXIAO_LIUCHONG and MarriageAnalyzer.SHENGXIAO_LIUCHONG[sx1] == sx2:
score = 20
relationships.append("六冲")
description = "不利配对,容易产生矛盾冲突,需要多包容"
# 相刑
elif sx1 in MarriageAnalyzer.SHENGXIAO_XING and MarriageAnalyzer.SHENGXIAO_XING[sx1] == sx2:
score = 30
relationships.append("相刑")
description = "需要化解,容易有口舌是非"
else:
relationships.append("普通")
description = "普通配对,相处需要互相理解"
return {
'生肖1': sx1,
'生肖2': sx2,
'关系': "、".join(relationships),
'分数': score,
'说明': description,
'score': score
}
@staticmethod
def analyze_rigan(bazi1, bazi2):
"""分析日干关系"""
rigan1 = bazi1.rizhu
rigan2 = bazi2.rizhu
wx1 = bazi1.rizhu_wuxing
wx2 = bazi2.rizhu_wuxing
score = 50
relationship = ""
description = ""
# 天干五合
if rigan1 in MarriageAnalyzer.TIANGAN_WUHE and MarriageAnalyzer.TIANGAN_WUHE[rigan1] == rigan2:
score = 90
relationship = "天干五合"
description = "有情之合,感情深厚,相互吸引"
# 五行生克
elif wx1 and wx2:
sheng_ke = EightCharacters.WUXING_SHENGKE
if wx1 in sheng_ke and wx2 in sheng_ke:
if sheng_ke[wx1]['生'] == wx2: # 1生2
score = 80
relationship = f"{wx1}生{wx2}"
description = f"{wx1}生{wx2},一方付出较多,但感情融洽"
elif sheng_ke[wx2]['生'] == wx1: # 2生1
score = 80
relationship = f"{wx2}生{wx1}"
description = f"{wx2}生{wx1},互相滋养,感情和谐"
elif sheng_ke[wx1]['克'] == wx2: # 1克2
score = 40
relationship = f"{wx1}克{wx2}"
description = f"{wx1}克{wx2},容易产生矛盾,需要互相包容"
elif sheng_ke[wx2]['克'] == wx1: # 2克1
score = 40
relationship = f"{wx2}克{wx1}"
description = f"{wx2}克{wx1},性格差异较大,需要磨合"
else:
score = 60
relationship = "五行平和"
description = "五行关系平和,相处稳定"
else:
relationship = "五行关系普通"
description = "五行关系普通,无明显生克"
else:
relationship = "无法判断"
description = "无法判断五行关系"
return {
'日干1': f"{rigan1}({wx1})",
'日干2': f"{rigan2}({wx2})",
'关系': relationship,
'分数': score,
'说明': description,
'score': score
}
@staticmethod
def analyze_wuxing_complement(bazi1, bazi2):
"""分析五行互补性"""
wx1 = bazi1.wuxing['distribution']
wx2 = bazi2.wuxing['distribution']
# 五行缺失分析
missing1 = [wx for wx, count in wx1.items() if count < 0.5]
missing2 = [wx for wx, count in wx2.items() if count < 0.5]
# 五行过旺分析
strong1 = bazi1.wuxing['strongest']
strong2 = bazi2.wuxing['strongest']
# 互补性评分
score = 50
complement_info = []
description = ""
# 检查缺失互补
complement_count = 0
for wx in missing1:
if wx2.get(wx, 0) > 1.5: # 对方有这个五行且较多
score += 10
complement_count += 1
complement_info.append(f"八字1缺{wx},八字2{wx}旺")
for wx in missing2:
if wx1.get(wx, 0) > 1.5:
score += 10
complement_count += 1
complement_info.append(f"八字2缺{wx},八字1{wx}旺")
# 检查过旺制约
if strong1 in EightCharacters.WUXING_SHENGKE and strong2 in EightCharacters.WUXING_SHENGKE:
if EightCharacters.WUXING_SHENGKE[strong1]['克'] == strong2:
score += 15
complement_info.append(f"八字1{strong1}旺,可制约八字2{strong2}旺")
elif EightCharacters.WUXING_SHENGKE[strong2]['克'] == strong1:
score += 15
complement_info.append(f"八字2{strong2}旺,可制约八字1{strong1}旺")
score = min(100, max(0, score))
if complement_count >= 2:
description = "五行互补良好,相互促进"
elif complement_count == 1:
description = "有一定互补性"
else:
description = "五行互补性一般"
return {
'八字1最旺': strong1,
'八字2最旺': strong2,
'八字1最弱': bazi1.wuxing['weakest'],
'八字2最弱': bazi2.wuxing['weakest'],
'互补情况': ";".join(complement_info) if complement_info else"无明显互补",
'分数': score,
'说明': description,
'score': score
}
@staticmethod
def analyze_ganzhi_he(bazi1, bazi2):
"""分析天干地支的相合关系"""
b1 = bazi1.bazi
b2 = bazi2.bazi
he_count = 0
he_info = []
# 检查天干五合
for pillar_name in ['年柱', '月柱', '日柱', '时柱']:
gan1 = b1[pillar_name][0]
gan2 = b2[pillar_name][0]
if gan1 in MarriageAnalyzer.TIANGAN_WUHE and MarriageAnalyzer.TIANGAN_WUHE[gan1] == gan2:
he_count += 1
he_info.append(f"{pillar_name}天干{gan1}与{gan2}相合")
# 检查地支六合
for pillar_name in ['年柱', '月柱', '日柱', '时柱']:
zhi1 = b1[pillar_name][1]
zhi2 = b2[pillar_name][1]
if zhi1 in MarriageAnalyzer.DIZHI_LIUHE and MarriageAnalyzer.DIZHI_LIUHE[zhi1] == zhi2:
he_count += 1
he_info.append(f"{pillar_name}地支{zhi1}与{zhi2}相合")
# 根据相合数量评分
score = 50 + he_count * 10
score = min(100, score)
if he_count >= 3:
description = "天干地支相合较多,缘分深厚"
elif he_count >= 1:
description = "有一定相合关系"
else:
description = "无明显相合关系"
return {
'相合数量': he_count,
'相合详情': ";".join(he_info) if he_info else"无相合",
'分数': score,
'说明': description,
'score': score
}
@staticmethod
def analyze_nayin(bazi1, bazi2):
"""分析纳音五行(简化版)"""
# 纳音五行表(年柱纳音)
nayin_map = {
('甲', '子'): '海中金', ('乙', '丑'): '海中金',
('丙', '寅'): '炉中火', ('丁', '卯'): '炉中火',
('戊', '辰'): '大林木', ('己', '巳'): '大林木',
('庚', '午'): '路旁土', ('辛', '未'): '路旁土',
('壬', '申'): '剑锋金', ('癸', '酉'): '剑锋金',
('甲', '戌'): '山头火', ('乙', '亥'): '山头火',
('丙', '子'): '涧下水', ('丁', '丑'): '涧下水',
('戊', '寅'): '城头土', ('己', '卯'): '城头土',
('庚', '辰'): '白蜡金', ('辛', '巳'): '白蜡金',
('壬', '午'): '杨柳木', ('癸', '未'): '杨柳木',
('甲', '申'): '泉中水', ('乙', '酉'): '泉中水',
('丙', '戌'): '屋上土', ('丁', '亥'): '屋上土',
('戊', '子'): '霹雳火', ('己', '丑'): '霹雳火',
('庚', '寅'): '松柏木', ('辛', '卯'): '松柏木',
('壬', '辰'): '长流水', ('癸', '巳'): '长流水',
('甲', '午'): '砂中金', ('乙', '未'): '砂中金',
('丙', '申'): '山下火', ('丁', '酉'): '山下火',
('戊', '戌'): '平地木', ('己', '亥'): '平地木',
('庚', '子'): '壁上土', ('辛', '丑'): '壁上土',
('壬', '寅'): '金箔金', ('癸', '卯'): '金箔金',
('甲', '辰'): '佛灯火', ('乙', '巳'): '佛灯火',
('丙', '午'): '天河水', ('丁', '未'): '天河水',
('戊', '申'): '大驿土', ('己', '酉'): '大驿土',
('庚', '戌'): '钗钏金', ('辛', '亥'): '钗钏金',
('壬', '子'): '桑柘木', ('癸', '丑'): '桑柘木',
('甲', '寅'): '大溪水', ('乙', '卯'): '大溪水',
('丙', '辰'): '沙中土', ('丁', '巳'): '沙中土',
('戊', '午'): '天上火', ('己', '未'): '天上火',
('庚', '申'): '石榴木', ('辛', '酉'): '石榴木',
('壬', '戌'): '大海水', ('癸', '亥'): '大海水'
}
year_ganzhi1 = bazi1.bazi['年柱']
year_ganzhi2 = bazi2.bazi['年柱']
nayin1 = nayin_map.get(year_ganzhi1, "未知")
nayin2 = nayin_map.get(year_ganzhi2, "未知")
return {
'八字1年柱纳音': nayin1,
'八字2年柱纳音': nayin2,
'说明': "年柱纳音代表先天缘分"
}
@staticmethod
def get_rating(score):
"""根据分数获取评级"""
if score >= 90:
return"天作之合"
elif score >= 80:
return"良缘佳配"
elif score >= 70:
return"中等婚配"
elif score >= 60:
return"一般婚配"
else:
return"需要努力"
@staticmethod
def get_stars(score):
"""根据分数获取星级"""
if score >= 90:
return"★★★★★"
elif score >= 80:
return"★★★★☆"
elif score >= 70:
return"★★★☆☆"
elif score >= 60:
return"★★☆☆☆"
else:
return"★☆☆☆☆"
@staticmethod
def generate_advice(results):
"""生成合婚建议"""
advice = []
# 生肖建议
sx_score = results['生肖配对']['score']
if sx_score >= 80:
advice.append("生肖配对良好,相处融洽,是天赐良缘。")
elif sx_score >= 60:
advice.append("生肖配对一般,需要互相包容理解。")
else:
advice.append("生肖配对有冲,建议多沟通,通过家居风水布局化解矛盾。")
# 日干建议
rg_score = results['日干关系']['score']
if rg_score >= 80:
advice.append("日干相生相合,感情基础深厚,相互扶持。")
elif rg_score >= 60:
advice.append("日干关系平和,感情发展稳定,细水长流。")
else:
advice.append("日干相克,需要更多磨合,建议培养共同爱好,增进理解。")
# 五行建议
wx_info = results['五行互补']
if wx_info['score'] >= 70:
advice.append("五行互补良好,能够相互促进,共同成长。")
else:
advice.append(f"八字1最旺{wx_info['八字1最旺']},八字2最旺{wx_info['八字2最旺']},建议取长补短,发挥各自优势。")
# 综合建议
total_score = results['综合评分']['分数']
if total_score >= 80:
advice.append("综合评分高,婚姻幸福指数高,珍惜缘分,用心经营。")
elif total_score >= 60:
advice.append("综合评分中等,婚姻需要双方共同经营,互相体谅。")
else:
advice.append("综合评分较低,但缘分难测,真情可以超越命理。重要的是彼此真心相待,共同面对困难。")
return advice
class Visualization:
"""合婚可视化类"""
@staticmethod
def create_comprehensive_report(bazi1, bazi2, analysis_results):
"""创建综合报告(单个图表)"""
fig = plt.figure(figsize=(16, 12))
fig.suptitle('八字合婚分析报告', fontsize=20, fontweight='bold', y=0.98)
# 1. 标题和基本信息
ax1 = plt.subplot2grid((5, 3), (0, 0), colspan=3)
Visualization.create_title_section(ax1, bazi1, bazi2, analysis_results)
# 2. 八字对比图
ax2 = plt.subplot2grid((5, 3), (1, 0), colspan=1, rowspan=1)
Visualization.create_bazi_comparison(ax2, bazi1, bazi2)
# 3. 五行雷达图
ax3 = plt.subplot2grid((5, 3), (1, 1), colspan=1, rowspan=1, projection='polar')
Visualization.create_wuxing_radar(ax3, bazi1, bazi2)
# 4. 匹配度评分图
ax4 = plt.subplot2grid((5, 3), (1, 2), colspan=1, rowspan=1)
Visualization.create_score_gauge(ax4, analysis_results.get('综合评分', {}).get('分数', 50))
# 5. 详细匹配分析
ax5 = plt.subplot2grid((5, 3), (2, 0), colspan=3, rowspan=2)
Visualization.create_detailed_analysis(ax5, analysis_results)
# 6. 生肖关系图
ax6 = plt.subplot2grid((5, 3), (4, 0), colspan=1, rowspan=1)
Visualization.create_shengxiao_chart(ax6, bazi1, bazi2)
# 7. 五行条形图
ax7 = plt.subplot2grid((5, 3), (4, 1), colspan=2, rowspan=1)
Visualization.create_wuxing_bar(ax7, bazi1, bazi2)
plt.tight_layout()
return fig
@staticmethod
def create_title_section(ax, bazi1, bazi2, analysis_results):
"""创建标题和基本信息部分"""
ax.axis('off')
# 获取当前时间
current_time = datetime.datetime.now().strftime("%Y年%m月%d日 %H:%M:%S")
# 获取综合评分
total_score = analysis_results.get('综合评分', {}).get('分数', 0)
rating = analysis_results.get('综合评分', {}).get('评级', '未知')
stars = analysis_results.get('综合评分', {}).get('星级', '')
# 标题
ax.text(0.5, 0.9, '八字合婚分析报告', ha='center', va='center',
fontsize=18, fontweight='bold', transform=ax.transAxes)
# 时间
ax.text(0.5, 0.82, f'生成时间: {current_time}', ha='center', va='center',
fontsize=10, transform=ax.transAxes)
# 八字信息
ax.text(0.05, 0.72, f'八字1: {bazi1.get_bazi_string()} 生肖: {bazi1.shengxiao}',
ha='left', va='center', fontsize=11, transform=ax.transAxes)
ax.text(0.05, 0.67, f'八字2: {bazi2.get_bazi_string()} 生肖: {bazi2.shengxiao}',
ha='left', va='center', fontsize=11, transform=ax.transAxes)
# 日主信息
ax.text(0.05, 0.60, f'日主1: {bazi1.rizhu}({bazi1.rizhu_wuxing}) 日主2: {bazi2.rizhu}({bazi2.rizhu_wuxing})',
ha='left', va='center', fontsize=11, transform=ax.transAxes)
# 综合评分
ax.text(0.05, 0.50, f'综合评分: {total_score}分 {stars} {rating}',
ha='left', va='center', fontsize=12, fontweight='bold',
color='darkred'if total_score >= 80 else'orange'if total_score >= 60 else'darkblue',
transform=ax.transAxes)
# 分隔线
ax.axhline(y=0.42, xmin=0.05, xmax=0.95, color='gray', linestyle='-', linewidth=1)
@staticmethod
def create_bazi_comparison(ax, bazi1, bazi2):
"""创建八字对比图"""
pillars = ['年柱', '月柱', '日柱', '时柱']
b1_ganzhi = [f"{bazi1.bazi[p][0]}{bazi1.bazi[p][1]}"for p in pillars]
b2_ganzhi = [f"{bazi2.bazi[p][0]}{bazi2.bazi[p][1]}"for p in pillars]
y_pos = np.arange(len(pillars))
ax.barh(y_pos - 0.2, [1]*4, height=0.4, label='八字1', color='skyblue')
ax.barh(y_pos + 0.2, [1]*4, height=0.4, label='八字2', color='lightcoral')
for i, (g1, g2) in enumerate(zip(b1_ganzhi, b2_ganzhi)):
ax.text(0.1, i - 0.2, g1, ha='left', va='center', fontsize=12, fontweight='bold')
ax.text(0.1, i + 0.2, g2, ha='left', va='center', fontsize=12, fontweight='bold')
ax.set_yticks(y_pos)
ax.set_yticklabels(pillars)
ax.set_xlim(0, 1)
ax.axis('off')
ax.set_title('八字对比', fontsize=12, fontweight='bold')
ax.legend(loc='upper right', fontsize=9)
@staticmethod
def create_wuxing_radar(ax, bazi1, bazi2):
"""创建五行分布雷达图"""
categories = ['金', '木', '水', '火', '土']
N = len(categories)
wx1 = bazi1.wuxing['distribution_int']
wx2 = bazi2.wuxing['distribution_int']
values1 = [wx1.get(cat, 0) for cat in categories]
values2 = [wx2.get(cat, 0) for cat in categories]
# 归一化
max_val = max(max(values1), max(values2), 1)
values1 = [v/max_val*100 for v in values1]
values2 = [v/max_val*100 for v in values2]
# 闭合数据
values1 += values1[:1]
values2 += values2[:1]
angles = [n / float(N) * 2 * np.pi for n in range(N)]
angles += angles[:1]
ax.plot(angles, values1, 'o-', linewidth=2, label='八字1', color='blue')
ax.fill(angles, values1, alpha=0.25, color='blue')
ax.plot(angles, values2, 'o-', linewidth=2, label='八字2', color='red')
ax.fill(angles, values2, alpha=0.25, color='red')
ax.set_xticks(angles[:-1])
ax.set_xticklabels(categories)
ax.set_ylim(0, 100)
ax.set_title('五行分布对比', fontsize=12, fontweight='bold')
ax.legend(loc='upper right', fontsize=9)
@staticmethod
def create_score_gauge(ax, score):
"""创建评分仪表盘"""
# 绘制仪表盘
theta = np.linspace(0, np.pi, 100)
r = np.ones(100)
# 背景圆弧
ax.fill_betweenx(np.linspace(0, 1, 100), 0, 1, color='lightgray', alpha=0.3)
# 分数区域
score_angle = score / 100 * np.pi
theta_score = np.linspace(0, score_angle, 50)
r_score = np.linspace(0, 1, 50)
# 根据分数设置颜色
if score >= 80:
color = 'green'
elif score >= 60:
color = 'orange'
else:
color = 'red'
ax.fill_betweenx(r_score, 0, 1, color=color, alpha=0.5)
# 指针
pointer_angle = score_angle
ax.plot([0, np.cos(pointer_angle)], [0, np.sin(pointer_angle)],
color='black', linewidth=3)
ax.set_xlim(-1.2, 1.2)
ax.set_ylim(0, 1.2)
ax.axis('off')
# 显示分数
ax.text(0, 0.5, f'{score}分', ha='center', va='center',
fontsize=20, fontweight='bold', color=color)
ax.set_title('综合评分', fontsize=12, fontweight='bold')
@staticmethod
def create_detailed_analysis(ax, analysis_results):
"""创建详细分析表格"""
# 清除坐标轴
ax.axis('off')
# 准备数据
data = []
# 各项分析结果
categories = ['生肖配对', '日干关系', '五行互补', '天干地支合']
for category in categories:
if category in analysis_results:
result = analysis_results[category]
score = result.get('分数', 0)
relation = result.get('关系', '')
description = result.get('说明', '')
data.append((category, f"{relation} ({score}分)", description))
# 纳音五行
if'纳音五行'in analysis_results:
nayin = analysis_results['纳音五行']
data.append(('年柱纳音',
f"{nayin.get('八字1年柱纳音', '')} vs {nayin.get('八字2年柱纳音', '')}",
nayin.get('说明', '')))
# 创建表格
table_data = [['项目', '结果', '说明']] + data
table = ax.table(cellText=table_data, loc='center', cellLoc='left',
colWidths=[0.15, 0.25, 0.6])
table.auto_set_font_size(False)
table.set_fontsize(9)
table.scale(1, 1.5)
# 设置表格样式
for i in range(len(table_data)):
for j in range(3):
cell = table[(i, j)]
cell.set_edgecolor('gray')
if i == 0: # 标题行
cell.set_facecolor('#4CAF50')
cell.set_text_props(weight='bold', color='white')
elif i % 2 == 0:
cell.set_facecolor('#f0f0f0')
ax.set_title('详细分析结果', fontsize=12, fontweight='bold', y=0.95)
@staticmethod
def create_shengxiao_chart(ax, bazi1, bazi2):
"""创建生肖关系图"""
sx1 = bazi1.shengxiao
sx2 = bazi2.shengxiao
# 创建圆形图
angles = np.linspace(0, 2*np.pi, 12, endpoint=False)
radius = 1
# 绘制12生肖
shengxiao_list = EightCharacters.SHENGXIAO
for i, sx in enumerate(shengxiao_list):
angle = angles[i]
x = radius * np.cos(angle)
y = radius * np.sin(angle)
# 标记两个输入生肖
if sx == sx1:
color = 'blue'
size = 300
label = '八字1'
elif sx == sx2:
color = 'red'
size = 300
label = '八字2'
else:
color = 'gray'
size = 200
label = ''
ax.scatter(x, y, s=size, c=color, alpha=0.7, label=label if label else'')
ax.text(x*1.2, y*1.2, sx, ha='center', va='center', fontsize=9)
# 连接线
idx1 = shengxiao_list.index(sx1)
idx2 = shengxiao_list.index(sx2)
x1 = radius * np.cos(angles[idx1])
y1 = radius * np.sin(angles[idx1])
x2 = radius * np.cos(angles[idx2])
y2 = radius * np.sin(angles[idx2])
ax.plot([x1, x2], [y1, y2], 'k-', linewidth=2, alpha=0.5)
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)
ax.axis('equal')
ax.axis('off')
ax.set_title('生肖关系图', fontsize=12, fontweight='bold')
# 添加图例(只显示一次)
handles, labels = ax.get_legend_handles_labels()
unique = [(h, l) for i, (h, l) in enumerate(zip(handles, labels)) if l not in labels[:i]]
if unique:
ax.legend(*zip(*unique), loc='upper center', bbox_to_anchor=(0.5, -0.1), ncol=2)
@staticmethod
def create_wuxing_bar(ax, bazi1, bazi2):
"""创建五行条形对比图"""
categories = ['金', '木', '水', '火', '土']
wx1 = bazi1.wuxing['distribution_int']
wx2 = bazi2.wuxing['distribution_int']
values1 = [wx1.get(cat, 0) for cat in categories]
values2 = [wx2.get(cat, 0) for cat in categories]
x = np.arange(len(categories))
width = 0.35
ax.bar(x - width/2, values1, width, label='八字1', color='skyblue')
ax.bar(x + width/2, values2, width, label='八字2', color='lightcoral')
ax.set_xlabel('五行', fontsize=10)
ax.set_ylabel('强度', fontsize=10)
ax.set_title('五行强度对比', fontsize=12, fontweight='bold')
ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.legend(fontsize=9)
# 添加数值标签
for i, v in enumerate(values1):
ax.text(i - width/2, v + 0.1, str(v), ha='center', va='bottom', fontsize=8)
for i, v in enumerate(values2):
ax.text(i + width/2, v + 0.1, str(v), ha='center', va='bottom', fontsize=8)
@staticmethod
def save_all_charts(bazi1, bazi2, analysis_results, person1_name="八字1", person2_name="八字2"):
"""保存所有图表到桌面文件夹"""
# 创建综合报告
fig_report = Visualization.create_comprehensive_report(bazi1, bazi2, analysis_results)
# 获取保存路径
report_filename = FileManager.get_unique_filename(
f"合婚报告_{person1_name}_{person2_name}", ".png"
)
# 保存报告
fig_report.savefig(report_filename, dpi=300, bbox_inches='tight')
plt.close(fig_report)
print(f"合婚报告已保存: {report_filename.name}")
# 创建单独图表
fig1, ax1 = plt.subplots(figsize=(10, 6))
Visualization.create_wuxing_bar(ax1, bazi1, bazi2)
wuxing_filename = FileManager.get_unique_filename(
f"五行对比_{person1_name}_{person2_name}", ".png"
)
fig1.savefig(wuxing_filename, dpi=300, bbox_inches='tight')
plt.close(fig1)
print(f"五行对比图已保存: {wuxing_filename.name}")
return report_filename
class BaziMarriageApp:
"""八字合婚应用主类"""
def __init__(self):
self.person1 = None
self.person2 = None
self.analysis_results = None
self.save_path = None
def get_input_with_default(self, prompt, default_value, input_type=int):
"""获取用户输入,允许默认值"""
user_input = input(f"{prompt} (默认: {default_value}): ").strip()
if not user_input:
return default_value
try:
return input_type(user_input)
except ValueError:
print(f"输入无效,使用默认值 {default_value}")
return default_value
def input_person_info(self, person_num):
"""输入个人信息"""
print(f"\n【输入八字{person_num}信息】")
print("(请输入阳历出生日期和时间)")
year = self.get_input_with_default("出生年份(如1990)", 1990)
month = self.get_input_with_default("出生月份(1-12)", 5)
day = self.get_input_with_default("出生日期(1-31)", 15)
hour = self.get_input_with_default("出生时辰(0-23,23-1点为子时)", 10)
name = input(f"八字{person_num}的姓名或昵称(可选): ").strip()
if not name:
name = f"八字{person_num}"
return {
'year': year,
'month': month,
'day': day,
'hour': hour,
'name': name
}
def calculate_bazi(self):
"""计算八字"""
print("\n" + "="*60)
print("正在计算八字和分析合婚...")
print("-"*60)
try:
# 创建八字对象
self.person1 = EightCharacters(
self.info1['year'], self.info1['month'],
self.info1['day'], self.info1['hour']
)
self.person2 = EightCharacters(
self.info2['year'], self.info2['month'],
self.info2['day'], self.info2['hour']
)
return True
except Exception as e:
print(f"计算八字时出错: {e}")
return False
def analyze_marriage(self):
"""进行合婚分析"""
# 进行合婚分析
analyzer = MarriageAnalyzer()
self.analysis_results = analyzer.analyze_compatibility(self.person1, self.person2)
return True
def display_results(self):
"""显示分析结果"""
print("\n" + "="*60)
print("【合婚分析结果】")
print("="*60)
# 显示基本信息
basic_info = self.analysis_results['基本信息']
print(f"\n八字信息:")
print(f" {self.info1['name']}: {basic_info['八字1']} 生肖: {basic_info['生肖1']}")
print(f" {self.info2['name']}: {basic_info['八字2']} 生肖: {basic_info['生肖2']}")
print(f"\n日主信息:")
print(f" {self.info1['name']}: {basic_info['日主1']}")
print(f" {self.info2['name']}: {basic_info['日主2']}")
print(f"\n五行分布:")
print(f" {self.info1['name']}: {basic_info['五行分布1']}")
print(f" {self.info2['name']}: {basic_info['五行分布2']}")
# 显示详细分析
categories = ['生肖配对', '日干关系', '五行互补', '天干地支合']
for category in categories:
if category in self.analysis_results:
result = self.analysis_results[category]
print(f"\n{category}:")
print(f" 关系: {result.get('关系', '')}")
print(f" 分数: {result.get('分数', 0)}分")
print(f" 说明: {result.get('说明', '')}")
# 显示纳音五行
if'纳音五行'in self.analysis_results:
nayin = self.analysis_results['纳音五行']
print(f"\n年柱纳音:")
print(f" {self.info1['name']}: {nayin.get('八字1年柱纳音', '')}")
print(f" {self.info2['name']}: {nayin.get('八字2年柱纳音', '')}")
print(f" 说明: {nayin.get('说明', '')}")
# 显示综合评分
if'综合评分'in self.analysis_results:
total = self.analysis_results['综合评分']
print(f"\n综合评分:")
print(f" 分数: {total.get('分数', 0)}分")
print(f" 评级: {total.get('评级', '')}")
print(f" 星级: {total.get('星级', '')}")
# 显示详细建议
if'详细建议'in self.analysis_results:
print(f"\n合婚建议:")
for i, advice in enumerate(self.analysis_results['详细建议'], 1):
print(f" {i}. {advice}")
def save_results(self):
"""保存分析结果到文件"""
print("\n" + "="*60)
print("正在保存分析结果...")
try:
# 1. 保存可视化图表
vis = Visualization()
report_file = vis.save_all_charts(
self.person1, self.person2,
self.analysis_results,
self.info1['name'], self.info2['name']
)
# 2. 保存JSON格式的分析结果
json_data = {
'分析时间': datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
'八字1信息': {
'姓名': self.info1['name'],
'出生日期': f"{self.info1['year']}年{self.info1['month']}月{self.info1['day']}日{self.info1['hour']}时",
'八字': self.person1.get_bazi_string(),
'生肖': self.person1.shengxiao,
'日主': f"{self.person1.rizhu}({self.person1.rizhu_wuxing})",
'五行分布': self.person1.get_wuxing_string()
},
'八字2信息': {
'姓名': self.info2['name'],
'出生日期': f"{self.info2['year']}年{self.info2['month']}月{self.info2['day']}日{self.info2['hour']}时",
'八字': self.person2.get_bazi_string(),
'生肖': self.person2.shengxiao,
'日主': f"{self.person2.rizhu}({self.person2.rizhu_wuxing})",
'五行分布': self.person2.get_wuxing_string()
},
'合婚分析结果': self.analysis_results
}
json_file = FileManager.save_json(
json_data,
f"合婚结果_{self.info1['name']}_{self.info2['name']}"
)
# 3. 保存文本报告
report_text = self.generate_text_report()
text_file = FileManager.save_text_report(
report_text,
f"合婚报告_{self.info1['name']}_{self.info2['name']}"
)
print("\n" + "="*60)
print("分析完成!所有结果已保存到桌面'合婚结果'文件夹")
print(f"1. 可视化报告: {report_file.name}")
print(f"2. JSON数据: {json_file.name}")
print(f"3. 文本报告: {text_file.name}")
# 显示文件夹路径
folder_path = FileManager.create_result_folder()
print(f"\n文件夹路径: {folder_path}")
return True
except Exception as e:
print(f"保存结果时出错: {e}")
return False
def generate_text_report(self):
"""生成文本报告"""
report_lines = []
# 标题
report_lines.append("="*60)
report_lines.append(" 八字合婚分析报告")
report_lines.append("="*60)
# 基本信息
report_lines.append(f"\n生成时间: {datetime.datetime.now().strftime('%Y年%m月%d日 %H:%M:%S')}")
report_lines.append("\n【基本信息】")
basic_info = self.analysis_results['基本信息']
report_lines.append(f"\n{self.info1['name']}:")
report_lines.append(f" 八字: {basic_info['八字1']}")
report_lines.append(f" 生肖: {basic_info['生肖1']}")
report_lines.append(f" 日主: {basic_info['日主1']}")
report_lines.append(f" 五行: {basic_info['五行分布1']}")
report_lines.append(f"\n{self.info2['name']}:")
report_lines.append(f" 八字: {basic_info['八字2']}")
report_lines.append(f" 生肖: {basic_info['生肖2']}")
report_lines.append(f" 日主: {basic_info['日主2']}")
report_lines.append(f" 五行: {basic_info['五行分布2']}")
# 详细分析
report_lines.append("\n【详细分析】")
categories = ['生肖配对', '日干关系', '五行互补', '天干地支合']
for category in categories:
if category in self.analysis_results:
result = self.analysis_results[category]
report_lines.append(f"\n{category}:")
report_lines.append(f" 关系: {result.get('关系', '')}")
report_lines.append(f" 分数: {result.get('分数', 0)}分")
report_lines.append(f" 说明: {result.get('说明', '')}")
# 纳音五行
if'纳音五行'in self.analysis_results:
nayin = self.analysis_results['纳音五行']
report_lines.append(f"\n年柱纳音:")
report_lines.append(f" {self.info1['name']}: {nayin.get('八字1年柱纳音', '')}")
report_lines.append(f" {self.info2['name']}: {nayin.get('八字2年柱纳音', '')}")
report_lines.append(f" 说明: {nayin.get('说明', '')}")
# 综合评分
if'综合评分'in self.analysis_results:
total = self.analysis_results['综合评分']
report_lines.append(f"\n【综合评分】")
report_lines.append(f" 分数: {total.get('分数', 0)}分")
report_lines.append(f" 评级: {total.get('评级', '')}")
report_lines.append(f" 星级: {total.get('星级', '')}")
# 合婚建议
if'详细建议'in self.analysis_results:
report_lines.append(f"\n【合婚建议】")
for i, advice in enumerate(self.analysis_results['详细建议'], 1):
report_lines.append(f" {i}. {advice}")
# 免责声明
report_lines.append("\n" + "="*60)
report_lines.append("注意事项:")
report_lines.append("1. 八字合婚仅供参考,婚姻幸福需要双方共同努力")
report_lines.append("2. 命理分析不可尽信,真情实意最为重要")
report_lines.append("3. 如遇相冲相克,可通过风水、性格调和等方式化解")
report_lines.append("="*60)
return"\n".join(report_lines)
def ask_show_chart(self):
"""询问是否显示图表"""
show_chart = input("\n是否显示图表?(y/n, 默认n): ").strip().lower()
if show_chart == 'y':
plt.show()
def run(self):
"""运行主程序"""
# 显示欢迎信息
print("\n" + "="*60)
print(" 八字合婚分析系统 v3.0")
print("="*60)
print("说明:本系统基于传统八字命理,结果仅供娱乐参考")
print("所有结果将自动保存到桌面'合婚结果'文件夹")
print("-"*60)
# 输入信息
self.info1 = self.input_person_info(1)
self.info2 = self.input_person_info(2)
# 计算八字
if not self.calculate_bazi():
return
# 进行合婚分析
if not self.analyze_marriage():
return
# 显示结果
self.display_results()
# 保存结果
if not self.save_results():
return
# 询问是否显示图表
self.ask_show_chart()
print("\n感谢使用八字合婚分析系统!")
def main():
"""主函数"""
try:
app = BaziMarriageApp()
app.run()
except KeyboardInterrupt:
print("\n\n程序被用户中断。")
except Exception as e:
print(f"\n程序运行出错: {e}")
print("请检查输入是否正确,或联系开发者。")
if __name__ == "__main__":
main()
通过上述代码运行(期间需要输入二位新人的出生年月日时四柱信息),我们可以得到如下结果:



婚姻合八字(又称八字合婚)是基于中国传统命理学的理论,阴阳五行学说与天干地支系统是它的理论基础,主要合婚原则包括:
1.生肖配对:三合、六合、六冲等
2.日干五行:双方日主(日柱天干)的相生相克
3.神煞匹配:如桃花、孤鸾、红鸾等
4.大运流年:运势同步性分析
由于八字合婚的复杂性,建议在实际应用中参考专业的命理知识库或咨询专业人士。此外,这种传统方法仅供娱乐参考,不应作为现实决策的唯一依据。
代码运行说明:每次合婚分析的结果都会自动保存到桌面的"合婚结果"文件夹中,无需手动指定路径。