分組批銷單模塊業務,內部算法
背景:獲取合並批銷單數據,對數據進行分組合並展示,每組數據12個,可以擴展2個位置,用於業務人員篩選產品發往快遞公司
規則:①相同供應商相同發貨時間一起展示,每組不能超過規定數據
②相同供應商相同時間的數據如果被分到2組,那麽兩組數據需要相連
③必須同一發貨時間的在一組中;最後每一組數據需要勻稱,不能呈現出特別多與特別少。
④以發貨時間為準,相同發貨時間的盡量放在一起,並且以發貨時間進行排序
⑤最後零碎發貨時間的放在一組
⑥最後如果零碎中有與大組中發貨時間相同的則放在一起,但每組數據不能超過15
思路:創建一個大list保存已經完美拍好的list
一、拿100%滿足條件
①先拿出100%符合條件,拿出多少條根據算法計算出來,放到大List中
②拿出100%滿足的剩余的放在一組中
二、填補
①取出大List中不滿數組長度的List,對其進行填充;填充時不能把同一供應商同一時間的拆開;最大長度12+2;這塊是另外一個算法,如何匹配到完美的填補
三、處理大量剩余
①剩余的處理與第二部類似,只不過,第二部是某單個數據在一個大的集合中查找,而這步則把集合中每一個小集合匹配成完美的組放入大Lst中
②同供應商同時間的必須放在一起,每組數據相加大於最大長度且小於最大長度+擴展長度 ---------算法
③由於業務數據原因有很大概率處理到最後會剩余幾條數據如何處理?
四、微調、剩余歸類---這裏是另外一個算法
①第三步中剩下的數據中如果有大於3的則獨立成一組,因為上步中已經把所有數據都匹配的近乎完美所以大於3肯定是在數組中放不下的
②把所有微小的顆粒放在同一組,裝進大lst中
五、按大組中每個list的第一條數據的日期排序並按先後順序給予組號
算法:
一、通過取整與取余對數組長度進行配比得到最優的情況
首先考慮到不滿一組時的數組長度處理,這關系到數據遍歷時拿數據
計算最優配比:比如25條數據,數組長度12擴展位2--取整2余1,此時如果我一組12條數據會落單一條這是非常不希望看到的結果
此時如果每組13條數據就可以得到最好的結果
1 /// <summary> 2 /// 得到真實數組長度的方法 3 /// </summary> 4 /// <param name="arrayLength">數組長度</param> 5 /// <param name="maxLen">最大長度</param> 6 /// <param name="extSeat">擴展長度</param> 7 /// <param name="arr">數組個數</param> 8 /// <param name="rem">數組剩余</param> 9 /// <param name="trueArrLength">真實數組長度</param> 10 /// <param name="isExt"></param> 11 private void trueArrayProperty(int arrayLength, int maxLen, int extSeat, ref int arr, ref int rem, ref int trueArrLength) 12 { 13 //真實組數 14 if (arrayLength < maxLen) 15 { 16 arr = 1; 17 rem = arrayLength; 18 } 19 else 20 { 21 arr = arrayLength / maxLen; 22 //余數 23 rem = arrayLength % maxLen; 24 } 25 //真實的數組長度 26 trueArrLength = maxLen; 27 for (int ai = 1; ai <= extSeat; ai++) 28 { 29 int a1 = arr * ai; 30 if (rem == 0) { break; } 31 if (a1 > rem) 32 { 33 trueArrLength = trueArrLength + ai; 34 arr = arrayLength / trueArrLength; 35 //余數 36 rem = arrayLength % trueArrLength; 37 } 38 } 39 }
算法二:摘數據,采用遞歸也是沒轍中想到的轍,如果不使用遞歸,從大集合中遍歷大集合會造成引用類型循環引用的問題
①將源數據按供應商和時間分組,拿到分組後結果集就可以拿到每組有多少條數據;
②數組長度按每組需要大於等於Maxlen且小於等MaxLen+extSeat
③while(boolean)的用意在於可能不能一次性匹配上數據,可能需要多次疊加才可以得到滿意的數組
④由於業務數據原因和分組匹配規則最後一定會出現不可能全部把souceDatas吃完,所以就用了zOther存儲這些另類數據,保證退出
1 /// <summary> 2 /// 遞歸剩余數據 3 /// </summary> 4 /// <param name="sourceDatas">源數據</param> 5 /// <param name="resultDatas">大lst</param> 6 /// <param name="maxLen">最大長度</param> 7 /// <param name="extSeat">擴展位</param> 8 /// <param name="zOther">雜項處理</param> 9 private void RecursionDatas(List<ResponseSaleOrderJoinModel> sourceDatas, List<List<ResponseSaleOrderJoinModel>> resultDatas, 10 int maxLen, int extSeat, List<ResponseSaleOrderJoinModel> zOther) 11 { 12 if (sourceDatas.Count == 0) 13 { 14 return; 15 } 16 List<ResponseSaleOrderJoinModel> curItem = new List<ResponseSaleOrderJoinModel>(); 17 var dataGroup = sourceDatas.GroupBy(e => new { e.saleOrderDate, e.ssellerID }).OrderByDescending(e => e.Count()); 18 var groupFirst = dataGroup.First(); 19 curItem = sourceDatas.Where(e => e.saleOrderDate == groupFirst.Key.saleOrderDate && e.ssellerID == groupFirst.Key.ssellerID).ToList(); 20 sourceDatas = sourceDatas.Except(curItem).ToList(); 21 var group = sourceDatas.Where(e => e.saleOrderDate == curItem.First().saleOrderDate).GroupBy(e => new { e.saleOrderDate, e.ssellerID }); 22 while (curItem != null) 23 { 24 foreach (var item in group) 25 { 26 //只匹配正好的 27 if (item.Count() >= (maxLen - curItem.Count) && item.Count() <= (maxLen + extSeat - curItem.Count)) 28 { 29 curItem.AddRange(item); 30 resultDatas.Add(curItem); 31 sourceDatas = sourceDatas.Except(curItem).ToList(); 32 curItem = new List<ResponseSaleOrderJoinModel>(); 33 break; 34 } 35 } 36 //一輪過後,如果沒匹配上,則可能過大而剩余中沒有小數 37 if (curItem.Count >= maxLen) 38 { 39 resultDatas.Add(curItem); 40 sourceDatas = sourceDatas.Except(curItem).ToList(); 41 curItem = new List<ResponseSaleOrderJoinModel>(); 42 } 43 else if (curItem.Count < maxLen && curItem.Count > 0) 44 { 45 //一輪過後,如果沒匹配上,則可能數據量過小而剩余中沒有能夠匹配上的 46 var dataFirst = group.FirstOrDefault(e=>e.Key.saleOrderDate==curItem.First().saleOrderDate); 47 if (dataFirst == null) 48 {//如果沒有找到同類,則有可能是與其它時間相同,也有可能與其它不同。如何處理 49 zOther.AddRange(curItem); 50 curItem = new List<ResponseSaleOrderJoinModel>(); 51 break; 52 } 53 curItem.AddRange(dataFirst); 54 sourceDatas = sourceDatas.Except(dataFirst).ToList(); 55 } 56 else 57 { 58 break; 59 } 60 group = sourceDatas.Where(e => e.saleOrderDate == curItem.First().saleOrderDate).GroupBy(e => new { e.saleOrderDate, e.ssellerID }).ToList(); 61 } 62 RecursionDatas(sourceDatas, resultDatas, maxLen, extSeat,zOther); 63 }
算法三:同樣是集合找集合的問題,使用了降級處理思路
①在進入遞歸前,我先把zOther過濾了一次,按同商同時分組後,數據量大於3的獨立出一組,以降低遞歸復雜度
②這時數據裏會出現2種情況,第一種是與大list種的時間相同,這類數據是可以歸並到list中的,而剩余的則是與大list中其他數組數據時間不同的,這類數據需要都放在一組中
1 private void RecursionOtherDatas(List<ResponseSaleOrderJoinModel> otherDatas, List<List<ResponseSaleOrderJoinModel>> resultDatas, 2 List<ResponseSaleOrderJoinModel> zz) { 3 if (otherDatas.Count == 0) { 4 return; 5 } 6 var otherGroup = otherDatas.GroupBy(e => new { e.saleOrderDate, e.ssellerID }); 7 8 var groupFirst = otherGroup.First(); 9 var data = otherDatas.Where(e => e.saleOrderDate == groupFirst.Key.saleOrderDate && e.ssellerID == groupFirst.Key.ssellerID).ToList(); 10 foreach (var item in resultDatas.OrderBy(e=>e.Count)) 11 { 12 if (item.Count == 15) { break; } 13 if (item.Any(e => e.saleOrderDate == groupFirst.Key.saleOrderDate)) 14 { 15 if (item.Count + data.Count <= 15) 16 { 17 item.AddRange(data); 18 otherDatas = otherDatas.Except(data).ToList(); 19 groupFirst = null; 20 break; 21 } 22 } 23 } 24 if (groupFirst != null) { 25 zz.AddRange(data); 26 otherDatas = otherDatas.Except(data).ToList(); 27 } 28 RecursionOtherDatas(otherDatas, resultDatas, zz); 29 }
後記:整個業務模塊用時將近一個月,但這套代碼只寫了4天。前邊大部分時間由於產品經理的需求一直在變每當我開發出一套算法他就變了掛,所以前邊一直在改改改,索性我比較機智,並不是在代碼的基礎上改,
而是每次都新建一個方法。後來改煩了,索性直接越級找大boss問清楚了真正的需求,最後落定出這套算法。
這次的教訓不管是大功能還是小功能都應該落實到文檔上,把自己理解的東西寫清楚,不管是自己忘了查看也好,未來扯皮也擺還都是有好處的
分組批銷單模塊業務,內部算法