执行Calc方法
执行结果:
执行结果:
public void Calc(string pokerData) { .....加上上面的..... TMeldRecord[][] resultMelds = CalcMinMeldsCount(allRecordsTmp, countArray); Debug.Log("总共获取到了" + resultMelds.Length + " 副手牌"); foreach (var meldsItem in resultMelds) { int count = 0; foreach (var item in meldsItem) count += item.straightCount * item.multiple; Debug.Log("************** 最小手数:" + (meldsItem.Length + pokerData.Length - count) + " 单牌数:" + (pokerData.Length - count)); foreach (var item in meldsItem) Debug.Log(item); } } /// <summary> /// 该方法用于 排序全部的组合,并获得排列中最短的那些排列 /// 返回值用数组,是因为 存在 最小手数相同的多个组合 /// </summary> /// <param name="allRecords">组合内容</param> /// <param name="countArray">每张牌的数量</param> /// <returns></returns> public static TMeldRecord[][] CalcMinMeldsCount(List<TMeldRecord> allRecords, int[] countArray) { //每次获取,需要重置 safeIndex = 5000; List<IntegerList> minRecords = new List<IntegerList>(); int minMeldCount = int.MaxValue; CalcSelfLoop(0, ref minMeldCount, countArray, allRecords, new IntegerList(), minRecords); TMeldRecord[][] meldRecords = new TMeldRecord[minRecords.Count][]; for (int i = 0; i < minRecords.Count; i++) { meldRecords[i] = new TMeldRecord[minRecords[i].Count]; int meldIdx = 0; for (int j = 0; j < allRecords.Count; j++) { if (minRecords[i].Contain(j)) { meldRecords[i][meldIdx++] = allRecords[j]; } } } return meldRecords; } //防止计算量过大,造成客户端严重卡顿,故设定一个数量,只能访问该方法指定次数,到达指定次数,返回相应结果 private static int safeIndex = 5000; public static void CalcSelfLoop(int currentIndex, ref int minMeldCount, int[] countArray, List<TMeldRecord> allRecords, IntegerList cacheRecords, List<IntegerList> resultRecords) { if (safeIndex-- <= 0) { if (safeIndex == -1) Debug.Log("超过预期计算量,为防止卡顿,开始强制结束"); return; } //遍历完毕 if (currentIndex >= allRecords.Count) { //判断结束,缓存数据 resultRecords.Add(cacheRecords); return; } //判断剩余各个组合是否可以 组合在一起 for (int i = currentIndex; i < allRecords.Count; i++) { TMeldRecord curRecord = allRecords[currentIndex]; //每局开始之前,另存一份剩余数量,以防被别的组合修改 int[] restArrayCount = new int[countArray.Length]; Array.Copy(countArray, restArrayCount, countArray.Length); //组合也复制一份,防止被修改 IntegerList cacheRecordNew = cacheRecords; //判断剩余数量是否足够 bool isEnough = true; for (int tmpIdx = 0; tmpIdx < curRecord.straightCount; tmpIdx++) { if (restArrayCount[(int)curRecord.startValue + tmpIdx] < curRecord.multiple) { isEnough = false; break; } } if (isEnough) { //足够,则减去相应数量 for (int tmpIdx = 0; tmpIdx < curRecord.straightCount; tmpIdx++) { restArrayCount[(int)curRecord.startValue + tmpIdx] -= curRecord.multiple; } //保存该组合索引 cacheRecordNew.Add(currentIndex); } //对下一组合进行判断 CalcSelfLoop(++currentIndex, ref minMeldCount, restArrayCount, allRecords, cacheRecordNew, resultRecords); } }执行结果(篇幅有限,只截取了最上面的日志和最下面的日志): 现在获取到了256幅手牌列表,中间就包含了 最小出牌次数的列表,现在就需要在获取的时候加入判断,如果比我已获取到的最大手数大的话,就舍弃前面全部获取到的,目的就是只保存出牌手数最少的那些列表。 上面我们看到了有大量重复的列表,我们也需要剔除,剔除方式:判断全部的索引值是否相同,有一个技巧就是用到 这两个运算符‘|’和‘&’加快计算效率。看上面代码,可以知道有个 IntegerList 类,我没有发源码出来,其实这是一个结构体,而且是一个只保存整型的结构体,在这个结构体中,保存了一个列表全部的组合索引值。 这个结构体的最大值是long(最大值为263)型,通过2索引值,进行保存,所以如索引值达到了63(组合数达到64),就会超过long的最大值,所以只要牌的张数不是太多,long大部分时候是满足需求的,如果确实组合数超过了64,那么就要把IntegerList换成别的方式了。
public struct IntegerList { private long _allValue; private const int Base = 2; public int Count { get; private set; } public void Add(int value) { Count++; _allValue |= getValue(value); } public bool Contain(int value) { return (_allValue & getValue(value)) == getValue(value); } private long getValue(int value) { return (long)Math.Pow(Base, value); } public long Value { get { return _allValue; } } }只保存最小组合数 和 去除重复的组合数的方式如下:
public static void CalcSelfLoop(int currentIndex, ref int minMeldCount, int[] countArray, List<TMeldRecord> allRecords, IntegerList cacheRecords, List<IntegerList> resultRecords) { ......忽略上面.... //遍历完毕 if (currentIndex >= allRecords.Count) { //该组合列表中剩余单张数量 int curSingleCount = GetTotalCount(countArray); if (minMeldCount >= curSingleCount + cacheRecords.Count) { //如果当前的组合数小于前面的组合的最小的组合数,则情况前面的组合数 if (curSingleCount + cacheRecords.Count < minMeldCount) resultRecords.Clear(); minMeldCount = curSingleCount + cacheRecords.Count; //重复组合 不加入列表 for (int i = 0; i < resultRecords.Count; i++) { if (cacheRecords.Value == resultRecords[i].Value) return; } //判断结束,缓存数据 resultRecords.Add(cacheRecords); } return; } .....忽略下面..... } public static int GetTotalCount(int[] countArray) { if (countArray == null) return 0; int result = 0; for (int i = 0; i < countArray.Length; i++) result += countArray[i]; return result; }执行结果: 现在就职剩下了3副手牌了,且最小手数都是4,也就是说3445566688的最小出牌手数列表有:
3456、456、88,外加单张6345、456、66、88445566、88,外加单张3、6PS:上面有个safeIndex,这里需要讲一下,当组合太多时,全部计算完不太现实,也没必要,因为手数最少的都是在前面,后面留下的都是单张了, 所以safeIndex就是为了避免计算太多而导致的卡顿而设计的,目前设定为5000,用一台i7 CPU的笔记本,递归五千次 是需要30ms左右,基本不会卡顿。