德州扑克-面对五重循环算法该如何优化?

9 篇文章 2 订阅
6 篇文章 0 订阅

      最近在做德州扑克项目,写到从7张牌中选择5张最优组合牌时,翻看了旧项目的代码,发现居然有人写出了五重循环算法,如图

-- //最大牌型
function GameLogic:FiveFromSeven( cbHandCardData,  cbCenterCardData,cbCardCount)
	-- //临时变量
    local cbTempCardData={}
    local cbLastCardData={}
	-- //拷贝数据s
	cbTempCardData=mytools.clone(cbHandCardData) 
	self:ArrayConcat(cbTempCardData,cbCenterCardData) 
	-- //排列扑克
	self:SortCardList(cbTempCardData,#cbTempCardData);
	-- -- //组合算法
	for  i=1,3 do
		for  j= i+1,4 do
			for  k = j+1,5 do
				for  l =k+1,6 do
                    for  m=l+1,7 do
                        local cbTempLastCardData={}
						-- //获取数据
						cbTempLastCardData[1]=cbTempCardData[i];
						cbTempLastCardData[2]=cbTempCardData[j];
						cbTempLastCardData[3]=cbTempCardData[k];
						cbTempLastCardData[4]=cbTempCardData[l];
						cbTempLastCardData[5]=cbTempCardData[m];
						-- //获取牌型
                        -- //牌型大小
                        if #cbLastCardData>0 then
                            if(self:CompareCard(cbLastCardData,cbTempLastCardData)==1)then
                                cbLastCardData=	mytools.clone(cbTempLastCardData)
                            end
                        else
                            cbLastCardData=	mytools.clone(cbTempLastCardData)
                        end
					end
				end
			end
		end
    end
    
	return cbLastCardData
end

以前也有发现过有人写出了5重循环的算法,不过不是自己的项目不好挑刺。这其实也是新手,或者是10年重复了一年经验的10年老手也会经常写出这种代码,一点都不出奇,如果没受过基础算法训练,或者见过类似写法的代码,还真有点难度。

      上面的算法有几个毛病,超过3重的循环是大忌,第二如果有需求是10个里面选择7个呢?还得改代码。如果是100选5呢?你会发现这算法已经改不下去了?要改动的算法稳定性就会变差!真正好用的算法,应该是通用的,通过传参就能达到需求应变的,这样才是合格的算法。

        写棋牌算法,我可以说是驾轻就熟,我尝试改写一下,我要以两种方式实现它,一种是递归,一种是非递归,且看非递归算法

-- //最大牌型
function GameLogic:FiveFromSeven2( cbHandCardData,  cbCenterCardData,cbCardCount)
	-- //临时变量
    local cbTempCardData={}
    local cbLastCardData={}
	-- //拷贝数据s
	cbTempCardData=mytools.clone(cbHandCardData) 
	self:ArrayConcat(cbTempCardData,cbCenterCardData) 
	-- //排列扑克
	self:SortCardList(cbTempCardData,#cbTempCardData);

        local cbIndex = {}
        for j = 1, cbCardCount do
            table.insert(cbIndex,j)
        end
        while(true) do
            local cbTempLastCardData={}
            -- //获取数据
            for j = 1, #cbIndex do
                table.insert(cbTempLastCardData,cbTempCardData[cbIndex[j]])
            end
            -- //获取牌型
            -- //牌型大小
            if #cbLastCardData>0 then
                if(self:CompareCard(cbLastCardData,cbTempLastCardData)==1)then
                    cbLastCardData=	mytools.clone(cbTempLastCardData)
                end
            else
                cbLastCardData=	mytools.clone(cbTempLastCardData)
            end

            if (cbIndex[cbCardCount] == #cbTempCardData) then
                local index = cbCardCount
                for i = cbCardCount, 2, -1 do
                    if ((cbIndex[i - 1] + 1) ~= cbIndex[i]) then
                        local cbNewIndex = cbIndex[i - 1]
                        for j = (i - 1), cbCardCount do
                            cbIndex[j] = cbNewIndex + j - i + 2
                        end
                        break
                    end
                    index = index - 1
                end
                if (index == 1) then
                    break
                end
            else
                cbIndex[cbCardCount] = cbIndex[cbCardCount] + 1
            end
        end
    
	return cbLastCardData
end

且看关键算法部分,代码行数大家相当,但是这种写法,在需要从100个选5时,也不需要改代码了,就只需要你传递参数来确定就可以了,可逻辑上不说明,一般人还是不太好理解,不像第一种直观一些。它的逻辑是这样的,七张牌

{0x32, 0x33,0x22, 0x12, 0x13, 0x23, 0x37}

循环1:【1】【2】【3】【4】【5】

循环2:【1】【2】【3】【4】【6】

循环3:【1】【2】【3】【4】【7】

循环4:【1】【2】【3】【5】【6】

循环5:【1】【2】【3】【5】【7】

。。。。。。。

相信你运行调试一下就明白了。

第二种递归算法:

function  GameLogic:Combination( cbCombineCardData,  cbResComLen,   cbResultCardData, cbSrcCardData ,  cbCombineLen1,  cbSrcLen,  cbCombineLen2)


	if( cbResComLen == cbCombineLen2 )then
        local cbResCardLen=#cbResultCardData+1
        cbResultCardData[cbResCardLen]={}
        for i = 1, cbResComLen do
            cbResultCardData[cbResCardLen][i]= cbCombineCardData[i]
        end
	else
	
		if(cbCombineLen1 >= 1 and cbSrcLen > 0 and (cbSrcLen+1) >= 0 )then
			cbCombineCardData[cbCombineLen2-cbCombineLen1+1] =  cbSrcCardData[cbSrcLen];
			cbResComLen =cbResComLen+1
			self:Combination(cbCombineCardData,cbResComLen, cbResultCardData, cbSrcCardData,cbCombineLen1-1, cbSrcLen-1, cbCombineLen2);

			cbResComLen=cbResComLen-1
			self:Combination(cbCombineCardData,cbResComLen, cbResultCardData, cbSrcCardData,cbCombineLen1, cbSrcLen-1, cbCombineLen2);
        end
	end
end

-- //最大牌型
function GameLogic:FiveFromSeven3( cbHandCardData,  cbCenterCardData,cbCardCount)
	-- //临时变量
    local cbTempCardData={}
    local cbLastCardData={}
	-- //拷贝数据s
	cbTempCardData=mytools.clone(cbHandCardData) 
	self:ArrayConcat(cbTempCardData,cbCenterCardData) 
	-- //排列扑克
	self:SortCardList(cbTempCardData,#cbTempCardData);
     -- //对牌组合
     local cbComCard = {}
     local cbComResCard = {}
     local cbComResLen = 0
    self:Combination( cbComCard, 0, cbComResCard, cbTempCardData,cbCardCount,#cbTempCardData,cbCardCount)
    for i = 1, #cbComResCard do
           -- //保存对牌
           -- //牌型大小
           if #cbLastCardData>0 then
               if(self:CompareCard(cbLastCardData,cbComResCard[i])==1)then
                   cbLastCardData=	mytools.clone(cbComResCard[i])
               end
           else
               cbLastCardData=	mytools.clone(cbComResCard[i])
           end
        
    end
	return cbLastCardData
end

这种逻辑上也直观简单,最后得出结果集时,再进行比较,这种写法有点损耗效率,当你真正有性能瓶颈时,可以把结果放在递归里面去处理。这样的写法,在有需求100选5时,也不需要改动代码,也能达到应用上的需求,但他有一个缺点,就是如果是数目巨大,递归过深,程序会崩掉。这个也是它的局限性,所以用这个你要预见到你的项目需求。

那么这三种的效率pk呢?在7选5的情况下,我们看看运行结果

第一种效率相对低一些,第二种和第三种不相上下。

       到了这里大家都明白了,第一种你没有理由选择它,但是它又是程序员最容易写出来的东西,有时为了项目稳定,它会长期存在。我更加倾向于使用第二种,因为第二种不存在递归的弱点,有的人会选择第三种,因为有时递归在表达一些东西时代码会更加简洁,看各人思维了。

还有更加好写法的欢迎拍砖,秋:460000713

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值