4

私はProcessingでいくつかのアルゴリズムを視覚的に表示しようとしています(しかし、実際には、この質問はGUIフレームワークを備えたすべての言語に当てはまります)。私の質問は、1秒あたりのフレーム数に基づいて更新される描画ループを備えたプログラムですが、アルゴリズムを実行してその視覚的表示をどのように更新しますか?

例として、ランダムな値の配列を棒グラフとして表示する場合があります。さまざまな並べ替え(バブルソート、クイックソート、マージソート)を使用してこの配列を並べ替えたいと思います。新しいアニメーションフレームで各スワップを表示して、アルゴリズムの各ステップを表示するにはどうすればよいですか?

または別の例はパスファインディングです。最終的なソリューションパス(検索されたすべてのパット)だけでなく、BFS、DFS、A*のステップを示したいと思います。

私が探している視覚的影響の例は、これらのアルゴリズムに関するウィキペディアの記事にあります。

http://en.wikipedia.org/wiki/Quicksort

http://en.wikipedia.org/wiki/File:Astar_progress_animation.gif

問題は、アルゴリズムが終了するまでに、描画ループが1回しかループしていないため、ユーザーに進行状況が表示されないことです。

私が考えることができる唯一のことは、アルゴリズムの各ステップに関連する値を含む「State」オブジェクトを持つことです。終了したら、各状態をループして視覚的に表示します。もっと良い方法はありますか?

4

4 に答える 4

2
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

/**
 * A panel that can show a demonstration of Quicksort in action. The items to be
 * sorted are the hues of a set of vertical bars. When sorted, the bars form a
 * spectrum from red to violet. Initially, the bars are sorted. There is a Start
 * button. When the user clicks this button, the order of the bars is randomized
 * and then Quicksort is applied. During the sort, a black bar marks the
 * location of an empty space from which the "pivot" element has been removed.
 * The user can abort the sort by clicking the button again.
 * <p>
 * The main point of this program is to demonstrate threads, with very simple
 * inter-thread communication. The recursive Quicksort algorithm is run in a
 * separate thread. The abort operation is implemented by setting the value of a
 * volatile variable that is checked periodically by the thread. When the user
 * aborts the sort before it finishes, the value of the variable changes; the
 * thread sees the change and exits.
 * <p>
 * This class contains a main() routine that allows the demo to be run as a
 * stand-alone application.
 */
public class QuicksortThreadDemo extends JPanel {

    /**
     * This main routine just shows a panel of type QuicksortThreadDemo.
     */
    public static void main(String[] args) {
        JFrame window = new JFrame("Demo: Recursion in a Thread");
        QuicksortThreadDemo content = new QuicksortThreadDemo();
        window.setContentPane(content);
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.pack();
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        window.setLocation((screenSize.width - window.getWidth()) / 2,
                (screenSize.height - window.getHeight()) / 2);
        window.setVisible(true);
    }

    private final static int ARRAY_SIZE = 150; // The number of colored bars.

    private int[] hue = new int[ARRAY_SIZE]; // The array that will be sorted.
    private Color[] palette = new Color[ARRAY_SIZE]; // Colors in spectral
                                                        // order.
    private Display display; // The panel that displays the colored bars.
    private JButton startButton; // The button that starts and stops the demo.

    private Runner runner; // The thread that runs the recursion.

    private volatile boolean running; // Set to true while recursion is running;
                                        // This is set to true as a signal to
                                        // the
                                        // thread to abort.

    /**
     * When the user aborts the recursion before it finishes, an exception of
     * this type is thrown to end the recursion cleanly.
     */
    private class ThreadTerminationException extends RuntimeException {
    }

    /**
     * A subpanel of type Display shows the colored bars that are being sorted.
     * The current pivot, if any, is shown in black. A 3-pixel gray border is
     * left around the bars.
     */
    private class Display extends JPanel {
        Display() {
            setPreferredSize(new Dimension(606, 206));
            setBackground(Color.GRAY);
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            double barWidth = (double) (getWidth() - 6) / hue.length;
            int h = getHeight() - 6;
            for (int i = 0; i < hue.length; i++) {
                int x1 = 3 + (int) (i * barWidth + 0.49);
                int x2 = 3 + (int) ((i + 1) * barWidth + 0.49);
                int w = x2 - x1;
                if (hue[i] == -1)
                    g.setColor(Color.BLACK);
                else
                    g.setColor(palette[hue[i]]);
                g.fillRect(x1, 3, w, h);
            }
        }
    }

    /**
     * The constructor sets up the panel, containing the Display and the Start
     * button below it.
     */
    public QuicksortThreadDemo() {
        for (int i = 0; i < ARRAY_SIZE; i++) {
            palette[i] = Color.getHSBColor((i * 230) / (ARRAY_SIZE * 255.0F),
                    1, 1);
            hue[i] = i;
        }
        setLayout(new BorderLayout());
        display = new Display();
        add(display, BorderLayout.CENTER);
        startButton = new JButton("Start");
        JPanel bottom = new JPanel();
        bottom.add(startButton);
        bottom.setBackground(Color.WHITE);
        add(bottom, BorderLayout.SOUTH);
        startButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                if (running)
                    stop();
                else
                    start();
            }
        });
    }

    /**
     * This method is called when the user clicks the Start button, while no
     * thread is running. It starts a new thread and sets the signaling
     * variable, running, to true; Also changes the text on the Start button to
     * "Finish".
     */
    private void start() {
        startButton.setText("Finish");
        runner = new Runner();
        running = true; // Set the signal before starting the thread!
        runner.start();
    }

    /**
     * This method is called when the user clicks the button while a thread is
     * running. A signal is sent to the thread to terminate, by setting the
     * value of the signaling variable, running, to false.
     */
    private void stop() {

        /*
         * Set the value of the signaling variable to false as a signal to the
         * thread to terminate.
         */

        running = false;

        /*
         * Wake the thread, in case it is sleeping, to get a more immediate
         * reaction to the signal.
         */

        runner.interrupt();

        /*
         * Wait for the thread to stop before setting runner = null. One second
         * should be plenty of time for this to happen, but in case something
         * goes wrong, it's better not to
         */

        try {
            runner.join(1000); // Wait for thread to stop. One second should be
                                // plenty of time.
        } catch (InterruptedException e) {
        }

        runner = null;
    }

    /**
     * This method is called frequently by the thread that is running the
     * recursion, in order to insert delays. It calls the repaint() method of
     * the display to allow the user to see what is going on; the delay will
     * give the system a chance to actually update the display. Since this
     * method is called regularly while the recursion is in progress, it is also
     * used as a convenient place to check the value of the signaling variable,
     * running. If the value of running has been set to false, this method
     * throws an exception of type ThreadTerminatedException. This exception
     * will cause all active levels of the recursion to be terminated. It is
     * caught in the run() method of the thread.
     * 
     * @param millis
     *            The number of milliseconds to sleep.
     */
    private void delay(int millis) {
        if (!running)
            throw new ThreadTerminationException();
        display.repaint();
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
        }
        if (!running)
            throw new ThreadTerminationException();
    }

    /**
     * The basic non-recursive QuickSortStep algorithm, which uses hue[lo] as a
     * "pivot" and rearranges elements of the hue array from positions lo
     * through hi so that positions the pivot value is in its correct location,
     * with smaller items to the left and bigger items to the right. The
     * position of the pivot is returned. In this version, we conceptually
     * remove the pivot from the array, leaving an empty space. The space is
     * marked by a -1, and it moves around as the algorithm proceeds. It is
     * shown as a black bar in the display. Every time a change is made, the
     * delay() method is called to insert a 1/10 second delay to let the user
     * see the change.
     */
    private int quickSortStep(int lo, int hi) {
        int pivot = hue[lo]; // Save pivot item.
        hue[lo] = -1; // Mark location lo as empty.
        delay(100);
        while (true) {
            while (hi > lo && hue[hi] > pivot)
                hi--;
            if (hi == lo)
                break;
            hue[lo] = hue[hi]; // Move hue[hi] into empty space.
            hue[hi] = -1; // Mark location hi as empty.
            delay(100);
            while (lo < hi && hue[lo] < pivot)
                lo++;
            if (hi == lo)
                break;
            hue[hi] = hue[lo]; // Move hue[lo] into empty space.
            hue[lo] = -1; // Mark location lo as empty.
            delay(100);
        }
        hue[lo] = pivot; // Move pivot item into the empty space.
        delay(100);
        return lo;
    }

    /**
     * The recursive quickSort algorithm, for sorting the hue array from
     * positions lo through hi into increasing order. Most of the actual work is
     * done in quickSortStep().
     */
    private void quickSort(int lo, int hi) {
        if (hi <= lo)
            return;
        int mid = quickSortStep(lo, hi);
        quickSort(lo, mid - 1);
        quickSort(mid + 1, hi);
    }

    /**
     * This class defines the treads that run the recursive QuickSort algorithm.
     * The thread begins by randomizing the array. It then calls quickSort() to
     * sort the entire array. If quickSort() is aborted by a
     * ThreadTerminationExcpetion, which would be caused by the user clicking
     * the Finish button, then the thread will restore the array to sorted order
     * before terminating, so that whether or not the quickSort is aborted, the
     * array ends up sorted.
     */
    private class Runner extends Thread {
        public void run() {
            try {
                for (int i = hue.length - 1; i > 0; i--) { // Randomize array.
                    int r = (int) ((i + 1) * Math.random());
                    int temp = hue[r];
                    hue[r] = hue[i];
                    hue[i] = temp;
                }
                delay(1000); // Wait one second before starting the sort.
                quickSort(0, hue.length - 1); // Sort the whole array.
            } catch (ThreadTerminationException e) { // User aborted quickSort.
                for (int i = 0; i < hue.length; i++)
                    hue[i] = i;
            } finally {// Make sure running is false and button label is
                        // correct.
                running = false;
                startButton.setText("Start");
                display.repaint();
            }
        }
    }

} // end QuicksortThreadDemo

`

于 2012-10-17T05:49:55.520 に答える
1

それを行う方法 (最善の方法ではないかもしれません) は、個別のステップのコードを記述し、sikuli またはその他の GUI スクリプトを使用して自動出力を行うことだと思います。

または、単純に MATLAB スクリプトでコードを記述し、+ および - ボタンを使用して次の反復を表示します。

于 2012-10-17T03:20:12.453 に答える
1

次の 2 つの方法があります。

変数でインクリメントできるようにアルゴリズムを書き直してください。次に、描画の「ループ」の各反復をインクリメントします。これは、処理でこのアプローチを試している人の例です。

http://www.processing.org/discourse/alpha/board_Programs_action_display_num_1059766998.html

オフスクリーン バッファに書き込み、ループの各フレーム/反復を保存してから、後で表示します。上記の例のバブル ソート アルゴリズムとフレームごとに描画するメソッドは残してありますので、バッファを使用するように構造をどのように変更したかがわかります。

int[] myArray;
PGraphics pg;
ArrayList<PImage> frameBufferList;

void setup() {
 size(400, 400);
 frameRate(15);
 frameBufferList = new ArrayList<PImage>();
 myArray = randomArray(20);
 bubbleSortSaveFrame(myArray);
}

void draw() {
  background(127);
  image(frameBufferList.get(frameCount % frameBufferList.size()), 0, 0);
}

int[] randomArray(int numOfElements) {
  int[] returnArray = new int[numOfElements];
  for (int i = 0; i < returnArray.length; i++) {
    returnArray[i] = floor(random(0, height));
  }
  return returnArray;
}

void displayArray(int[] arrToDisplay) {
  float step = float(width) / float(arrToDisplay.length);
  for (int i = 0; i < arrToDisplay.length; i++) {
    rectMode(CORNERS);
    rect(i*step, height, (i+1)*step, height-arrToDisplay[i]);
  }
}

void displayArrayBuffer(int[] arrToDisplay) {
  pg = createGraphics(width, height, P2D); // setup buffer
  pg.beginDraw(); // start buffer
  pg.background(127);  
  float step = float(width) / float(arrToDisplay.length);
  for (int i = 0; i < arrToDisplay.length; i++) {
    pg.rectMode(CORNERS);
    pg.rect(i*step, height, (i+1)*step, height-arrToDisplay[i]);
  }
  pg.endDraw(); // end buffer
  frameBufferList.add(pg);
}

void bubbleSort(int array[]) 
{ 
  int i, j; 
  boolean sorted = false;  
  // while the array isn't sorted 
  while (!sorted) { 
    sorted = true; 
    // loop through array 
    for (i=0;i<array.length-1;i++) { 
     if (array[i] > array[i+1]) { 
       // swap values 
       j = array[i]; 
       array[i] = array[i+1]; 
       array[i+1] = j; 
       //println(array); 
       // trigger's been hit, array isn't sorted. 
       sorted = false; 
     } 
    } 
  } 
} 

void bubbleSortSaveFrame(int array[]) 
{ 
  int i, j; 
  boolean sorted = false; 
  // while the array isn't sorted 
  while (!sorted) { 
    sorted = true; 
    // loop through array 
    for (i=0;i<array.length-1;i++) { 
             displayArrayBuffer(array); // save the frame
     if (array[i] > array[i+1]) { 
       // swap values 
       j = array[i]; 
       array[i] = array[i+1]; 
       array[i+1] = j; 
       //println(array); 
       // trigger's been hit, array isn't sorted. 
       sorted = false; 
     } 
    } 
  }
} 

注: 通常、フレームごとに 1 つのバッファを使用します。この場合、保存する各画像に対してバッファを作成するため、何千もの画像を作成するとメモリ不足になる可能性があります。メモリにヒットせずに PGraphics / buffer を使用する別の解決策がある人はいますか?

于 2012-10-17T19:10:08.850 に答える
0

あなたの質問に対する一般的な答えはありません (gr8 question btw)。アルゴリズムに依存します。バブルソートを例に考えてみましょう。このアルゴリズムには、終了するまでの有限数のステップがあります (つまり、最悪の場合のパフォーマンスを想定できます)。

ウィキペディアから盗んだ:

  procedure bubbleSort( A : list of sortable items )
     repeat     
       swapped = false
       for i = 1 to length(A) - 1 inclusive do:
         /* if this pair is out of order */
         if A[i-1] > A[i] then
           /* swap them and remember something changed */
           swap( A[i-1], A[i] )
           swapped = true
         end if
       end for
     until not swapped
 end procedure

(最悪の場合)repeatループに n 個のループ、forループに n 個のループがあることはわかっています(n 乗の複雑さ)。for のループは高速で、ループごとにステータス バーを更新する必要はありません (ユーザーは進行状況を確認できず、GUI でさえ for のすべてのステータスを更新するには十分に高速ではありません)。したがって、進捗ステップは n/100 になります。各 while ループの最後に、その進行状況によってステータス バーを更新し、繰り返しが終了したら、100% に更新します。
各アルゴリズムを分析し、それぞれについてこのような解決策を見つける必要があります。私が言ったように、一般的な魔法の解決策はありません。

于 2012-10-17T05:24:31.037 に答える