The number of combinations in the configurator

I was asked to program the procedure to determine the number of possible combinations in the product configurator.

The configurator is really simple. Despite the fact that it has more features than this, it can be modeled as several “radio groups” (for example, user interface control), where you need to select one of n parameters.

The only restrictions that can be used are rules that say that if one of the parameters is selected, the other parameter cannot be selected.

So, I want to calculate the number of different products that can be customized, given the set of option groups and restrictions.

I made a naive approach to solve this problem using the inclusion exclusion principle . However, as far as I can see, any algorithm based on this method should work in O (2 ^ n), which will not work. Of course, there are several possible optimizations that should provide decent battery life, but there are still scenarios of the worst-case scenario that are easily constructed.

This is pretty much where I am now. Any suggestions?

Update

I understand that I have not explained how the rules are applied well enough.

There are several groups with options. In each group, one and only one parameter must be selected. A group can have one or more parameters.

There is only one type of restriction. If option A in any group is selected, option B in any other group cannot be selected. There can be any number of restrictions, there are no restrictions, how many restrictions / rules apply to the parameter group or the parameter itself.

So an example would be:

Group 1:
x1 x2 x3 x4 x5

Group 2:
y1 y2 y3

Group 3:
z1 z2 z3 z4

Limitations:
x1 ↔ y2 *
x1 ↔ z4
y2 ↔ z2

* If option x1 is selected in group 1, option y2 in group 2 cannot be selected.

Using the inclusion exception, I would calculate the number of combinations as

Combinations = C no rules - C r [1] - C r [2] - C r [3] + C r [1,2] + C r [1,3] + C r [2,3] - C < sub> tsub> [1,2,3]

Where

C no rules = 5 * 3 * 4

C r [a, b, c] = Number of combinations that violates rules a, b, and c.

The method, unfortunately, requires 2 ^ | rules | calculations.

+4
source share
11 answers

Ok, I can't get around 2 ^ N, but I can reduce the sample set. To do this, we compute "Constrained Constraints." Compost. A restriction is a restriction if, if all parameters on the left side are selected, then none of the options on the right side can be selected, but no other restrictions based on the parameters of the left side can be applied.

We need to compute the set of all possible composite constraints from the set of constraints. Although not necessary, we will “fix” the existing restrictions by replacing the left and right hands if the group of the right hand is smaller than the group of the left. This can reduce some of the tight constraints, although more efficient heuristics for exchange are possible.

We also need to calculate the “minimum set” of options that can be arbitrarily selected for each group. This minimum set is calculated by removing from the list of available options any parameters that appear on the left side of the configured restriction.

The algorithm follows, but I do not prove that it calculates CC correctly. I will prove that if this happens, then they can be used to calculate the number of possible combinations.

  • Correct the restrictions so that the left hand group is less than or equal to the right hand group.
  • Make restrictions:

    • Left Click Sort Restrictions
    • Consistently, for each constraint:

      • Fold the constraint with all the constraints that follow it with the same left hand, turning x1 <-> y1 , x1 <-> y2 ... x1 <-> yN into Set(x1) <-> Set(y1 ... yN)
      • Add the folded constraint to each constraint already folded if:
        • x1 is not in the right hand of an already folded constraint
        • x1 is not included in the same group of any element on the left
      • Add the folded constraint and all its compositions to the set of folded constraints
  • Calculate the minimum set by selecting all the parameters and deleting those that appear in the left hand of the fixed constraints.

Now you can calculate the number of combinations with the formula below. Let a CC call consist of a folded constraint. Then the number of combinations:

 C(Mininum Set) + CCC1 + ... + CCCn 

Where:

  • C (minimum set) - the number of possible combinations with a minimum set.
  • CCCx is the number of combinations, possibly taking the minimum set, replacing any groups for which there is an option in the left hand of CCx with this option, and then deleting any parameters on the right side of CCx.

Note that the expression is purely additive. This means that in order for this expression to produce the expected result, the following two conditions must be met:

  • There are no two terms that may contain the same combination.
  • All combinations must be subject to these conditions.
  • Invalid combinations may be obtained by any term.

For the first proof, note that there are no two different CCs with the same left hand. If two CCs have the same left hand but different right hands, this means that there is a restriction on the addition that should apply to one of the CCs, or an unacceptable restriction applied to the other.

Since there are no two CCs with the same left hand, and the minimum set does not contain the left hand of any CC by definition, any two CCs can be selected with at least one parameter that is selected for one, but not for the other. Therefore, no two CCs can give the same combination.

In the second proof, we note that the set CCs contains, by definition, all valid combinations for the options on the left.

Suppose that there is one combination that does not appear in either the minimum set or the CC set. If this combination does not contain a left parameter, then it should be a combination of the minimum set by definition. Therefore, it should contain options from the left hand.

Since the set of CCs contains all valid combinations for the left hand, there is one CC with the same left options. Therefore, this combination should have an option that is not included in any combination for this CC. But the only options not included in this CC are those that appear in the left hand for other CCs, and those that should be excluded from it by restrictions. Since this is not so, then this combination cannot exist.

For the third proof, we first consider the minimal set. The minimum set does not contain any parameters in the left hand of any group. Since all restrictions are between one left and one right parameter, no restriction applies to the minimum set.

Now consider the SS. By definition, CC has a valid combination of left options. Any option that is incompatible with the left hand should be displayed in the right hand, and any option from this right hand should be removed from the minimum set. Since there are no options on the minimum set, where it is incompatible with each other for starters, there can be an unsatisfied restriction in any combination on CC.

And this completes the proof.

Let's see how this applies with the example from the comments:

 G1: x1, x2, x3 G2: y1, y2 G3: z1, z2, z3 R1: x1 <-> y2 R2: x3 <-> y2 R3: y1 <-> z1 R4: y2 <-> z2 R5: y2 <-> z3 CC1: {x1} <-> {y2} CC2: {x3} <-> {y2} CC3: {y1} <-> {z1} CC4: {x1, y1} <-> {y2, z1} CC5: {x3, y1} <-> {y2, z1} CC6: {y2} <-> {z2, z3} 

Briefly think about non-list compound groups:

 R1&R2: {x1, x3} <-> {y2} -- not in the list because x1 and x3 belongs to the same group R1&R5: {x1, y2} <-> {y2} -- not in the list because the left hand of R2, y2 appears in the right hand of R1 

Now let's see what options are possible in each set:

 Minimum Set: (x2), (), (z1, z2, z3) CC1: (x1), (), (z1, z2, z3) -- replace G1 with x1, remove y2 from G2 CC2: (x3), (), (z1, z2, z3) -- replace G1 with x3, remove y2 from G2 CC3: (x2), (y1), (z2, z3) -- replace G2 with y1, remove z1 from G3 CC4: (x1), (y1), (z2, z3) -- replace G1 with x1, G2 with y1, remove y2 and z1 CC5: (x3), (y1), (z2, z3) -- replace G1 with x3, G2 with y1, remove y2 and z1 CC6: (x2), (y2), (z1) -- replace G2 with y2, remove z2 and z3 from G3 

Now add things up:

 C(Minimum Set) = 1 * 0 *3 = 0 CCC1 = 1 * 0 * 3 = 0 CCC2 = 1 * 0 * 3 = 0 CCC3 = 1 * 1 * 2 = 2 CCC4 = 1 * 1 * 2 = 2 CCC5 = 1 * 1 * 2 = 2 CCC6 = 1 * 1 * 1 = 1 C(Minimum Set) + CCC1 + CCC2 + CCC3 + CCC4 + CCC5 + CCC6 0 + 0 + 0 + 2 + 2 + 2 + 1 = 7 

I will add one more thought. Despite the fact that there are only 6 CCCs for 5 rules, which are much less than 32 otherwise expected 32, these CCCs are calculated with the worst time of the least time, i.e., for each rule you must compare and combine it with all created still CCC. You can think of them as binary numbers, where the bit is set if the rule is combined and not set if not.

However, incompatible combinations are immediately discarded, so that combining each new rule does not waste time on combinations that are already considered invalid. In addition, by sorting the rules before the start, consecutive rules in the same group can be dropped without testing for incompatibility with the correct data structures.

As this specific example shows, the average time can be much better than 2 ^ N.

Alternative Algorithms and Considerations

They talk about 2-SAT and 3-SAT. It seems to me that this is a 2-SAT problem, in the sense that every restriction a b is actually a sentence "! A ||! B". Thus, all restrictions together can simply be written as "(! X1 ||! Y2) && (! X1 ||! Z4) && (! Y2 & &! Z3)", etc. This means you can "solve" it in the sense that you can find out if there is a logical purpose for each option that changes this value. There is a linear algorithm for this Aspall, Plass and Tarjan, with a slide presentation here .

But knowing whether restrictions can be allowed or not is not specified . The number of ways to set all the parameters was set while maintaining the problem with 2-SAT.

Effective algorithms now exist for counting the number of ways to satisfy the 2-SAT problem. For example, this article presents an algorithm that runs at 1.2561 n . But even this will not help us, since we need to know which solutions should be able to calculate the number of combinations that satisfy this solution.

According to Wikipedia, this article has an algorithm that effectively lists all the solutions that we want. But if the calculation is already exponential, it will be listed. Better than 2 n is possible, but still exponentially.

If we list all the solutions to the 2-SAT problem, the number of combinations for each group will be set to 1 times the number of free options that are not displayed in any restriction, for each group for which there is no choice was chosen by the solution.

For example, taking the previous set of groups and restrictions. The 2-SAT problem, including mutual exclusion, is this:

(! x1 ||! y2) && (! x3 ||! y2) && (! y1 ||! z1) && (! y2 ||! z2) && (! y2 ||! z3) & & (! x1 | |! x3) && (! y1 ||! y2) && (! z1 ||! z2) && (! z1 ||! z3) && (! z2 ||! z3)

The first line is the five rules. The second line is the mutual exclusion of all parameters in the same group that appear in the restriction rules.

The solutions to these problems with 2-SAT are:

 x1 x3 y1 y2 z1 z2 z3 Combinations true false true false false true false 1 true false true false false false true 1 true false true false false false false 0 true false false false true false false 0 true false false false false true false 0 true false false false false false true 0 true false false false false false false 0 false true true false false true false 1 false true true false false false true 1 false true true false false false false 0 false true false false true false false 0 false true false false false true false 0 false true false false false false true 0 false true false false false false false 0 false false true false false true false 1 false false true false false false true 1 false false true false false false false 0 false false false true true false false 1 false false false true false false false 0 false false false false true false false 0 false false false false false true false 0 false false false false false false true 0 false false false false false false false 0 

In the first two solutions there are no groups without the selected option, therefore the number of combinations is 1. The third solution does not have the option selected for the G3 group, so we multiply 1 by 0. In the lines that start with "false false", no group was selected for the G1 group no options: x2. Therefore, we multiply 1 by 1 for them and by 0 if there is no option for G2 or G3 (in this case, the number of free options is 0).

Now the question is how to include one option that will be selected in each group, and still claim that it is a 2-SAT. The problem, as indicated, has two implicit restrictions: for each group, one and only one must be selected. These two restrictions can be written as:

x 1 || x 2 || x 3 (for group x with options x 1 .. x 3 )
(! x 1 ||! x 2 ) && & && & & (! x 1 ||! x 3 ) && & & (! x 2 ||! x 3 )

The later limitation is 2-SAT, the first of which is 3-SAT for any non-trivial case. Be that as it may, I do not apply the first restriction, but then the counter becomes 0. The counting algorithm should look like this:

  • For unlimited combinations, multiply the number of unlimited restrictions in each group by each other.
  • For combinations with full limitation, add the result of the following calculations:
    • For each solution, multiply the number of restrictions without restrictions in each group without the option judged to be “true” for each other.

So, for each group in which there is at least one parameter without restrictions, the choice is implicit and anonymous. For each group in which all parameters are part of some restriction, if no parameter has been selected, the count for this group becomes 0, and therefore the number of combinations for this solution also becomes 0.

This is similar to tricking the problem out of the> 2-SAT limit. In the end, if it was possible, then the 3-SAT problem could be solved simply by listing the solutions of the 2-SAT part of it, and then discarding all that do not satisfy its 3-SAT part. Alas, there is one fundamental difference that I can determine:

  • All predicates that are not resolved by the 2-SAT part of the problem do not contain any additional restrictions.

Given this rather restrictive restriction on offers, we can solve this problem by listing solutions to the explicit 2-SAT restrictions.

If someone wants to continue this, continue. I am satisfied with the improvement of the proposed solution 2 n .

+3
source

If you have groups of options N , each with parameters Xi ( 0<=i<N ), then

 X0*X1*...*X(N-1) 

Gives you the answer you want. In other words, multiply the size of each group of options.

+2
source

If you have parameters n with Ci possible values ​​for each parameter and m constraints, the upper bound on the number of configurations is as follows (ignoring the constraints).

 N0 = C1 * C2 * ... * Cn 

The only limitation of the form ci == x => cj != y prohibits the following number of configurations.

  N Dk = ------- Ci * Cj 

Therefore, the number of configurations is obtained by subtracting the forbidden configurations from the upper boundary, ignoring the restrictions.

 N = prod(Ci, i = 1...n) * (1 - sum(1 / (Cxj * Cyj), j = 1...m)) 

Here xj and yj are both parameter indices from the j constraint.

Example

 Parameters n = 4 Parameter 1 C1 = 4 0 1 2 3 Parameter 2 C2 = 3 4 5 6 Parameter 3 C3 = 2 XY Parameter 4 C4 = 3 7 8 9 Constraints m = 2 Constraint 1 c2 == 4 => c3 != X Constraint 2 c3 == X => c4 != 9 N0 = 4 * 3 * 2 * 3 = 72 D1 = 72 / (3 * 2) = 12 D2 = 72 / (2 * 3) = 12 N = 72 - (12 + 12) = 48 

UPDATE

I think that this is not entirely correct, because it does not take into account the dependencies of the constraints.

+1
source

There are no shortcuts for the general case. This is not as bad as you think. See Rethinking below.

Is 2 ^ n really that bad? How many exclusion rules are we talking about here? You really need to do this only once for each configurator, unless the set of rules / options is constantly changing and requires dynamic recounting. If there really is a huge number of rules, then I would not look for an exact solution - we will consider only kth order intersections and say that "the number of combinations is not less / more ...". There may be other sieve methods that allow you to quickly determine the boundaries of the response.

Also keep in mind: if you consider only those exceptions that you really need, then 2 ^ n is just the upper bound, and your actual number of calculations can be significantly less for any real scenarios. That is, if C [1,2] is equal to zero, then C [1,2, ...]. Consider this: for each constraint, combine sets of constraints if they share common parameters. It is clear that your real time will be determined by the size of the largest "cluster" (which, yes, can be greater than n).


Rethinking : C [x, y] will have a null value in most cases. A restriction may overlap only with other restrictions associated with another group. In other words, (x1 ↔ y1) can only overlap with (x1 ↔ z1) or (y1 ↔ z2) or something, and not (x1 ↔ y2). Similarly, the set of restrictions can overlap only with the new group: the combination (x1 ↔ y1) with (y1 ↔ z2) does not interact with (x3 ↔ z2) (the group x is already fixed at x1). You only need to consider inclusion / exclusion, in which each rule added to the combination adds a previously unaffected group to the mix. So you are actually O (2 G ), where G is the number of groups (and also, possibly, another estimate based on the size of the groups). Much more manageable!

+1
source

EDIT

This algorithm is incorrect. I presented an alternative answer in another post, which in the worst case is another 2 ^ N, but otherwise can give better results.

This works in the example chosen because y2 is part of the x1 exception set, and the first two constraints are based on x1. However, now I see what needs to be done. It is still close to 2 ^ N, but there are optimizations that can lead to significant gains.

To fix this algorithm, the composed rules should have the form set (ox) ↔ set (oy). To compose them, for each constraint using the left oX that you compose, you also make other compositions of them with each rule that you have already created, if oX is not part of the right side of the composed rule and is not a group as well and the left side of the group.

For completely independent constraints, this is 2 ^ N. Otherwise, you decrease N by doing:

  • unifying constraints with a common left
  • does not calculate combinations of rules that are mutually exclusive, it is divided into:
    • does not combine the rules for options in the same group
    • does not combine rules in which the left side of one is displayed on the right side of the other.

I don’t think the correction of this algorithm is worth it. This is a pretty tough memory, and it will have the same order as my alternative answer, which is much easier.

Edit end

Turn it around. How about this for the algorithm:

  • Correct the rules, if necessary, for the rule o1 <-> o2 , group(o1) < group(o2)
  • Calculate the "compiled" rules by collapsing all the rules oX <-> o? into one rule oX <-> Set(o?)
  • Compute a “clean” set of groups by removing the left option of each rule from them.
  • Compute alternative sets from a clean set, one for each rule made, replacing the left option group with the leftmost parameter and subtracting the right-hand parameters of the rule from other groups.
  • For each set of groups, calculate the number of combinations by multiplying the number of options in each group in the set.
  • Add all results from step 5.

See this at work:

 Group 1: x1 x2 x3 x4 x5 Group 2: y1 y2 y3 Group 3: z1 z2 z3 z4 Constraints (already fixed): x1 <-> y2 * x1 <-> z4 y2 <-> z2 Composed rules: x1 <-> (y2, z4) y2 <-> (z2) Clean set of groups: x2x3x4x5, y1y3, z1z2z3z4 Alternate sets: x1, y1y3, z1z2z3 (first composed rule) x2x3x4x5, y2, z1z3z4 (second composed rule) Totals: 4 * 2 * 4 = 32 1 * 2 * 3 = 6 4 * 1 * 3 = 12 Total: 50 

, , . , , - . :

 c(no rules) = 60 c1 => 4 c2 => 3 c3 => 5 c12 => 1 c13 => 1 c23 => 0 c123 => 0 c(no rules) - c1 - c2 - c3 + c12 + c13 + c23 - c123 = 50 

, . , , -O .

Scala:

 case class GroupOption(id: Int, option: Int) case class Group(id: Int, options: Set[Int]) case class Rule(op1: GroupOption, op2: GroupOption) case class ComposedRule(op: GroupOption, set: Set[GroupOption]) object ComputeCombinations { def fixRules(rules: Set[Rule]) = { rules map (rule => if (rule.op1.id > rule.op2.id) Rule(rule.op2, rule.op1) else rule) } def ruledOptions(id: Int, rules: Set[Rule]): Set[Int] = ( rules filter (rule => rule.op1.id == id) map (rule => rule.op1.option) ) def cleanseSet(groups: Set[Group], rules: Set[Rule]) = { groups map (group => Group(group.id, group.options -- ruledOptions(group.id, rules))) } def composeRules(rules: Set[Rule]): Set[ComposedRule] = Set( ( rules.toList sort (_.op1.id < _.op1.id) foldLeft (List[ComposedRule]()) ) { (list, rule) => list match { case ComposedRule(option, set) :: tail if option == rule.op1 => ComposedRule(option, set + rule.op2) :: tail case _ => ComposedRule(rule.op1, Set(rule.op2)) :: list }} : _* ) def subset(groups: Set[Group], composedRule: ComposedRule) = ( groups filter (_.id != composedRule.op.id) map (group => Group(group.id, group.options -- (composedRule.set filter (_.id == group.id) map (_.option) ))) ) def subsets(groups: Set[Group], composedRules: Set[ComposedRule]) = ( composedRules map (composedRule => subset(groups, composedRule)) ) def combinations(groups: Set[Group]) = ( groups.toList map (_.options.size) reduceLeft (_*_) ) def allCombinations(groups: Set[Group], rules: Set[Rule]) = { val fixedRules = fixRules(rules) val composedRules = composeRules(fixedRules) val cleanSet = cleanseSet(groups, fixedRules) val otherSets = subsets(cleanSet, composedRules) val allSets = otherSets + cleanSet val totalCombinations = allSets.toList map (set => combinations(set)) reduceLeft (_+_) totalCombinations } } object TestCombinations { val groups = Set(Group(1, Set(1, 2, 3, 4, 5)), Group(2, Set(1, 2, 3)), Group(3, Set(1, 2, 3, 4))) val rules = Set(Rule(GroupOption(1, 1), GroupOption(2, 2)), Rule(GroupOption(1, 1), GroupOption(3, 4)), Rule(GroupOption(2, 2), GroupOption(3, 2))) def test = ComputeCombinations.allCombinations(groups, rules) } 
+1
source

, ... ; ; , , , . , . , , , () 18000 -, 80- , - select/some multi-select. , , ; (.. , , , , ) . ; ( -) ~ 450 , - , xml /. / xml, , ~ 150 , .

; .

+1
source

x ^ n, n - , x - ?

0
source

, . , , Cr [i, j] , C [k]. , . . C [k] . C [i, j] , ( ) . , , , .

= C ( ) - Cr [1] - Cr [2] - Cr [3]

. , , . . , .

0
source

Daniel post :

, , , scala . , .

, :

 Group 1: a1 a2 a3 a4 a5 Group 2: b1 b2 b3 Group 3: c1 c2 c3 c4 Group 4: d1 d2 d3 d4 d5 Rules: a1 <-> b2 a1 <-> c2 b2 <-> c2 b2 <-> d1 a2 <-> d2 

( 227 ):

 Without rules => 300
Rules: [1] => 20
Rules: [2] => 15
Rules: [3] => 25
Rules: [4] => 20
Rules: [5] => 12
Order: 1 => 208 (diff: -92)
Rules: [1, 2] => 5
Rules: [1, 3] => 5
Rules: [2, 3] => 5
Rules: [1, 4] => 4
Rules: [2, 4] => 1
Rules: [3, 4] => 5
Rules: [1, 5] => 0
Rules: [2, 5] => 0
Rules: [3, 5] => 1
Rules: [4, 5] => 0
Order: 2 => 234 (diff: 26)
Rules: [1, 2, 3] => 5
Rules: [1, 2, 4] => 1
Rules: [1, 3, 4] => 1
Rules: [2, 3, 4] => 1
Rules: [1, 2, 5] => 0
Rules: [1, 3, 5] => 0
Rules: [2, 3, 5] => 0
Rules: [1, 4, 5] => 0
Rules: [2, 4, 5] => 0
Rules: [3, 4, 5] => 0
Order: 3 => 226 (diff: -8)
Rules: [1, 2, 3, 4] => 1
Rules: [1, 2, 3, 5] => 0
Rules: [1, 2, 4, 5] => 0
Rules: [1, 3, 4, 5] => 0
Rules: [2, 3, 4, 5] => 0
Order: 4 => 227 (diff: 1)
Rules: [1, 2, 3, 4, 5] => 0
Order: 5 => 227 (diff: 0)

***Combinations: 227***

scala:

 val groups = Set(Group(1, Set(1, 2, 3, 4, 5)),
                   Group(2, Set(1, 2, 3)),
                   Group(3, Set(1, 2, 3, 4)),
                   Group(4, Set(1, 2, 3, 4, 5)))

  val rules = Set(Rule(GroupOption(1, 1), GroupOption(2, 2)),
                  Rule(GroupOption(1, 1), GroupOption(3, 2)),
                  Rule(GroupOption(2, 2), GroupOption(3, 2)),
                  Rule(GroupOption(2, 2), GroupOption(4, 1)),
                  Rule(GroupOption(1, 2), GroupOption(4, 2)))

258 .

, . , ? , .

0
source

.

  • # P-complete, .
  • , - , , NP-complete
  • , - , , , (2SAT)

; P = NP.

:

:

  • , #P, , 2SAT . 2SAT,

(p1 p2) (p2 p3)

p1, p2, p3. , . , = p1, p2, p3, .

  1. , - , NP, 3SAT . 3SAT,

(p1 p2 p3) (p2 p1 p4)

:

p1 p1true p1false

p2 p2false p2true

p3 p3false p3true

p4 p4false p4true

group clause_1 c1a c1b c1c

group clause_2 c2a c2b c2c

clause_1 : (p1 p2 p3). , c1a p1true, c1b , p2true , c1c , p3false. , :

p1false ↔ c1a

p2false ↔ c1b

p3true ↔ c1c

clause_2, constaints

p2false ↔ c2a

p1true ↔ c2b

p4false ↔ c2c

( > 0), , p1,..., p4, 3SAT . , ( = 0), 3SAT . , , > 0, , 3SAT.

, . .

, 0: - NP-hard, . ( "" , "" . , .)

0
source

sdcvvc , -? N ( N , : , ), , . .

0
source

All Articles