四叉树的堆栈溢出异常

时间:2016-12-08 22:00:24

标签: c# asp.net recursion

我遇到了我在C#中工作的Quad Tree实现的问题。在文件Tree.cs中,以下行将导致堆栈溢出异常,始终在树中的大约50个对象开始(可能足以导致右下角的第一个分支):

else
{
    //bottom right
    TreeList[3].PushB(b);
    return;
}

出于某种原因,似乎当我允许调用此代码时,它会创建一个无限循环,因此Stack Overflow Exception。我不明白为什么这会导致无限递归,而其他人则没有。

这是代码。 Ball.cs和Tree.cs都位于Classes文件夹中。

Ball.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QuadTree.Classes
{

    class Ball
    {
        protected int x, y, r;
        protected decimal vx, vy;
        public static int min_w = 0, 
            max_w = 200, 
            min_h = 0, 
            max_h = 200;

        //treating origin as top-left of screen


        public Ball(int set_x = 1, int set_y = 1, decimal set_vx = 1, decimal set_vy = 1, int set_r = 1)
        {
            x = set_x;
            y = set_y;
            vx = set_vx;
            vy = set_vy;
            r = set_r;
        }


        public int get_x()
        {
            return x;
        }


        public int get_y()
        {
            return y;
        }


        public void Print()
        {
            Console.WriteLine("x: {0} y: {1} vx: {2} vy: {3} r: {4}", x, y, vx, vy, r);
        }


        //get the y-intercept of the current ball
        protected decimal getB()
        {
            return (decimal)y - ((vy / vx) * (decimal)x);
        }


        //get the y-intercept given an x, y, and slope
        public decimal getB(int x, int y, decimal m)
        {
            return (decimal)y - (m * (decimal)x);
        }


        //get the slop of the line that goes through both balls
        protected decimal getM(Ball b)
        {
            return getM(y, b.y, x, b.x);
        }


        //get the slop of the line going through two points
        public decimal getM(int y1, int y2, int x1, int x2)
        {
            if (x1 - x2 == 0)
            {
                return 0;
            }
            else
            {
                return ((decimal)(y1 - y2)) / ((decimal)(x1 - x2));
            }
        }


        public void Move()
        {
            x += (int)vx;
            y += (int)vy;

            if (x > max_w)
            {
                vx *= -1;
                x = x - (x - max_w);
            }
            else if (x < min_w)
            {
                vx *= -1;
                x *= -1; //won't work if min_w != 0
            }

            if(y > max_h)
            {
                vy *= -1;
                y = y - (y - max_h);
            }
            else if (y < min_h)
            {
                vy *= -1;
                y *= -1; //won't work if min_h !=0
            }
        }


        //detect if the current ball collides with the given ball
        public void Collide(Ball b)
        {
            decimal d;

            d = (decimal)Math.Sqrt(Math.Pow((x - b.x), 2) + Math.Pow((y - b.y), 2));

            if (d<= r || d <= b.r)
            {
                ResolveCollision(b);
            }
            return;
        }


        //determine the resulting vectors after the collision
        private void ResolveCollision(Ball b)
        {
            //get the line between the center points
            decimal M; 
            M = getM(b);


            //determine angle between the line and ball a
            double theta_1;

            if (b.vx != 0)
            {
                double top = (double)((M - (b.vy / b.vx)));
                double bottom = (double)(1 + (M * (b.vy / b.vx)));

                if (bottom != 0)
                {
                    theta_1 = Math.Atan(top / bottom);
                }
                else
                {
                    theta_1 = 0;
                }
            }
            else
            {
                if (1 + M != 0)
                {
                    theta_1 = Math.Atan((double)(M / (1 + M)));
                }
                else
                {
                    theta_1 = 0;
                }

            }


            theta_1 = theta_1 * (Math.PI / 180);

            //calculate new vx and vy for ball a
            //http://www.gamefromscratch.com/post/2012/11/24/GameDev-math-recipes-Rotating-one-point-around-another-point.aspx
            double new_vx, new_vy;


            new_vx = Math.Cos(theta_1) * (double)(vx) - Math.Sin(theta_1) * (double)(vy) + x;
            new_vy = Math.Sin(theta_1) * (double)(vx) + Math.Cos(theta_1) * (double)(vy) + y;



            vx = (decimal)new_vx - x;
            vy = (decimal)new_vy - y;

            //determine angle between the line and ball b

            if (b.vx != 0)
            {
                double top = (double)((M - (b.vy / b.vx)));
                double bottom = (double)(1 + (M * (b.vy / b.vx)));

                if (bottom != 0)
                {
                    theta_1 = Math.Atan(top / bottom);
                }
                else
                {
                    theta_1 = 0;
                }


            }
            else
            {
                if (1 + M != 0)
                {
                    theta_1 = Math.Atan((double)(M / (1 + M)));
                }
                else
                {
                    theta_1 = 0;
                }
            }


            theta_1 = theta_1 * (Math.PI / 180);

            //calculate new vx and vy for ball a


            new_vx = Math.Cos(theta_1) * (double)(b.vx) - Math.Sin(theta_1) * (double)(b.vy) + b.x;
            new_vy = Math.Sin(theta_1) * (double)(b.vx) + Math.Cos(theta_1) * (double)(b.vy) + b.y;


            b.vx = (decimal)new_vx - x;
            b.vy = (decimal)new_vy - y;
        }
    }
}

Tree.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QuadTree.Classes
{
    class Tree //: IDisposable
    {
        protected int min_w,
            max_w,
            min_h,
            max_h,
            thresh_hold, level;

        bool leaf = true;

        protected List<Ball> BallList = new List<Ball>();
        protected List<Tree> TreeList = new List<Tree>();


        public Tree(int set_min_w, int set_max_w, int set_min_h, int set_max_h, int set_thresh_hold, int set_level)
        {
            min_w = set_min_w;
            max_w = set_max_w;
            min_h = set_min_h;
            max_h = set_max_h;
            thresh_hold = set_thresh_hold;
            level = set_level;
        }


        //push a ball onto the tree
        public void PushB(Ball b)
        {
            if(leaf)
            {
                BallList.Add(b);

                if (BallList.Count > thresh_hold)
                {
                    Branch();
                }
            }
            else
            {
                LeafPush(b); //push the ball to a leaf node
            }
            return;
        }


        //push a ball onto a leaf of the current node
        protected void LeafPush(Ball b)
        {
            if (b.get_x() <= max_w / 2)
            {
                //left
                if (b.get_y() <= max_h / 2)
                {
                    //top left
                    TreeList[0].PushB(b);
                    return;
                }
                else
                {
                    //bottom left
                    TreeList[2].PushB(b);
                    return;
                }
            }
            else
            {
                //right
                if (b.get_y() <= max_h / 2)
                {
                    //top right
                    TreeList[1].PushB(b);
                    return;
                }
                else
                {
                    //bottom right
                    TreeList[3].PushB(b);
                    return;
                }
            }
        }


        private void Branch()
        {
            Console.WriteLine("Branching level {0}", level);

            leaf = false;

            TreeList.Add(new Tree(min_w, max_w / 2, min_h, max_h / 2, thresh_hold, level + 1));                //top left
            TreeList.Add(new Tree((max_w / 2) + 1, max_w, min_h, max_h / 2, thresh_hold, level + 1));          //top right
            TreeList.Add(new Tree(min_w, max_w / 2, (max_h / 2) + 1, max_h, thresh_hold, level + 1));          //bottom left
            TreeList.Add(new Tree((max_w / 2) + 1, max_w, (max_h / 2) + 1, max_h, thresh_hold, level + 1));    //bottom right

            foreach(Ball b in BallList)
            {
                LeafPush(b);
            }

            BallList.Clear();

            return;
        }
    }
}

Program.cs的

using QuadTree.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace QuadTree
{
    class Program
    {
        static void Main(string[] args)
        {
            Random rnd = new Random();

            List<Ball> BallList = new List<Ball>();

            for (int i = 0; i < 100; i++)
            {
                BallList.Add(new Ball(rnd.Next(Ball.min_w, Ball.max_w),
                                      rnd.Next(Ball.min_h, Ball.max_h),
                                      rnd.Next(1, 5),
                                      rnd.Next(1, 5),
                                      rnd.Next(1, 5)));
            }




            Tree t = new Tree(Ball.min_w, Ball.max_w, Ball.min_h, Ball.max_h, 10, 0);

            foreach (Ball b in BallList)
            {
                b.Move();
                t.PushB(b);
            }


            Console.ReadLine();
        }

    }
}

2 个答案:

答案 0 :(得分:1)

您需要修改您创建子树的方式。当您创建第四个子树(右下象限)时,您将使用以下数字:

(max_w / 2) + 1, max_w, (max_h / 2) + 1, max_h

这总是会产生右下象限分支的相同尺寸(101,200,101,200),因为您只使用了最大数字。对于每个后续分支中的右下象限也是如此。

程序将运行正常,直到您达到第四个子树的阈值。然后它尝试分支,并且当它分支时,它将所有它的球发送到随后的第四子树中。这将继续发生,因为所有这些球在该象限中都有坐标。这就是你的无限循环发生的地方。

如果您试图继续细分象限,则需要将新尺寸基于父象限的最小和最大宽度和高度。

编辑:

此代码应正确细分象限:

int center_w = min_w + (max_w - min_w) / 2;
int center_h = min_h + (max_h - min_h) / 2;
TreeList.Add(new Tree(min_w, center_w, min_h, center_h, 
    thresh_hold, level + 1)); // top left
TreeList.Add(new Tree(center_w + 1, max_w, min_h, center_h, 
    thresh_hold, level + 1)); //top right
TreeList.Add(new Tree(min_w, center_w, center_h + 1, max_h, 
    thresh_hold, level + 1)); //bottom left
TreeList.Add(new Tree(center_w + 1, max_w, center_h + 1, max_h,
    thresh_hold, level + 1)); //bottom right

答案 1 :(得分:0)

所以我似乎需要逻辑来测试创建一组新的节点是否可行。我决定要一个节点的最小宽度为10,所以我更改了这段代码:

//push a ball onto the tree
public void PushB(Ball b)
{
    if(leaf)
    {
        BallList.Add(b);

        if (BallList.Count > thresh_hold)
        {
            //test if branching will produce a viable area
            if ((max_w / 2) - min_w >= 10)
            {
                Branch();
            }
        }
    }
    else
    {
        LeafPush(b); //push the ball to a leaf node
    }
    return;
}

现在我不再获得Stack Overflow异常