简体   繁体   English

避免堆栈溢出(C中的迷宫生成器)

[英]Avoiding stack overflow (Maze generator in C)

I'm currently writing a maze generator in C, using the depth-first search algorithm. 我目前正在使用深度优先搜索算法在C中编写迷宫生成器。 It's working really well, and I'm happy with the result, but when I push the dimensions of the maze a bit too far (more than 2000x2000), it gives me a stack overflow. 它工作得非常好,我对结果感到满意,但是当我将迷宫的尺寸推得太远(超过2000x2000)时,它会给我一个堆栈溢出。

I know it's due to the recursivity used in the algorithm, but I really don't know how I can avoid this problem... 我知道这是由于算法中使用的递归性,但我真的不知道如何避免这个问题......

Here's the sample of the program where the recursive occurs : 以下是递归发生的程序示例:

*int dirs is composed of 4 numbers randomized (1, 2, 3 and 4) * int dirs由随机化的4个数字组成(1,2,3和4)

x and y are the coordinates in the map xy是地图中的坐标

void    recursive_gen(char **map, int x, int y, t_size size)
{
  int   *dirs;
  int   i;

  i = -1;
  dirs = gen_random_dirs();
  while (++i < 4)
    {
      if (dirs[i] == 1)
        up(map, x, y, size);
      if (dirs[i] == 2)
        right(map, x, y, size);
      if (dirs[i] == 3)
        down(map, x, y, size);
      if (dirs[i] == 4)
        left(map, x, y, size);
    }
}

And there's up function (the other are pretty much the same): 并且有功能(另一个几乎相同):

void    up(char **map, int x, int y, t_size size)
{
  if (y - 2 < 0)
    return ;
  if (map[y - 2][x] == 'X')
    {
      map[y - 1][x] = '*';
      map[y - 2][x] = '*';
      recursive_gen(map, x, y - 2, size);
    }
}

EDIT : So I did the same in iterative, with a custom stack to stock coords. 编辑:所以我在迭代中做了同样的事情,使用自定义堆栈来存储coords。 It works great, but I can't figure out if 10k*10k maze if infinite looping, or if it's really really long (1000*1000 takes 43s, 10k*10k I stopped the program at 1000s) 它工作得很好,但我无法弄清楚10k * 10k迷宫是否无限循环,或者它真的很长(1000 * 1000需要43s,10k * 10k我在1000s停止了程序)

Is there anyway I can optimize it? 无论如何我可以优化吗? Here's the new code : 这是新代码:

void    recursive_gen(char **map, t_size size)
{
  int   *pos;
  int   *dirs;
  int   **stack;
  int   i;
  int   istack;

  istack = 0;
  pos = malloc(sizeof(int) * 2);
  pos[0] = 0;
  pos[1] = 0;
  stack = alloc_stack(size);
  while (is_stack_empty(stack) == 0)
    {
      dirs = gen_random_dirs();
      i = -1;
      while (++i < 4)
        {
          if (dirs[i] == 1 && up(map, pos, size, stack) == 1)
            break ;
          if (dirs[i] == 2 && right(map, pos, size, stack) == 1)
            break ;
          if (dirs[i] == 3 && down(map, pos, size, stack) == 1)
            break ;
          if (dirs[i] == 4 && left(map, pos, size, stack) == 1)
            break;
        }
      if (i == 4)
        {
          pos[0] = stack[istack][0];
          pos[1] = stack[istack][1];
          stack[istack][0] = -1;
          stack[istack][1] = -1;
          istack -= 1;
        }
      else
        istack += 1;
    }
}

And the new up function : 新功能:

int     lastof_stack(int **stack)
{
  int   i;

  i = 0;
  while (stack[i][1] != -1)
    i++;
  return (i);
}

int     up(char **map, int *pos, t_size size, int **stack)
{
  if (pos[1] - 2 < 0)
    return (0);
  if (map[pos[1] - 2][pos[0]] == 'X')
    {
      map[pos[1] - 1][pos[0]] = '*';
      map[pos[1] - 2][pos[0]] = '*';
      pos[1] -= 2;
      stack[lastof_stack(stack)][0] = pos[0];
      stack[lastof_stack(stack)][1] = pos[1];
      return (1);
    }
  return (0);
}

EDIT : working iterative program with custom stack (full working) 编辑 :使用自定义堆栈的工作迭代程序(完整工作)

Here's a sample of the final code ! 这是最终代码的示例!

int     sub_gen(int *pos, int **stack, int istack, int i)
{
  if (i == 4)
    {
      pos[0] = stack[istack][0];
      pos[1] = stack[istack][1];
      stack[istack][0] = -1;
      stack[istack][1] = -1;
      istack -= 1;
    }
  else
    istack += 1;
  return (istack);
}

void    recursive_gen(char **map, t_size size)
{
  int   *pos;
  int   *dirs;
  int   **stack;
  int   i;
  int   istack;

  istack = 0;
  pos = alloc_pos();
  stack = alloc_stack(size);
  while (stack[0][0] != -1)
    {
      dirs = gen_random_dirs();
      i = -1;
      while (++i < 4)
    if ((dirs[i] == 1 && up(map, pos, stack, istack) == 1) ||
            (dirs[i] == 2 && right(map, pos, size, stack, istack) == 1) ||
            (dirs[i] == 3 && down(map, pos, size, stack, istack) == 1) ||
            (dirs[i] == 4 && left(map, pos, stack, istack) == 1))
          break ;
      istack = sub_gen(pos, stack, istack, i);
    }
}

and up function 和功能

int     up(char **map, int *pos, int **stack, int i)
{
  if (pos[1] - 2 < 0)
    return (0);
  if (map[pos[1] - 2][pos[0]] == 'X')
    {
      map[pos[1] - 1][pos[0]] = '*';
      map[pos[1] - 2][pos[0]] = '*';
      pos[1] -= 2;
      stack[i + 1][0] = pos[0];
      stack[i + 1][1] = pos[1];
      return (1);
    }
  return (0);
}

I can upload the full code on github if someone's interested ! 如果有兴趣的话,我可以在github上传完整的代码!

The stack space is usually small and wont be enough to hold lots of stack frames from all the recursive calls. 堆栈空间通常很小,并不足以容纳所有递归调用的大量堆栈帧。 But the heap on the other hand has lots of space (Almost all of your virtual address space). 但另一方面,堆有很多空间(几乎所有的虚拟地址空间)。

So you can create a custom stack there which just holds the relevant data on it. 因此,您可以在其中创建一个自定义堆栈,其中只包含相关数据。

Then you cam use a while loop to process each instance on the stack. 然后你使用while循环来处理堆栈上的每个实例。 Your code is a version of DFS. 您的代码是DFS的一个版本。 Look up how you can do DFS without recursion. 查看如何在没有递归的情况下执行DFS。

The basic idea is that you start with an empty stack and push the first coordinate on it. 基本思想是从一个空堆栈开始并按下它上面的第一个坐标。

Then repeat the following steps till there are elements on the stack (use a while loop). 然后重复以下步骤,直到堆栈上有元素(使用while循环)。

  1. Pop an element from the stack 从堆栈中弹出一个元素
  2. Perform the operation for that point 执行该点的操作
  3. Add neighbours to the stack if they meet the condition (similar to what you used in recursion. Hint : See when do you call recursion, what condition do you check). 如果满足条件,则将邻居添加到堆栈中(类似于递归中使用的那些。提示:查看何时调用递归,您检查的条件是什么)。
  4. Repeat if stack not empty. 如果堆栈不为空则重复。

There is yet another way if you want to avoid all the code but are ready to sacrifice portability. 如果你想避免所有代码但又准备牺牲可移植性,还有另一种方法。

You can allocate some space on the heap (order of 100s of MBs) and make that your call stack by setting the stack pointer to that. 您可以在堆上分配一些空间(100个MB的顺序),并通过将堆栈指针设置为那个来调用堆栈。 Then start your recursion. 然后开始递归。 After the recursion is done switch back to original stack. 递归完成后切换回原始堆栈。

Remember though you might have to change the Thread Environment Block's field to update the stack limit and stack base because the libraries may check against them to check if the stack is in the limit, or has overflowed. 请记住,您可能必须更改线程环境块的字段以更新堆栈限制和堆栈基础,因为库可能会检查它们以检查堆栈是否在限制内,或者是否已溢出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM