/// <summary> /// /// </summary> /// <param name="dataWrapper"></param> /// <param name="optimizerLimit">Maximum number of different combinations to try. Zero: no limit.</param> /// <param name="progressChanged"></param> /// <param name="operation"></param> /// <returns></returns> public static IEnumerable <ResultData> GetResults(DataWrapper dataWrapper, int optimizerLimit, System.Action <string> progressChanged, string operation) { int cntMax = dataWrapper.Balance.Count; int cnt = 0; foreach (KeyValuePair <string, int> item in dataWrapper.Balance) { string product = item.Key; progressChanged?.Invoke($"{operation} {++cnt}/{cntMax}"); int balance = item.Value; int shipped = dataWrapper.SumFactoryShipments.ContainsKey(product) ? dataWrapper.SumFactoryShipments[product] : 0; if (shipped == 0) { //None of the orders can be fulfilled, but there is nothing to store yield return(new ResultData(product, shipped, ResultDecision.Create(dataWrapper.GetCustomerOrdersByItemName(product), fulfill: false))); } else if (balance == 0) { //All orders fulfill, nothing to store yield return(new ResultData(product, shipped, ResultDecision.Create(dataWrapper.GetCustomerOrdersByItemName(product), fulfill: true))); } else if (balance > 0) { //All orders fulfill, there are some product item from the factory shipments to store yield return(new ResultData(product, shipped, ResultDecision.Create(dataWrapper.GetCustomerOrdersByItemName(product), fulfill: true))); } else if (balance < 0) { //Some orders may be fulfilled, but not all. Optimization needed. Some product items from the factory shipments might be to store. IEnumerable <ResultDecision> decisionsComplex = null; double efficiencyComplex = 0; IEnumerable <ResultDecision> decisionsSimple = ResolverSimple.Resolve(shipped, dataWrapper.GetCustomerOrdersByItemName(product), out double efficiencySimple); if (efficiencySimple < 1) { decisionsComplex = ResolverComplex.Resolve(optimizerLimit, shipped, dataWrapper.GetCustomerOrdersByItemName(product).ToArray(), efficiencySimple, out efficiencyComplex); } if (efficiencyComplex > efficiencySimple) { yield return(new ResultData(product, shipped, decisionsComplex)); } else { yield return(new ResultData(product, shipped, decisionsSimple)); } } } }
/// <summary> /// Simple algorithm to solve of 01-Knapsack problem /// </summary> /// <param name="shipped"></param> /// <param name="orders"></param> /// <param name="efficiency"></param> /// <returns></returns> /// <remarks>Works on sorted "weights", not recursive, only checks to swap the last added element in the "bag".</remarks> public static IEnumerable <ResultDecision> Resolve(int shipped, IEnumerable <CustomerOrder> orders, out double efficiency) { HashSet <ResultDecision> results = new HashSet <ResultDecision>(); int total = 0; ResultDecision last = null; int lastTotal = 0; foreach (CustomerOrder item in orders) { if (total + item.Quantity <= shipped) { ResultDecision rd = new ResultDecision(item, true); results.Add(rd); lastTotal = total; total += item.Quantity; last = rd; } else if (last != null && lastTotal + item.Quantity <= shipped && lastTotal + item.Quantity > total) { results.Remove(last); results.Add(new ResultDecision(last.CustomerOrder, false)); ResultDecision rd = new ResultDecision(item, true); results.Add(rd); total = lastTotal + item.Quantity; lastTotal = total; last = rd; } else { results.Add(new ResultDecision(item, false)); } } efficiency = total / (double)shipped; return(results); }