我有一个.net核心Web应用程序,可供我们的员工根据输入的余额将产品与客户匹配。产品有5种。产品1必须作为主要产品包含在每个捆绑中。根据该值,其他4种产品将尽可能多地捆绑在一起。但是产品1也有多个不同的价格点。因此,可以说客户可以负担得起产品1的最高价格,但只能负担2个附加组件。我需要它下降到产品1的下一个值,并添加每个插件等。有人可以给我的任何指导都会真正帮助您。
答案 0 :(得分:0)
假设您有一个Products
表,其中有Type
和Price
。
您可以将Products
放入Type
1的其余部分中,并使用其他设备:
var Prod1 = Products.Where(p => p.Type == 1);
var ProdNot1 = Products.Where(p => p.Type != 1);
使用一些扩展方法,您可以创建非类型1产品的所有可能组合(如果从数据库中提取数据,则必须在内存中执行此操作):
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k) {
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] { e }).Concat(c)));
}
public static IEnumerable<IEnumerable<T>> AllCombinations<T>(this IEnumerable<T> elements) => Enumerable.Range(1, elements.Count()).SelectMany(k => elements.Combinations(k));
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences) {
var emptyProduct = Enumerable.Empty<T>().AsSingleton();
return sequences.Aggregate(
emptyProduct,
(accumulator, sequence) =>
from accseq in accumulator
from item in sequence
select accseq.Append(item));
}
public static IEnumerable<IEnumerable<T>> CartesianProductWithEmpties<T>(this IEnumerable<IEnumerable<T>> sequences) =>
sequences.AllCombinations().SelectMany(s => s.CartesianProduct());
现在您可以计算AddOnCombos
:
var AddOnCombos = ProdNot1.GroupBy(p => p.Type)
.CartesianProductWithEmpties()
.Select(tpc => new { Products = tpc, Price = tpc.Sum(p => p.Price), Types = tpc.Sum(p => p.Type) });
使用价格最低的Type 1产品:
var minPriceProd1 = Prod1.MinBy(p => p.Price);
您可以使用CustBal
来估算附加产品的预算:
var CustBal = 375;
var nonProd1BalGuess = CustBal - minPriceProd1.Price;
然后,您可以猜测哪个附加组合将起作用,最大化类型数,优先处理较低类型并以最低价格购买
var nonProd1Guess = AddOnCombos.Where(aoc => aoc.Price <= nonProd1BalGuess)
.OrderByDescending(aoc => aoc.Products.Count())
.ThenBy(aoc => aoc.Types)
.ThenBy(aoc => aoc.Price)
.FirstOrDefault();
使用附加猜测,您可以猜测获得价格较高的Type 1产品的剩余预算。
var allProdBalGuess = nonProd1BalGuess - (nonProd1Guess?.Price ?? 0);
然后计算类型1的最终产品以及附加产品剩余的数量:
var finalProd1 = Prod1.Where(p => p.Price <= minPriceProd1.Price + allProdBalGuess).OrderByDescending(p => p.Price).First();
var nonProd1Bal = CustBal - finalProd1.Price;
然后找到最佳的附加组件组合:
var nonProd1 = AddOnCombos.Where(aoc => aoc.Price <= nonProd1Bal)
.OrderByDescending(aoc => aoc.Products.Count())
.ThenBy(aoc => aoc.Types)
.ThenByDescending(aoc => aoc.Price)
.ThenByDescending(aoc => Enumerable.Range(2, 5).Select(t => aoc.Products.FirstOrDefault(p => p.Type == t)?.Price ?? 0), new SeqCompare<double>())
.FirstOrDefault();
要优先考虑在较低的Type
产品上花费更多的预算,您需要一些扩展来比较价格顺序:
public static class IEnumerableExt {
public static int SeqCompareTo<T>(this IEnumerable<T> s1, IEnumerable<T> s2) => SequenceCompare(s1, s2);
public static int SequenceCompare<T>(IEnumerable<T> source1, IEnumerable<T> source2) {
// You could add an overload with this as a parameter
IComparer<T> elementComparer = Comparer<T>.Default;
using (IEnumerator<T> iterator1 = source1.GetEnumerator())
using (IEnumerator<T> iterator2 = source2.GetEnumerator()) {
while (true) {
bool next1 = iterator1.MoveNext();
bool next2 = iterator2.MoveNext();
if (!next1 && !next2) // Both sequences finished
return 0;
if (!next1) // Only the first sequence has finished
return -1;
if (!next2) // Only the second sequence has finished
return 1;
// Both are still going, compare current elements
int comparison = elementComparer.Compare(iterator1.Current,
iterator2.Current);
// If elements are non-equal, we're done
if (comparison != 0)
return comparison;
}
}
}
}
public class SeqCompare<T> : IComparer<IEnumerable<T>> {
public int Compare(IEnumerable<T> x, IEnumerable<T> y) => x.SeqCompareTo(y);
}