麻将老胡头算法

智能胡牌判断算法 · 麻将AI核心引擎

麻将老胡头算法核心示意图
★ 经典递归回溯算法 ★

什么是麻将老胡头算法?

老胡头算法(Lao Hu Tou Algorithm)是麻将AI领域最经典的胡牌判断算法之一。它通过递归回溯的方式,将手牌逐一拆解为顺子、刻子和对子,从而高效判断当前手牌是否满足胡牌条件。

该算法起源于国内早期的麻将游戏开发社区,因其代码简洁、逻辑清晰、性能出色而广泛流传,至今仍是许多麻将AI系统的核心基础模块。

麻将胡牌算法流程图

算法核心原理

麻将胡牌的本质,是将所有手牌(胡牌时共14张)分解为面子将牌的组合。

胡牌牌型 = 4 × 面子 + 1 × 将牌

其中面子可以是顺子(同花色连续三张)或刻子(三张同牌),将牌为一对(两张相同)。

老胡头算法的核心思路:先挑出一对将,把剩余牌做递归拆解,只要找到一种全部分解的方案,即可判定胡牌。

算法的三大特点

递归逻辑简洁清晰
剪枝后性能高效
🔧可扩展性极强
🌍适用多种麻将规则

算法实现步骤

  1. 统计牌数

    将34种牌(万、条、筒、字牌)映射为数组索引,统计每种牌的张数。

  2. 尝试将牌

    遍历所有牌型,轮流尝试每种牌拿出2张作为将牌(即对子)。

  3. 递归拆解

    将剩余牌递归拆为顺子或刻子,若全部拆完则判定为胡牌。

  4. 剪枝优化

    跳过数量为0的牌型,大量减少无效递归,提升运算速度。

老胡头算法拆解流程图

代码实现(JavaScript版)

以下是老胡头算法的核心代码,采用经典递归回溯框架,并加入了剪枝优化处理:

// 麻将老胡头算法 - 胡牌判断核心
function isHu(pai) {
    // 初始化34种牌的计数数组
    let count = new Array(34).fill(0);
    for (let p of pai) count[p]++;
    
    // 总牌数必须是 3的倍数+2
    if (pai.length % 3 !== 2) return false;
    
    // 依次尝试每种牌作为将牌
    for (let king = 0; king < 34; king++) {
        if (count[king] >= 2) {
            count[king] -= 2;  // 拿出将牌
            if (canSplit(count)) {
                count[king] += 2;
                return true;
            }
            count[king] += 2;
        }
    }
    return false;
}

// 递归拆解剩余牌
function canSplit(count) {
    // 找到第一张非零牌
    let i = 0;
    while (i < 34 && count[i] === 0) i++;
    if (i === 34) return true;  // 全部拆完了
    
    // 尝试刻子(三张相同)
    if (count[i] >= 3) {
        count[i] -= 3;
        if (canSplit(count)) {
            count[i] += 3;
            return true;
        }
        count[i] += 3;
    }
    
    // 尝试顺子(仅限万条筒)
    if (i < 27 && count[i] > 0 && 
        count[i+1] > 0 && count[i+2] > 0 &&
        Math.floor(i/9) === Math.floor((i+2)/9)) {
        count[i]--; count[i+1]--; count[i+2]--;
        if (canSplit(count)) {
            count[i]++; count[i+1]++; count[i+2]++;
            return true;
        }
        count[i]++; count[i+1]++; count[i+2]++;
    }
    
    return false;
}

以上代码可直接复制运行,通过传入手牌编号数组即可判断是否胡牌。

常见问题解答

Q1:老胡头算法能处理七对子吗?
可以。七对子(7组对子共14张)属于特殊胡牌牌型。老胡头算法可通过增加额外的检测模块来支持:在标准胡牌判断失败后,额外检查手牌是否满足7个对子的条件。同时支持清一色、碰碰胡等特殊牌型时,也可用同样的思路在前置阶段完成检测。
Q2:算法的时间复杂度是多少?
最坏情况下的时间复杂度约为O(34×3^n),其中n为可拆解的牌组数量。但经过剪枝优化后,实际运行时性能极高。在普通智能手机上判断一手牌通常只需几微秒,完全能够满足实时计算的需求,适用于麻将AI、辅助分析等对性能要求较高的场景。
Q3:如何处理风牌和字牌?
风牌(东南西北)和字牌(中发白)在算法中作为独立的牌处理。它们与万条筒的区别在于:字牌没有顺子的概念,只能组成刻子。代码中已通过 i<27 的判断条件确保只有万条筒区域才能尝试拆顺子,字牌区域仅能拆刻子,逻辑上天然正确。
Q4:算法支持十三幺特殊牌型吗?
基础版本不直接支持十三幺,因为十三幺的牌型结构与常规胡牌有本质区别。建议的做法是:在运行老胡头算法之前,先单独检测特殊牌型(如十三幺、九莲宝灯、七对等)。若检测到满足特殊牌型则直接判定胡牌,否则再走标准的老胡头递归判断流程。
Q5:老胡头算法与通用回溯算法有何区别?
老胡头算法是通用回溯算法在麻将场景下的优化落地实现。它在思想方法上遵循回溯算法的核心套路,但针对麻将特点做了特殊设计:比如将牌枚举策略、刻子优先拆解、顺子的花色边界判断、深度剪枝等。相比直接套用通用回溯框架,老胡头算法的代码量更小、运行效率更高、且更容易理解和维护。

算法优化建议

  • 使用牌型哈希缓存:对相同的牌型组合复用计算结果,避免重复递归遍历。
  • 有限深度剪枝:设定最大递归深度,提前终止无效子树搜索,大幅提升最坏场景性能。
  • 优先拆解离散牌:优先处理数量少、组合可能性少的牌,减少搜索分支。
  • 特殊牌型前置检测:将七对、十三幺等特殊牌型放到优先判断,避免进入常规递归流程。
  • 动态规划优化:对高频重复的子问题做记忆化存储,将指数级复杂度降为多项式级。