go
package main
import (
"fmt"
"math/rand"
"time"
)
// YaoValue 表示爻的数值(6、7、8、9)
type YaoValue int
const (
LaoYin YaoValue = 6 // 老阴(变爻)
ShaoYang YaoValue = 7 // 少阳(不变阳)
ShaoYin YaoValue = 8 // 少阴(不变阴)
LaoYang YaoValue = 9 // 老阳(变爻)
)
// castYao 模拟生成单爻的完整三变过程
func castYao() YaoValue {
total := 49 // "大衍之数五十,其用四十有九"
// 进行三次变化(三变)
for i := 0; i < 3; i++ {
total = oneChange(total)
}
// 最终剩余数除以4得到6/7/8/9
result := total / 4
return YaoValue(result)
}
// oneChange 实现单次变化:分二、挂一、揲四、归奇
func oneChange(total int) int {
// 1. 分而为二以象两:随机分成左右两堆
// 确保每堆至少有4根(揲四的最小单位)
minHeap := 4
maxHeap := total - 1 - minHeap // 挂一后剩余的最大可能值
rand.Seed(time.Now().UnixNano())
left := minHeap + rand.Intn(maxHeap-minHeap+1)
right := total - 1 - left // 2. 挂一以象三:从右边取1根
// 3. 揲之以四以象四时:左右分别以4为单位计数
// 4. 归奇于扐以象闰:取余数(1-4)
leftRem := left % 4
rightRem := right % 4
// 余数为0时视为4(整除情况)
if leftRem == 0 {
leftRem = 4
}
if rightRem == 0 {
rightRem = 4
}
// 剩余蓍草数 = 总数 - 挂一 - 左余 - 右余
remaining := total - 1 - leftRem - rightRem
return remaining
}
// generateSixLines 生成六爻(完整卦象)
func generateSixLines() [6]YaoValue {
var lines [6]YaoValue
for i := 0; i < 6; i++ {
lines[i] = castYao()
// 每次起卦间隔微小时间,确保随机性
time.Sleep(1 * time.Millisecond)
}
return lines
}
// 演示单爻的详细计算过程
func demonstrateSingleYao() {
fmt.Println("=== 单爻生成详细过程演示 ===")
total := 49
fmt.Printf("初始总数:%d
", total)
for change := 1; change <= 3; change++ {
// 分二
left := 4 + rand.Intn(41) // 4-44之间的随机数
right := total - 1 - left
// 揲四取余
leftRem := left % 4
rightRem := right % 4
if leftRem == 0 {
leftRem = 4
}
if rightRem == 0 {
rightRem = 4
}
remaining := total - 1 - leftRem - rightRem
fmt.Printf("第%d变:左堆=%d,右堆=%d,挂一=1,左余=%d,右余=%d,剩余=%d
",
change, left, right, leftRem, rightRem, remaining)
total = remaining
}
finalYao := total / 4
fmt.Printf("最终结果:%d ÷ 4 = %d → ", total, finalYao)
switch finalYao {
case 6:
fmt.Println("老阴(变阴)")
case 7:
fmt.Println("少阳(不变阳)")
case 8:
fmt.Println("少阴(不变阴)")
case 9:
fmt.Println("老阳(变阳)")
}
}
func main() {
fmt.Println("=== 大衍筮法分堆与余数计算实现 ===")
fmt.Println("核心算法:分二、挂一、揲四、归奇 → 三变 → 得爻")
fmt.Println()
// 演示单爻生成过程
demonstrateSingleYao()
fmt.Println("
=== 完整六爻生成 ===")
hexagram := generateSixLines()
// 输出六爻结果
fmt.Println("爻位\t数值\t阴阳\t变爻\t说明")
fmt.Println("----\t----\t----\t----\t--------")
for i, yao := range hexagram {
position := i + 1
yinyang := ""
changing := ""
description := ""
switch yao {
case 6:
yinyang = "阴"
changing = "★"
description = "老阴(变阴)"
case 7:
yinyang = "阳"
description = "少阳(不变)"
case 8:
yinyang = "阴"
description = "少阴(不变)"
case 9:
yinyang = "阳"
changing = "★"
description = "老阳(变阳)"
}
fmt.Printf("第%d爻\t%d\t%s\t%s\t%s
",
position, yao, yinyang, changing, description)
}
// 构建二进制卦码(从初爻到上爻)
fmt.Println("
=== 卦象编码 ===")
var binaryCode string
var changingCount int
for i := 0; i < 6; i++ {
if hexagram[i] == 6 || hexagram[i] == 8 {
binaryCode += "0" // 阴爻
} else {
binaryCode += "1" // 阳爻
}
if hexagram[i] == 6 || hexagram[i] == 9 {
changingCount++
}
}
fmt.Printf("二进制卦码:%s(从初爻到上爻)
", binaryCode)
fmt.Printf("变爻数量:%d
", changingCount)
// 显示本卦与变卦的生成逻辑
fmt.Println("
=== 本卦与变卦生成 ===")
var originalCode, changedCode string
for i := 5; i >= 0; i-- { // 从上爻到初爻
val := hexagram[i]
// 本卦
if val == 6 || val == 8 {
originalCode = "0" + originalCode
} else {
originalCode = "1" + originalCode
}
// 变卦:老阴变阳,老阳变阴,少阳少阴不变
if val == 9 || val == 8 { // 老阳9变阴,少阴8不变
changedCode = "0" + changedCode
} else { // 老阴6变阳,少阳7不变
changedCode = "1" + changedCode
}
}
fmt.Printf("本卦编码:%s
", originalCode)
fmt.Printf("变卦编码:%s
", changedCode)
}
分堆与余数计算的核心算法解析
1. 大衍筮法的数学原理
大衍筮法的核心是通过三次变化(三变)将49根蓍草逐步减少,最终得到6、7、8、9四个数字之一。每次变化包含四个步骤:
| 步骤 | 数学操作 | 易学象征 | 代码实现 |
|---|---|---|---|
| 分二 | 随机分为两堆 | 象征天地两仪 | left := minHeap + rand.Intn(...) |
| 挂一 | 从右边取1根 | 象征人(三才) | right := total - 1 - left |
| 揲四 | 左右分别除以4 | 象征四时 | left % 4 和 right % 4 |
| 归奇 | 取余数(1-4) | 象征闰月 | 余数0转为4的处理 |
2. 余数计算的数学推导
经过三次变化后,剩余蓍草数必为以下四种情况之一,对应最终的爻值:
go
// 三变后剩余蓍草数与爻值对应表
剩余蓍草数 ÷4结果 爻值 爻象 属性
24、28、32、36 → 6 老阴 变爻
25、29、33、37 → 7 少阳 不变
26、30、34、38 → 8 少阴 不变
27、31、35、39 → 9 老阳 变爻
这个数学规律的推导过程如下:
- 第一次变化后剩余:49 - 1 - (1+2+3+4) = 40 或 44
- 第二次变化后剩余:36、32、28、24
- 第三次变化后剩余:24-39之间的4的倍数±(1-3)
3. 随机分堆的实现细节
go
// 关键随机数生成逻辑
func oneChange(total int) int {
// 确保每堆至少有4根(揲四的最小单位)
minHeap := 4
maxHeap := total - 1 - minHeap // 挂一后的最大值
// 生成左堆的随机数量
left := minHeap + rand.Intn(maxHeap-minHeap+1)
// 右堆 = 总数 - 1(挂一)- 左堆
right := total - 1 - left
// 验证:确保左右堆都至少为4
if left < 4 || right < 4 {
panic("分堆错误:堆中蓍草数不足4")
}
return calculateRemaining(total, left, right)
}
4. 完整六爻生成流程
生成完整卦象需要重复六次单爻生成过程,每次都要确保随机性独立:
go
// 六爻生成的最佳实践
func generateHexagramWithValidation() [6]YaoValue {
var hexagram [6]YaoValue
seeds := make([]int64, 6)
for i := 0; i < 6; i++ {
// 为每爻使用不同的随机种子
seeds[i] = time.Now().UnixNano() + int64(i)
rand.Seed(seeds[i])
hexagram[i] = castYao()
// 验证爻值有效性
if hexagram[i] < 6 || hexagram[i] > 9 {
panic(fmt.Sprintf("无效爻值:%d", hexagram[i]))
}
}
return hexagram
}
5. 边界条件与错误处理
在实际实现中需要考虑以下边界情况:
go
// 边界条件处理函数
func validateCalculation(total int) error {
// 检查初始总数
if total != 49 {
return fmt.Errorf("初始蓍草数必须为49,当前:%d", total)
}
// 模拟1000次计算验证结果范围
for i := 0; i < 1000; i++ {
yao := castYao()
if yao < 6 || yao > 9 {
return fmt.Errorf("爻值超出范围:%d", yao)
}
}
// 验证概率分布(理论上6、7、8、9应接近均匀分布)
counts := map[YaoValue]int{6: 0, 7: 0, 8: 0, 9: 0}
for i := 0; i < 10000; i++ {
yao := castYao()
counts[yao]++
}
fmt.Println("概率分布验证:")
for val, count := range counts {
percentage := float64(count) / 10000.0 * 100
fmt.Printf(" 爻值%d:%.2f%%
", val, percentage)
}
return nil
}
6. 性能优化与并发处理
对于需要大量生成卦象的应用,可以考虑以下优化:
go
// 并发生成多个卦象
func generateMultipleHexagrams(n int) [][6]YaoValue {
results := make([][6]YaoValue, n)
var wg sync.WaitGroup
for i := 0; i < n; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
results[index] = generateSixLines()
}(i)
}
wg.Wait()
return results
}
// 缓存常见计算结果
var calculationCache = make(map[string]YaoValue)
func cachedCastYao(seed int64) YaoValue {
cacheKey := fmt.Sprintf("%d", seed)
if result, ok := calculationCache[cacheKey]; ok {
return result
}
rand.Seed(seed)
result := castYao()
calculationCache[cacheKey] = result
return result
}
7. 实际应用示例
假设我们需要为一个具体的占卜问题生成卦象:
go
// 应用示例:为特定问题起卦
func divinationForQuestion(question string) {
fmt.Printf("占卜问题:%s
", question)
fmt.Println("开始起卦...")
// 生成六爻
hexagram := generateSixLines()
// 输出详细过程
fmt.Println("
爻象生成过程:")
for i, yao := range hexagram {
fmt.Printf(" 第%d爻:", i+1)
// 模拟三变过程
total := 49
for change := 1; change <= 3; change++ {
left := 4 + rand.Intn(41)
right := total - 1 - left
leftRem := left % 4
rightRem := right % 4
if leftRem == 0 { leftRem = 4 }
if rightRem == 0 { rightRem = 4 }
remaining := total - 1 - leftRem - rightRem
fmt.Printf(" 变%d→余%d", change, remaining)
total = remaining
}
fmt.Printf(" → 爻值%d", yao)
switch yao {
case 6:
fmt.Println("(老阴 ⚋)")
case 7:
fmt.Println("(少阳 ---)")
case 8:
fmt.Println("(少阴 - -)")
case 9:
fmt.Println("(老阳 ⚊)")
}
}
// 生成卦象解读
interpretHexagram(hexagram)
}
// 卦象解读函数
func interpretHexagram(hexagram [6]YaoValue) {
fmt.Println("
=== 卦象解读 ===")
// 计算阴阳爻数量
var yinCount, yangCount int
for _, yao := range hexagram {
if yao == 6 || yao == 8 {
yinCount++
} else {
yangCount++
}
}
fmt.Printf("阴爻数:%d,阳爻数:%d
", yinCount, yangCount)
// 判断卦象属性
if yinCount > yangCount {
fmt.Println("卦性:偏阴(主静、柔顺)")
} else if yangCount > yinCount {
fmt.Println("卦性:偏阳(主动、刚健)")
} else {
fmt.Println("卦性:阴阳平衡")
}
// 变爻分析
var changingYaos []int
for i, yao := range hexagram {
if yao == 6 || yao == 9 {
changingYaos = append(changingYaos, i+1)
}
}
if len(changingYaos) > 0 {
fmt.Printf("变爻位置:%v
", changingYaos)
fmt.Println("提示:本卦将转为变卦,需结合两卦综合解读")
} else {
fmt.Println("无变爻:直接以本卦卦辞解读")
}
}
通过上述完整的Go语言实现,我们不仅实现了大衍筮法的核心分堆与余数计算,还提供了完整的卦象生成、验证和解读功能。这个实现严格遵循《周易·系辞上传》的记载,同时利用现代编程技术确保了算法的正确性和随机性,为《周易》研究提供了可靠的技术工具。