How to calculate the best price
It look like this algorithms will be run more than one time.
So I feel free to use an eavy pre-compute step (sorry if it is not the case).
Computing the best price, is computing the group of combos than can by apply.
So I'm only printing combos that can be apply.
working data types
define thoses type
in sml notation
type index = (productId, tag) Hashtbl.t
and tag = {
combos : combo list [default is empty list]
subIndex : index [default is empty hash]
}
and combo = {
comboId : int;
comboName : string
constituentProducts : list product;
comboPrice : int
}
and product = {
productId : int;
productName : string,
price : int (*In fact as I understand the problem this price don't change any thing. *)
}
pre-compute
The precompute step is index building.
Sort all products in your combos by the productsId. vital
let insertIntoIndex(Index index, Combo combo, int level = 0) ->
assert level < combo.constituentProducts.length;
tag entry = index[combo.constituentProducts[level].productId];
if entry is null/notFound then { entry = new tag(); }
if level + 1 == combo.constituentProducts.length
then
{
entry.combos.add combo;
}
else
{
if null/notFound = entry.subIndex.get(combo.constituentProducts[level])
then entry.subIndex.add (combo.constituentProducts[level].productId) (new tag());
insertIntoIndex(entry.subIndex, combo, level+1)
}
iter insertIntoIndex over all your combos, for the same index.
As you can see this index is a form of tree.
Each node can math a combo and be a part of a greater combo.
computation
Put all the basket products into an array (with repetition if need);
Sort that array by productId in the same order as use for sorting combos. vital
for(int position = 0; position < array.length ; position++)
scan(index, product, position);
let scan(index,product, position) ->
entry = index.get array[position];
if entry != null/notfound
then
{
iter print entry.combos; (* or build a set of result *)
foreach(product key : entry.subIndex.keys)
{
positionOfFoundKey = array.find(from = position, to = length-1 , look = key )
if (positionOfFoundKey != -1) (* -1 for not found / null is the find function *)
scan(entry.subIndex[key], array[positionOfFoundKey], positionOfFoundKey)
}
}
as product are sorted, there will be no combos counts twice, unless the combos is really present.
You can improve the scan function by adding a bag to found the matching product, remove from the bag the product that have been scan.
Complexity of the search :
n x scan
Complexity of scan :
size of bucket x maximum size of combo x number of combo
I believe this is just an upper bound that should never append.
I don't know if it is the best, but it look fast enouph to me as I don't know what your inputs look like.