4

Let's begin by listing what I have looked at and what I am not looking for

I do not want to list all the permutations from an array - Get all permutations of a PHP array?

I do not want to find all combinations in order from an array - https://stackoverflow.com/a/38871855/1260548

The two above examples got me to where I am now but I still generate too many combinations. With 50 nodes I end up with billions if not trillions of combinations and I think I can reduce this further with a tree structure.

What I am looking for is all the possible ordered combinations from a tree which could be structured as a multidimensional array like so

[1]
--[2]
--[4]
[8]
--[3]
--[9]
----[5]
[6]
[7]

What I want to find are all the possible open nodes (even leaves/end nodes can be open). So one possible combination here is all the numbers like

  • 1.2.3.4.5.8.9

node 1 here is a parent of 2 and 4. 8 is a parent of 3 and 9. 9 is a child of 8 but a parent of 5. Other possible combinations are.

  • 1
  • 1.2.4
  • 1.6.7.8
  • 3.5.8.9
  • 3.5.6.7.8.9

It is not possible to open a node if the parent is not open. For example, if 1 is not included then 2 and 4 can not be included. If 9 is not included then 5 can not be included and if 8 is not included then 3, 9 and 5 can not be included.

The following is code I use to generate a sample node structure to test with. Please note that this structure is ordered and has a fixed depth where as I would like to come up with a function that will work with any order and any depth.

$arr = [];
$result = [];
$xtotal = 0;
$ytotal = 0;
$ztotal = 0;
for ($x=0; $x<2; $x++) {
  $arr[$xtotal] = array();
  $ytotal = $xtotal+1;
  for ($y=0; $y<2; $y++) {
    $arr[$xtotal][$ytotal] = array();
    $ztotal = $ytotal+1;
    for ($z=0; $z<2; $z++) {
      $arr[$xtotal][$ytotal][$ztotal] = array();
      $ztotal++;
    }
    $ytotal = $ztotal+1;
  }
  $xtotal = $ytotal+1;
}
for ($c=0; $c<5; $c++) {
  $arr[$xtotal] = array();
  $xtotal++;
}

So I am wondering how to go about writing a function that would list all these possible combinations?

EDIT: With a smaller set I can list all possible combinations.

[1]
--[2]
--[4]
[8]

1
8
1.8
1.2
1.4
1.2.8
1.4.8
1.2.4
1.2.4.8
Peter
  • 29,454
  • 5
  • 48
  • 60
Uberswe
  • 1,038
  • 2
  • 16
  • 36
  • 1
    provide your tree structure, means code and what have u tried? – Shubham Agrawal Jul 28 '17 at 08:59
  • @ShubhamAgrawal I added some code to generate a sample tree structure, note that the same is ordered and only a few levels deep but I would like to make a function that will work for nodes in any order and any depth. I have tried the linked questions above and perhaps I can use them to solve my problem. I am trying to solve this but at this time I have no code to show that even comes close to solving my issue which is why I asked in the hopes that someone has encountered this problem before. – Uberswe Jul 28 '17 at 09:53
  • My question is very similar to this question which also does not have a clear answer https://stackoverflow.com/questions/41248914/finding-all-possible-paths-in-graph – Uberswe Jul 31 '17 at 10:00
  • Can you explain what an "open node" is and what it means for the tree structure to "be opened"? How does it differ from the "all possible paths" question you linked to? – Jory Geerts Jul 31 '17 at 10:54
  • The way I visualise my tree is similar to this example from vue.js (https://vuejs.org/v2/examples/tree-view.html) which is why I refer to them as being opened or closed. I can also open the end nodes to show a description for example which is why I can have the combinations 1.2 and 1.4 and not just 1.2.4 with 1 being opened. Now that I look back at my question maybe it is confusing to describe it as opened/closed – Uberswe Jul 31 '17 at 11:02
  • "It is not possible to open a node if the parent is not open." How is "3.5.8.9" a valid combination then? – Peter Jul 31 '17 at 18:35
  • @Peter `3.5.8.9` is listed in asc order, not in path order. Perhaps it will be easier to think of it as `8.3.9.5` – mickmackusa Aug 01 '17 at 02:04
  • 1
    @MarkusTenghamn I have some questions here: https://pastebin.com/PqUwNnm8 – mickmackusa Aug 01 '17 at 03:32
  • @mickmackusa Thanks for the questions and feedback. I updated my question with a complete example using a smaller array along with all the possible combinations. I answered your questions here: https://pastebin.com/vTdRjayq – Uberswe Aug 01 '17 at 08:23

1 Answers1

1

I've come up with a function that seems to do what you are looking for. However, in storing all the different combinations, you can easily start running into memory issues. If you want 50 nodes, this solution might not work for you depending on memory limits.

I used a little bit different node generator (though I did test with yours too) that allowed me more flexibility in creating random combinations:

$arr = [];
$counter = 0;
// you can change the (2,6) to produce different numbers of root nodes
for($i=1;$i<=mt_rand(2,6);$i++){
    $curr = $counter++;
    $arr[$curr] = [];
    // guarantee the first node (0) will have children nodes (easier testing) - random for other nodes
    $child = ($curr == 0) ? true : rand(0,1);
    if($child){
        // you can change the (1,2)
        for($j=1;$j<=mt_rand(1,2);$j++){
            $curr2 = $counter++;
            $arr[$curr][$curr2] = [];
            $child2 = rand(0,1);
            if($child2){
                // you can change the (1,2) here too
                for($k=1;$k<=mt_rand(1,2);$k++){
                    $curr3 = $counter++;
                    $arr[$curr][$curr2][$curr3] = [];
                }
            }
        }
    }
}

Now for the calculating:

function treenodes($arr,&$results,$parent=null){
    foreach($arr as $k=>$a){
        // here we copy all our current results - this gives us one with the current node closed (original), and one with it open (clone)
        $clone = [];
        foreach($results as $key=>$result){
            // if this node is allowed in this result (parent is on) - root nodes are always allowed
            if($parent === null || in_array($parent,$result)){
                $clone[] = array_merge($result,array($k));
            }
        }
        $results = array_merge($results,$clone);
        // if this node has children, run this function on them as well
        if(count($a)){
            treenodes($a,$results,$k);
        }
    }
}
// we start with one option of no nodes open
$results = [[]];
treenodes($arr,$results);

// show results - you can order these another way if you'd like before printing
print count($results)."\n";
foreach($results as $result){
    print implode(",",$result)."\n";
}
Jo.
  • 778
  • 1
  • 12
  • 17
  • Seems to be working, I want to test it more but will mark this is the answer if everything works as expected. Thanks for the nice seed as well :) – Uberswe Aug 01 '17 at 08:52
  • Works as expected with up to 30 nodes, after that I start running into memory issues as the result gets exponentially larger. I guess it would be possible to get around this with something like `SplFixedArray` – Uberswe Aug 01 '17 at 14:52