Search code examples
data-structurestreecomputer-science

Why generating a Tree according to initial depth is infinite loop (recursion)?


Somebody help me to find the cause of infinite loop (recursion) ?

root = generate_tree(0) // only the root (0 initial_depth)

root = generate_tree(1) // root and two children (1 initial_depth)

0 or 1 initial_depth is working properly but if greater than 1 it will cause an infinite loop.

Here's my Pseudo Code of creating a Tree according to initial depth

class Node
    children = []
    generate_subtree()
        //let assume it will create two random nodes and pass it to children
        children = two random nodes


Node generate_tree(initial_depth)
if initial_depth == 0
   return a random Node
else
   root = random node
   grow_tree(root, initial_depth)
   return root;


grow_tree(tree, init_depth)
    if initial_depth == 1
        tree.generate_subtree()
    else if initial_depth > 1
        tree.generate_subtree()
        for subtree in tree.children
            grow_tree(subtree, initial_depth - 1)

Update I included my actual code

Im working on Unity with C# script

public List<GNode> TERMINALS = new List<GNode>
{
      new CanMove_UP(),
      new CanMove_DOWN(),
      new CanMove_LEFT(),
      new CanMove_RIGHT()
};

public List<GNode> NODES = new List<GNode>
{
      new ConstantNum(),
      new RandomNum()
};

Main Program let's say i want to create a tree

Tree tree1 = new Tree(0, NODES, TERMINALS); //working properly
Tree tree2 = new Tree(1, NODES, TERMINALS); //working properly
Tree tree3 = new Tree(2, NODES, TERMINALS); //it will cause infinite loop

GNode.cs

using System;
using System.Collections.Generic;

namespace GP
{
    public abstract class GNode
    {
        public const float TERMINAL_RATIO = 0.2f; 

        public String data { get; private set; }
        public bool is_terminal { get; private set; }
        public int depth { get; private set; }
        public GNode[] children { get; private set; }

        public abstract void initialize(int depth = 0);
        public abstract void generate_subtree(List<GNode> nodes, List<GNode> terminals);

        public GNode() { }

        public GNode(String data, bool is_terminal, int depth = 0)
        {
            this.initialize(data, is_terminal, depth);
        }

        protected void initialize(String data, bool is_terminal, int depth = 0)
        {
            this.data = data;
            this.is_terminal = is_terminal;
            this.depth = depth;
            this.children = new GNode[0];
        }

        protected void generate_subtree(List<GNode> nodes, List<GNode> terminals, int num_of_childrens)
        {
            List<GNode> classes = new List<GNode>();
            for (int i = 0; i < num_of_childrens; i++)
            {
                if (nodes.Count > 0 && Utility.GetRandomFloat() > GNode.TERMINAL_RATIO)
                    classes.Add(nodes[Utility.GetRandomNumber(0, nodes.Count)]);
                else
                    classes.Add(terminals[Utility.GetRandomNumber(0, terminals.Count)]);
                    classes[i].initialize(this.depth + 1);
            }
            this.children = classes.ToArray();
        }
    }

    #region NODES
    public class ConstantNum : GNode
    {
        public ConstantNum(int depth = 0)
        {
            this.initialize(depth);
        }

        public override void initialize(int depth = 0)
        {
            base.initialize(Utility.GetRandomNumber(0, 9).ToString(), true, depth);
        }

        public override void generate_subtree(List<GNode> nodes, List<GNode> terminals)
        {
            base.generate_subtree(nodes, terminals, 2);
        }
    }

    public class RandomNum : GNode
    {
        public RandomNum(int depth = 0)
        {
            this.initialize(depth);
        }

        public override void initialize(int depth = 0)
        {
            base.initialize("random", true, depth);
        }

        public override void generate_subtree(List<GNode> nodes, List<GNode> terminals)
        {
            base.generate_subtree(nodes, terminals, 2);
        }
    }
    #endregion


    #region TERMINALS
    public class MeasureNode : GNode
    {
        public String Measure { set; private get; }

        public override void initialize(int depth = 0)
        {
            base.initialize(this.Measure, true, depth);
        }

        public override void generate_subtree(List<GNode> nodes, List<GNode> terminals)
        {
             base.generate_subtree(nodes, terminals, 2);
        }
    }

    public class CanMove_UP : MeasureNode
    {
        public override void initialize(int depth = 0)
        {
            base.Measure = "CanMove_UP";
            base.initialize(depth);
        }
    }

    public class CanMove_DOWN : MeasureNode
    {
        public override void initialize(int depth = 0)
        {
            base.Measure = "CanMove_DOWN";
            base.initialize(depth);
        }
    }

    public class CanMove_LEFT : MeasureNode
    {
        public override void initialize(int depth = 0)
        {
            base.Measure = "CanMove_LEFT";
            base.initialize(depth);
        }
    }

    public class CanMove_RIGHT : MeasureNode
    {
        public override void initialize(int depth = 0)
        {
            base.Measure = "CanMove_RIGHT";
            base.initialize(depth);
        }
    }
    #endregion
}

Tree.cs

using System;
using System.Collections.Generic;

namespace GP
{
    public class Tree
    {
        private int initial_depth;
        private List<GNode> nodes;
        private List<GNode> terminals;
        private GNode root;

        public Tree(int initial_depth, List<GNode> nodes, List<GNode> terminals, GNode root = null)
        {
            this.initial_depth = initial_depth;
            this.nodes = nodes;
            this.terminals = terminals;
            this.root = root;
            if (root == null)
                this.root = this.generate_tree(initial_depth, nodes, terminals);
        }

        public GNode generate_tree(int initial_depth, List<GNode> nodes, List<GNode> terminals)
        {
            if (initial_depth == 0)
                return terminals[Utility.GetRandomNumber(0, terminals.Count)]; //construct a random node from terminals
            else
            {
                GNode tree;
                if (Utility.GetRandomFloat() <= GNode.TERMINAL_RATIO)
                    tree = terminals[Utility.GetRandomNumber(0, terminals.Count)];
                else
                    tree = nodes[Utility.GetRandomNumber(0, nodes.Count)];
                this.grow_tree(tree, initial_depth, nodes, terminals);
                return tree;
            }
        }

        public void grow_tree(GNode tree, int init_depth, List<GNode> nodes, List<GNode> terminals)
        {
            if (initial_depth == 1)
                tree.generate_subtree(new List<GNode>(), terminals); //empty nodes
            else if (initial_depth > 1)
            {
                tree.generate_subtree(nodes, terminals);
                foreach (GNode subtree in tree.children)
                    this.grow_tree(subtree, initial_depth - 1, nodes, terminals);
            }
        }
    }
}

Utility.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public static class Utility
{
    private static readonly System.Random getRandom = new System.Random();

    public static int GetRandomNumber(int min, int max)
    {
        lock (getRandom)
        {
            return getRandom.Next(min, max);
        }
    }

    public static int GetRandomNumber(int max)
    {
        return GetRandomNumber(0, max);
    }

    public static float GetRandomFloat(float min = 0.0f, float max = 1.0f)
    {
        return Random.Range(min, max);
    }
}

Solution

  • class Node:
    
        def __init__(self):
            self.children = []
    
        def generate_subtree(self):
            self.children.append(Node())
            self.children.append(Node())
    
    
    def generate_tree(initial_depth):
        if initial_depth == 0:
            return Node()
        else:
            root = Node()
            grow_tree(root, initial_depth)
            return root
    
    
    def grow_tree(tree, initial_depth):
        if initial_depth == 1:
            tree.generate_subtree()
        elif initial_depth > 1:
            tree.generate_subtree()
            for subtree in tree.children:
                grow_tree(subtree, initial_depth - 1)
    
    tree = generate_tree(4)
    

    I have translated the exact pseudo-code that you have written and it is working fine. Can you post your code, or you can just verify from mine what you are missing.