开源一个不过百行代码而又强悍与效率的麻将赖子算法!! <开源与原创>

前言:  
         博主16年底开始从事休闲类麻将游戏开发,开始接触赖子算法时,当时互联网找不到任何资料,于是自己琢磨了三天开始尝试写出第一个算法并商用至今,开源给行业内其他公司使用,其中思路部分来源于《算法导论》机械工业第三版第16章2节: 贪心算法原理。

关于贪心

贪心算法就是做出一系列选择来使原问题达到最优解。在每一个决策点,都是做出当前看来的最优选择,比如在活动选择问题里面,我们总是在一个问题的基础上选择结束时间最早的活动,之后再在剩下活动的基础上选出结束时间最早的活动,以此类推,直到没有活动可以进行选择。但是遗憾的是这种算法并不是总能得到最优解,并且是否能得到最优解还取决于对于贪心策略的选择。

一般来说,设计贪心算法涉及到下面几个步骤:

  1. 确定问题的最优子结构
  2. 基于问题的最优子结构设计一个递归算法
  3. 证明我们做出的贪心选择,只剩下一个子问题
  4. 证明贪心选择总是安全的
  5. 设计一个递归算法实现贪心策略
  6. 将贪心算法转化为迭代算法

到目前为止已经写过三款不同的赖子算法,分别为:
  1. 递归贪心算法              (<1ms级 , 复杂度为:最好O(n),最差O(n^3) )
  2. 优化型迭代贪心算法   ( us级 , 复杂度为:最好O(n),最差O(n^2) )
  3. 哈希表查找                 (us级 , 复杂度为:O(1) )

     

以上所有版本均支持多赖子,哪怕手牌是23张,全是赖子,均不影响复杂度,差异甚小

在征得源项目老板的同意下,分享第一种算法给游戏爱好者们学习与进步,废话少说,直接上代码如下:

-----------------------------------------------------------------------------------------------------------------------
//会牌核心算法递归版本, 由原胡牌算法演进而来,检测更全面    by    iori  2017/1/10
bool laizi_hupai_base(int *pi_mj, int i_num, int *pi_select,int laizi_val)
{
    //pi_select[]用于在递归调用时记录已经访问过的牌
    int i,j,k;
    int i_max,i_min,i_mid;
    int i_mark1,i_mark2;
    int i_have3=0 ,laizi_num = 0;
    
    for(i=0; i<i_num; i++)
    {
        if( pi_select[i] )
            continue;
        else
            i_mark1 = i;
        
        for( j=i+1; j<i_num; j++)
        {
            if( i == j )
                continue;
            if(pi_select[j])
                continue;
            else
                i_mark2 = j;
            
            for( k=j+1; k<i_num; k++)
            {
                if( i==k || j==k )
                    continue;
                if( pi_select[k] )
                    continue;
                
                i_have3 = 1;
                laizi_num = 0;
                i_max = std::max(pi_mj[i], std::max(pi_mj[j], pi_mj[k]));
                i_min = std::min(pi_mj[i], std::min(pi_mj[j], pi_mj[k]));
                i_mid = (i_max + i_min) / 2;
                
		//判断选中的是否为赖子,这里传入多个val,可以支持多赖
                if (pi_mj[i] == laizi_val) laizi_num++;
                if (pi_mj[j] == laizi_val) laizi_num++;
                if (pi_mj[k] == laizi_val) laizi_num++;
                
                if (laizi_num >= 2) //能成有会刻或者顺,默认刻
                {
                        // do something  根据你的项目需求可以保存数据
                }
                else
                if( (pi_mj[i] == pi_mj[j] && pi_mj[i] == pi_mj[k]) || (laizi_num == 1 && (pi_mj[i]  ==  pi_mj[j] ||  pi_mj[j] == pi_mj[k] || pi_mj[i]  == pi_mj[k]))  )  //表示能组成刻子
                {
                        // do something
                }
                else  if (laizi_num == 1)//判断是否能成有会顺
                {
                    if(pi_mj[i] == laizi_val)
                    {
                        if(pi_mj[j] - pi_mj[k] != -1 && pi_mj[j] - pi_mj[k] != -2 && pi_mj[j] - pi_mj[k] != 1 && pi_mj[j] - pi_mj[k] != 2 )
                            continue;
			//这里根据项目不同的牌值区分 中发白与风牌过滤掉,博主这个项目没有,就不补上了
                    }else if(pi_mj[j] == laizi_val)
                    {
                        if(pi_mj[i] - pi_mj[k] != -1 && pi_mj[i] - pi_mj[k] != -2 && pi_mj[i] - pi_mj[k] != 1 && pi_mj[i] - pi_mj[k] != 2 )
                            continue;
                    }else if(pi_mj[k] == laizi_val)
                    {
                        if(pi_mj[i] - pi_mj[j] != -1 && pi_mj[i] - pi_mj[j] != -2 && pi_mj[i] - pi_mj[j] != 1 && pi_mj[i] - pi_mj[j] != 2 )
                            continue;
                    }
                        
                }else
                if( i_max-i_min == 2 && ( pi_mj[i] == i_mid || pi_mj[j] == i_mid || pi_mj[k] == i_mid)) //表示能组成顺子
                {
                        // do something
                }else
                    continue;
                
                //记录下来 下面递归
                pi_select[i] = pi_select[j] = pi_select[k] = 1;
                
                if( !laizi_hupai_base(pi_mj, i_num, pi_select,laizi_val) )
                {
                    pi_select[i] = pi_select[j] = pi_select[k] = 0; //递归一次结束,返回
                    continue;
                }
                
                //选择的3张成刻、顺,而剩下的和了
                return true;
            }
            
            //只剩两张牌, 相等 或者 其中一张为癞子时,满足牌型
            if( (i_have3 == 0) &&( pi_mj[i_mark1] == pi_mj[i_mark2] || pi_mj[i_mark1] == laizi_val ||  pi_mj[i_mark2] == laizi_val  ))
            {
                return true; //这里不退出,可以保存下来计算出所有能胡的牌型
            }
        }
    }
    return false;
}			

------------------------------------------------------------------ end by Iori

    算法好不好,跑一跑就知道,打上time log就明了,如有疑问,欢迎留言,支持原创,支持开源!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值