Get all possible combinations of the following tree data structures
Later, you will see a data structure that is very similar to a simplified version of the function model (there is a view for them here ) and is some version of the tree. I have implemented a data structure in Java and now I am trying to parse it. In particular, I want to get all the combinations of elements that are possible if a specific feature model was provided.
Suppose we have the following feature model:
Items are fields. Circles that are not filled indicate optional items (remember o for optional), filled for required items . The root element must be included. Due to these rules, the following combinations or lists of elements are possible:
- [1,3,5,6]
- [1,2,3,5,6]
- [1,3,4,5,6,7]
- [1,2,3,4,5,6,7]
Now I want to get them not through visual search, but through Java program. For this, I have implemented several classes:
-
Tree
stores the entire model of the object (figure). -
Element
is one of the rectangles. -
Edge
describes lines and circles. -
Combination
contains one possible combination (combination list).
In the class, Element
I have implemented a function to get all combinations named recursiveConfiguration
:
public List<Combination> recursiveCombination(List<Combination> combinations){
for(Combination c: combinations){
c.add(this);
}
// Iterate through all edges of the current element
for(Edge e: getEdges()){
int type = e.getType();
// In this case getChildren always returns only one feature.
Element child = e.getChildren().get(0);
List<Combination> childCombinations = child.recursiveCombination(combinations);
if(type == 1){
// If type is mandatory
combinations = childCombinations; // Line with compiler error
}else{
// If type is optional
combinations.addAll(childCombinations);
}
}
return combinations;
}
Algorithm explanation: It calls a list of combinations of one element, which is an empty list. The function is called on the root element 1
. It adds the first loop of the loop 1
to the combinations (it uses DFS to traverse the tree). Second, the method calls itself starting at the element 2
, passing in the original list (containing 1
). From here the configuration is returned, consisting of elements 1
and 2
. The returned list is processed depending on the type of edge (required or optional and added according to the original list). In this particular case, the list should consist of two combinations of 1
and 1,2
. When the subtree under the first edge (in this case only the element 2
) is complete, the next subtree is processed (3
, 5
, 6
).
The next is not important anymore, look in the horizontal bar.
But at the moment I am confused with recursion because when I call this the returned list is combinations
empty. I believe it has to do with the way the leaves are treated. Because they are not being processed properly at the moment (see bold text above). Anyone have an idea? Of course, I will try to explain the problem in more detail if necessary, and thanks for the thoughts you asked in this question.
Specification of the original question: Suppose the algorithm works at all (I checked it several times). Therefore, I assume that my understanding of Java is wrong in some part of the program. Two thoughts might be interesting:
- After I turned on all the compiler warnings (thanks Prune), it remains to leave
The parameter configurations should not be assigned
and showConfigure problem severity
for the linecombinations = childCombinations;
. Perhaps this is the problem? - Is it possible that the variable
combinations
that is always passed to the function changes in the second / third / ... recursive call directly and not after returning?
Currently, the output generated by the program is returning the following combinations (which is clearly not correct):
- [1, 2, 3, 3, 5, 5, 6, 6, 4, 4, 7, 7]
- [1, 2, 3, 3, 5, 5, 6, 6, 4, 4, 7, 7]
- [1, 2, 3, 3, 5, 5, 6, 6, 4, 4, 7, 7]
- [1, 2, 3, 3, 5, 5, 6, 6, 4, 4, 7, 7]
Thanks for your time and effort!
source to share
Since I was not able to clearly explain the problem, you were unable to post a solution. I will now post the solution. First, spoiled the surprise: the algorithm was correct as I understood it, but the same variable combinations
was changed during the entire recursion process. Because of this, the same combination was produced four times.
The solution is to use the method clone
and therefore change List
to LinkedList
.
private LinkedList<Combination> recursiveConfiguration(LinkedList<Combination> combinations){
for(Combination c: combinations){
c.add(this);
}
// Iterate through all edges of the current node
for(Edge e: getEdges()){
int type = e.getType();
if((type == 1) || (type == 2)){
// If type is mandatory or optional.
// In this case getChildren always returns only one feature.
LinkedList<Combination> copyConfig = new LinkedList<>();
for(Combination c: configurations){
@SuppressWarnings("unchecked")
LinkedList<FeatureNode3> copy = (LinkedList<FeatureNode3>) c.getFeatureList().clone();
Combination config = new Combination();
config.setFeatureList(copy);
copyConfig.add(config);
}
FeatureNode3 child = e.getChildren().get(0);
LinkedList<Combination> childCombinations = child.recursiveConfiguration(copyConfig);
if(type == 1){
// If type is mandatory
combinations = childCombinations;
}else{
// If type is optional
combinations.addAll(childCombinations);
}
}
}
return combinations;
}
Thanks for your time and advice!
source to share
I think your logic of adding a node for each combination in the list at the beginning is wrong, this should be done later.
The correct algorithm might look like this:
-
Call your recursive function using the root node. Return a list of combinations.
-
If it's a leaf node, return a list of combinations with just the node inside.
-
For each edge: call the recursive function with the child node and store the results.
-
Create all possible combinations of obligatory children. For example. with two obligatory children, one has 11 combinations, the other has 7, you get 11 * 7 = 77 combinations. You are basically building a cross product of all the required combinations, this is an easier step. If a node has no required children, then it is a list of combinations with one empty combination inside (to which the current node will be appended).
-
Combine with extra kids combinations. For example. with two extra extra children with 3 and 5 combinations, you get 77 + 77 * 3 + 77 * 5 + 77 * 3 * 5 = 1848 combinations. Here you have the option to choose whether you take an optional branch or not and so it blows up pretty quickly, with 3 branches a, b, c and x required combinations, you get x + ax + bx + cx + abx + acx + bcx + abcx (all combinations of taking and not taking a, b or c).
-
Add a node function to call with each combination and return the result.
source to share