我可以使用Processing一步一步地绘制回溯吗?

我可以使用Processing一步一步地绘制回溯吗?,processing,backtracking,Processing,Backtracking,我想使用processing编写一个回溯-8 Queens-可视化代码 所以我尝试在安装程序中使用noLoop,并在每个更新步骤中调用redraw和delay100,但没有成功 这是我的职责 int cellH = 38, cellW = 38, n = 8; PImage img; boolean [][] grid; boolean [] visC, visMD, visSD; boolean firstTime = true; void drawQueen(int r, int c){

我想使用processing编写一个回溯-8 Queens-可视化代码

所以我尝试在安装程序中使用noLoop,并在每个更新步骤中调用redraw和delay100,但没有成功

这是我的职责

int cellH = 38, cellW = 38, n = 8;
PImage img;
boolean [][] grid;
boolean [] visC, visMD, visSD;
boolean firstTime = true;

void drawQueen(int r, int c){
  image(img, c*cellW, r*cellH, cellW, cellH);
}

void drawGrid(){
  background(255);
  for(int r = 0 ; r < n ; ++r){
    for(int c = 0 ; c < n ; ++c){
     if((r&1) != (c&1)) fill(0);
     else  fill(255);
     rect(c*cellW, r*cellH, (c+1)*cellW, (r+1)*cellH);
   }
  }
}

void updateQueens(){
  for(int r = 0 ; r < n ; ++r)
    for(int c = 0 ; c < n ; ++c)
      if(grid[r][c] == true)
        drawQueen(r, c);
}
boolean backTrack(int r){
 if(r == n)  return true;
 else{
   for(int c = 0 ; c < n ; ++c){
     if(!visC[c] && !visMD[n+r-c] && !visSD[r+c]){
       //Do
       grid[r][c] = visC[c] = visMD[n+r-c] = visSD[r+c] = true;
       redraw();
       delay(100);
       //Recurse
       if(backTrack(r+1))  return true;
       //Undo
       grid[r][c] = visC[c] = visMD[n+r-c] = visSD[r+c] = false;
     }
   }
 }
 return false;
}

void setup(){
  size(280, 280);
  cellH = 280/n;
  cellW = 280/n;

  grid = new boolean[n][n];
  visC = new boolean[n];
  visMD = new boolean[2*n];
  visSD = new boolean[2*n];

  noLoop();
  img = loadImage("queen.png");
  backTrack(0);
}

void draw(){
  drawGrid();
  updateQueens();
}
运行草图时,仅显示最终状态

还有其他的想法吗?

根据:

在构建程序时,只有在内部调用redraw才有意义 事件,例如鼠标按下。这是因为重画不会运行 立即绘制它仅设置一个标志,指示正在进行更新 需要

重画不会导致绘制屏幕。它设置了一个需要调用draw的标志,该标志发生在循环结束时。一种解决方案是将draw重命名为drawScreen,并调用它,而不是重新绘制。

根据:

在构建程序时,只有在内部调用redraw才有意义 事件,例如鼠标按下。这是因为重画不会运行 立即绘制它仅设置一个标志,指示正在进行更新 需要


重画不会导致绘制屏幕。它设置了一个需要调用draw的标志,该标志发生在循环结束时。一种解决方案是将draw重命名为drawScreen并调用它,而不是重新绘制。

处理的方式是,通过将draw函数转到该循环的主体来模拟循环,并在setup函数中执行所有初始化

为了模拟一个递归,你可以把它变成一个循环,然后做上面的事情,通常这可以用一个堆栈来完成,你基本上用你的堆栈替换系统的堆栈;我对一些想法做了一些阅读检查,我发现如果递归调用位于函数体的末尾,那么将递归转换为带有堆栈的循环将非常容易

现在的问题是,在调用返回后应该执行的递归调用之后,您有一些代码,但是查看它,它只是撤销对全局变量所做的更改,如果我们认为这些变量作为状态的一部分不是非常有效的,并且不能很好地扩展,那么我们可以克服它。但是在你的例子中,撤销部分是不必要的,递归调用将是函数体中的最后一件事,让我们暂时离开内部for循环

为此,我定义了一个名为State的类,如下所示

    class State {
      private final int SIZE = 8;
      private boolean grid[][], visC[], visR[], visMD[], visSD[];

      int r, c;

      State() {
        visC = new boolean[SIZE];
        visR = new boolean[SIZE];
        visMD = new boolean[2*SIZE];
        visSD = new boolean[2*SIZE];
        grid = new boolean[SIZE][SIZE];
      }

      State(State other) {
        this();
        cpyArr(other.visMD, this.visMD);
        cpyArr(other.visSD, this.visSD);
        cpyArr(other.visC, this.visC);
        cpyArr(other.visR, this.visR);

        for (int i = 0 ; i < other.grid.length ; ++i)
          for (int j = 0 ; j < other.grid[i].length ; ++j)
            this.grid[i][j] = other.grid[i][j];

        this.r = other.r;
        this.c = other.c;
      }

      void cpyArr(boolean from[], boolean to[]) {
        for (int i = 0 ; i < from.length ; ++i) to[i] = from[i];
      }

      boolean isValid(int r, int c) {
        return (r < SIZE && c < SIZE && !visR[r] && !visC[c] && !visMD[SIZE + r - c] && !visSD[r + c]);
      }

      // actually update this sate with r and c
      void affect() {
        grid[r][c] = visC[c] = visMD[SIZE + r - c] = visSD[r + c] = true;
      }

      PVector[] getPositions() {
        ArrayList<PVector> ret = new ArrayList<PVector>();
        for (int i = 0; i < SIZE; ++i)
          for (int j = 0; j < SIZE; ++j)
            if (grid[i][j]) ret.add(new PVector(j, i));
        return ret.toArray(new PVector[0]);
      }
    }

我们可以将绘制函数的主体看作是while循环的主体,在栈为空时使用NOLoop停止它,所以最后的代码将类似于…

stack.push(initialState);
while(stack.size() != 0) {
    State currentState = stack.pop();
    // do stuff ...
    stack.push(nextState);
}
import java.util.Stack;

final int GRID_SIZE = 8;
float cellH, cellW;
PImage img;

Stack<State> stack;

void setup() {
  size(500, 500);
  frameRate(5);

  cellH = (float) height / GRID_SIZE;
  cellW = (float) width / GRID_SIZE;

  img = loadImage("queen.png");

  stack = new Stack<State>();
  State state = new State();
  state.r = -1;
  state.c = -1;
  stack.push(state);

  noLoop();

}

void draw() {
  // stop if the stack is empty
  if (stack.size() == 0) {
    noLoop();
    return;
  }

  State current = stack.pop();
  drawGrid(current);

  // stop when a solution is found
  if (current.r == GRID_SIZE - 1) {
    noLoop();
    return;
  }

  for (int c = GRID_SIZE - 1; c >= 0; --c) {
    State next = new State(current);

    if (!next.isValid(current.r+1, c)) continue;
    next.c = c;
    next.r = current.r + 1;
    next.affect();

    stack.push(next);
  }

}

void drawGrid(State state) {
  float cellH = height / GRID_SIZE;
  float cellW = width / GRID_SIZE;

  background(255);
  for (int r = 0; r < GRID_SIZE; ++r) {
    for (int c = 0; c < GRID_SIZE; ++c) {
      if ((r&1) != (c&1)) fill(0);
      else  fill(255);
      rect(c*cellW, r*cellH, (c+1)*cellW, (r+1)*cellH);
    }
  }

  PVector pos[] = state.getPositions();
  for (PVector vec : pos) {
    image(img, vec.x * cellW + cellW * 0.1, vec.y * cellH + cellH * 0.1, cellW * 0.8, cellH * 0.8);
  }
}

// to resume the search after a solution is found
void keyPressed() {
  if (key == ' ') loop();
}
请注意,我们稍后留下的内部for循环部分是反向的,因此要执行的第一个状态与回溯将探索的第一个状态相同

现在在数据文件中为queen.png资源放置一些漂亮的图像,结果非常好


处理的方式是,通过将draw函数转到循环体来模拟循环,并在setup函数中执行所有初始化

为了模拟一个递归,你可以把它变成一个循环,然后做上面的事情,通常这可以用一个堆栈来完成,你基本上用你的堆栈替换系统的堆栈;我对一些想法做了一些阅读检查,我发现如果递归调用位于函数体的末尾,那么将递归转换为带有堆栈的循环将非常容易

现在的问题是,在调用返回后应该执行的递归调用之后,您有一些代码,但是查看它,它只是撤销对全局变量所做的更改,如果我们认为这些变量作为状态的一部分不是非常有效的,并且不能很好地扩展,那么我们可以克服它。但是在你的例子中,撤销部分是不必要的,递归调用将是函数体中的最后一件事,让我们暂时离开内部for循环

为此,我定义了一个名为State的类,如下所示

    class State {
      private final int SIZE = 8;
      private boolean grid[][], visC[], visR[], visMD[], visSD[];

      int r, c;

      State() {
        visC = new boolean[SIZE];
        visR = new boolean[SIZE];
        visMD = new boolean[2*SIZE];
        visSD = new boolean[2*SIZE];
        grid = new boolean[SIZE][SIZE];
      }

      State(State other) {
        this();
        cpyArr(other.visMD, this.visMD);
        cpyArr(other.visSD, this.visSD);
        cpyArr(other.visC, this.visC);
        cpyArr(other.visR, this.visR);

        for (int i = 0 ; i < other.grid.length ; ++i)
          for (int j = 0 ; j < other.grid[i].length ; ++j)
            this.grid[i][j] = other.grid[i][j];

        this.r = other.r;
        this.c = other.c;
      }

      void cpyArr(boolean from[], boolean to[]) {
        for (int i = 0 ; i < from.length ; ++i) to[i] = from[i];
      }

      boolean isValid(int r, int c) {
        return (r < SIZE && c < SIZE && !visR[r] && !visC[c] && !visMD[SIZE + r - c] && !visSD[r + c]);
      }

      // actually update this sate with r and c
      void affect() {
        grid[r][c] = visC[c] = visMD[SIZE + r - c] = visSD[r + c] = true;
      }

      PVector[] getPositions() {
        ArrayList<PVector> ret = new ArrayList<PVector>();
        for (int i = 0; i < SIZE; ++i)
          for (int j = 0; j < SIZE; ++j)
            if (grid[i][j]) ret.add(new PVector(j, i));
        return ret.toArray(new PVector[0]);
      }
    }

我们可以将绘制函数的主体看作是while循环的主体,在栈为空时使用NOLoop停止它,所以最后的代码将类似于…

stack.push(initialState);
while(stack.size() != 0) {
    State currentState = stack.pop();
    // do stuff ...
    stack.push(nextState);
}
import java.util.Stack;

final int GRID_SIZE = 8;
float cellH, cellW;
PImage img;

Stack<State> stack;

void setup() {
  size(500, 500);
  frameRate(5);

  cellH = (float) height / GRID_SIZE;
  cellW = (float) width / GRID_SIZE;

  img = loadImage("queen.png");

  stack = new Stack<State>();
  State state = new State();
  state.r = -1;
  state.c = -1;
  stack.push(state);

  noLoop();

}

void draw() {
  // stop if the stack is empty
  if (stack.size() == 0) {
    noLoop();
    return;
  }

  State current = stack.pop();
  drawGrid(current);

  // stop when a solution is found
  if (current.r == GRID_SIZE - 1) {
    noLoop();
    return;
  }

  for (int c = GRID_SIZE - 1; c >= 0; --c) {
    State next = new State(current);

    if (!next.isValid(current.r+1, c)) continue;
    next.c = c;
    next.r = current.r + 1;
    next.affect();

    stack.push(next);
  }

}

void drawGrid(State state) {
  float cellH = height / GRID_SIZE;
  float cellW = width / GRID_SIZE;

  background(255);
  for (int r = 0; r < GRID_SIZE; ++r) {
    for (int c = 0; c < GRID_SIZE; ++c) {
      if ((r&1) != (c&1)) fill(0);
      else  fill(255);
      rect(c*cellW, r*cellH, (c+1)*cellW, (r+1)*cellH);
    }
  }

  PVector pos[] = state.getPositions();
  for (PVector vec : pos) {
    image(img, vec.x * cellW + cellW * 0.1, vec.y * cellH + cellH * 0.1, cellW * 0.8, cellH * 0.8);
  }
}

// to resume the search after a solution is found
void keyPressed() {
  if (key == ' ') loop();
}
请注意,我们稍后留下的内部for循环部分是反向的,因此要执行的第一个状态与回溯将探索的第一个状态相同

现在在数据文件中为queen.png资源放置一些漂亮的图像,结果非常好


我试图用线程解决它,它给了我一个很好的输出,下面是我的代码:

int cellH = 38, cellW = 38, n = 8;
PImage img;
boolean [][] grid;
boolean [] visC, visMD, visSD;
boolean firstTime = true;

Thread thread;

void setup(){
  size(560, 560);
  cellH = 560/n;
  cellW = 560/n;

  grid = new boolean[n][n];
  visC = new boolean[n];
  visMD = new boolean[2*n];
  visSD = new boolean[2*n];
  img = loadImage("queen.png");
  thread = new Thread(new MyThread());

  thread.start();
}

void draw(){
  if(thread.isAlive())
    drawGrid();
  else{
    noLoop();
    endRecord();
    return;
  }
}

void drawGrid(){
  background(255);
  for(int r = 0 ; r < n ; ++r){
   for(int c = 0 ; c < n ; ++c){
     if((r&1) != (c&1)) fill(0);
     else  fill(255);
     rect(c*cellW, r*cellH, (c+1)*cellW, (r+1)*cellH);
     if(grid[r][c] == true)
       image(img, c*cellW, r*cellH, cellW, cellH);
   }
  }
}

boolean backTrack(int r){
  if(r == n)  return true;
  else{
    for(int c = 0 ; c < n ; ++c){
      if(!visC[c] && !visMD[n+r-c] && !visSD[r+c]){
        //Do
        grid[r][c] = visC[c] = visMD[n+r-c] = visSD[r+c] = true;

        try{
          Thread.sleep(200);
        }catch(InterruptedException e){System.out.println(e);}  

        //Recurse
        if(backTrack(r+1))  return true;
        //Undo
        grid[r][c] = visC[c] = visMD[n+r-c] = visSD[r+c] = false;

        try{
          Thread.sleep(200);
        }catch(InterruptedException e){System.out.println(e);}
      }
    }
  }
  return false;
}

class MyThread implements Runnable{    
  public void run(){    
    backTrack(0);    
  }
}  
以下是输出:


我试图用线程解决它,它给了我一个很好的输出,下面是我的代码:

int cellH = 38, cellW = 38, n = 8;
PImage img;
boolean [][] grid;
boolean [] visC, visMD, visSD;
boolean firstTime = true;

Thread thread;

void setup(){
  size(560, 560);
  cellH = 560/n;
  cellW = 560/n;

  grid = new boolean[n][n];
  visC = new boolean[n];
  visMD = new boolean[2*n];
  visSD = new boolean[2*n];
  img = loadImage("queen.png");
  thread = new Thread(new MyThread());

  thread.start();
}

void draw(){
  if(thread.isAlive())
    drawGrid();
  else{
    noLoop();
    endRecord();
    return;
  }
}

void drawGrid(){
  background(255);
  for(int r = 0 ; r < n ; ++r){
   for(int c = 0 ; c < n ; ++c){
     if((r&1) != (c&1)) fill(0);
     else  fill(255);
     rect(c*cellW, r*cellH, (c+1)*cellW, (r+1)*cellH);
     if(grid[r][c] == true)
       image(img, c*cellW, r*cellH, cellW, cellH);
   }
  }
}

boolean backTrack(int r){
  if(r == n)  return true;
  else{
    for(int c = 0 ; c < n ; ++c){
      if(!visC[c] && !visMD[n+r-c] && !visSD[r+c]){
        //Do
        grid[r][c] = visC[c] = visMD[n+r-c] = visSD[r+c] = true;

        try{
          Thread.sleep(200);
        }catch(InterruptedException e){System.out.println(e);}  

        //Recurse
        if(backTrack(r+1))  return true;
        //Undo
        grid[r][c] = visC[c] = visMD[n+r-c] = visSD[r+c] = false;

        try{
          Thread.sleep(200);
        }catch(InterruptedException e){System.out.println(e);}
      }
    }
  }
  return false;
}

class MyThread implements Runnable{    
  public void run(){    
    backTrack(0);    
  }
}  
以下是输出:


添加打印重绘时;在重新绘制之前;,它被打印了很多次吗?你能给我们看看你的drawGrid函数吗?@J.D.是的,它被打印了。@DWuest添加了完整的代码。@Rabbid76有什么诀窍吗?当你添加printlnRedrawing时;在重新绘制之前;,它印了很多次了吗?可以吗
请给我们看一下drawGrid函数?@J.D.是的,它会打印。@DWuest添加了完整的代码。@Rabbid76有什么诀窍吗?我不工作,文档上说:所有处理程序都会在绘图结束时更新屏幕,决不会更早。如果添加PrintLN调用绘图;以上重画和打印图纸;你会明白我的意思的。”“调用绘图”打印多次,最后“绘图”打印一次。现在将redraw重命名为draw-将其转换为直接函数调用,print语句将交替进行,这意味着每次更新都会被绘制。是的,单词会被打印出来,但不会更新场景。我不工作,文档中说:所有处理程序都会在绘制结束时更新屏幕,而不会更早。如果添加PrintlCalling draw;以上重画和打印图纸;你会明白我的意思的。”“调用绘图”打印多次,最后“绘图”打印一次。现在将redraw重命名为draw-将其转换为直接函数调用print语句将交替进行,这意味着每次更新都会被绘制。是的,单词会被打印,但不会更新场景。非常好的方法。它具有很好的通用性,可以用很少的编辑来可视化几乎任何算法。非常好的方法。它具有很好的通用性,并且可以用于可视化几乎任何只需很少编辑的算法。