最近在做德州扑克项目,写到从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