1. 程式人生 > >《演算法》第六章部分程式 part 1

《演算法》第六章部分程式 part 1

▶ 書中第六章部分程式,包括在加上自己補充的程式碼,粒子碰撞系統及用到的粒子類

● 粒子系統

  1 package package01;
  2 
  3 import java.awt.Color;
  4 import edu.princeton.cs.algs4.StdIn;
  5 import edu.princeton.cs.algs4.StdDraw;
  6 import edu.princeton.cs.algs4.MinPQ;
  7 import edu.princeton.cs.algs4.Particle;
  8 
  9 public class class01
10 { 11 private static final double HZ = 0.5; // 每個時間步長內重畫的次數 12 private MinPQ<Event> pq; // 優先佇列 13 private double t = 0.0; // 計時器 14 private Particle[] particles; //
粒子資料列表 15 16 private static class Event implements Comparable<Event> // 碰撞事件類 17 { 18 private final double time; // 預計事件發生時間 19 private final Particle a, b; // 事件中的粒子 20 private
final int countA, countB; // 粒子在加入事件中時的已碰撞的次數 21 22 public Event(double inputT, Particle inputA, Particle inputB) // 輸入當前時間和兩個粒子,計算碰撞事件 23 { 24 time = inputT; 25 a = inputA; 26 b = inputB; 27 countA = (a != null) ? a.count() : -1; 28 countB = (b != null) ? b.count() : -1; 29 } 30 31 public int compareTo(Event that) // 比較兩個事件哪個先發生 32 { 33 return Double.compare(this.time, that.time); 34 } 35 36 public boolean isValid() // 判斷事件是否有效 37 { 38 return (a == null || a.count() == countA) && (b == null || b.count() == countB);// 原始碼簡化版 39 //if (a != null && a.count() != countA || b != null && b.count() != countB) // 原始碼,只要有粒子當前實際碰撞數與事件中記錄的 count 不等,說明事件失效 40 // return false; 41 //return true; 42 } 43 } 44 45 public class01(Particle[] inputParticle) 46 { 47 particles = inputParticle.clone(); // 輸入列表的深拷貝 48 } 49 50 private void predict(Particle a, double limit) // 更新優先佇列中關於粒子 a 的事件 51 { 52 if (a == null) 53 return; 54 for (int i = 0; i < particles.length; i++) // 預測 a 與各粒子碰撞的時間,只要時間小於閾值就將其放入優先佇列 55 { 56 double targetTime = t + a.timeToHit(particles[i]); 57 if (targetTime <= limit) 58 pq.insert(new Event(targetTime, a, particles[i])); 59 } 60 double targetTimeX = t + a.timeToHitVerticalWall(), targetTimeY = t + a.timeToHitHorizontalWall(); 61 if (targetTimeX <= limit) 62 pq.insert(new Event(targetTimeX, a, null)); 63 if (targetTimeY <= limit) 64 pq.insert(new Event(targetTimeY, null, a)); 65 } 66 67 private void redraw(double limit) // 重畫所有粒子位置 68 { 69 StdDraw.clear(); 70 for (int i = 0; i < particles.length; i++) 71 particles[i].draw(); 72 StdDraw.show(); 73 StdDraw.pause(20); // 暫停 20 ms 74 if (t < limit) // 還沒到時限,加入重畫事件 75 pq.insert(new Event(t + 1.0 / HZ, null, null)); 76 } 77 78 public void simulate(double limit) // 模擬器 79 { 80 pq = new MinPQ<Event>(); 81 for (int i = 0; i < particles.length; i++) // 首次計算所有粒子之間的碰撞 82 predict(particles[i], limit); 83 for (pq.insert(new Event(0, null, null)); !pq.isEmpty();) // 多加入一個重畫所有粒子位置的事件 84 { 85 Event e = pq.delMin(); // 取出發生時間最近的時間,判斷是否有效 86 if (!e.isValid()) 87 continue; 88 Particle a = e.a, b = e.b; 89 for (int i = 0; i < particles.length; i++) // 更新所有粒子位置 90 particles[i].move(e.time - t); 91 t = e.time; // 更新當前時間 92 93 if (a != null && b != null) // 粒子 - 粒子碰撞 94 a.bounceOff(b); 95 else if (a != null && b == null) // 粒子撞豎直牆壁 96 a.bounceOffVerticalWall(); 97 else if (a == null && b != null) 98 b.bounceOffHorizontalWall(); // 粒子撞水平牆壁 99 else if (a == null && b == null) 100 redraw(limit); // 僅重畫所有粒子位置 101 predict(a, limit); // 重新預測 a 與 b相關的事件 102 predict(b, limit); 103 } 104 } 105 106 public static void main(String[] args) 107 { 108 StdDraw.setCanvasSize(600, 600); // 視窗大小 109 StdDraw.enableDoubleBuffering(); // double 緩衝區 110 Particle[] particles; // 粒子列表 111 112 if (args.length == 1) // 輸入一個引數,生成相應個數的個粒子 113 { 114 int n = Integer.parseInt(args[0]); 115 particles = new Particle[n]; 116 for (int i = 0; i < n; i++) 117 particles[i] = new Particle(); 118 } 119 else // 否則按標準輸入流依次輸入每個粒子的資訊 120 { 121 int n = StdIn.readInt(); 122 particles = new Particle[n]; 123 for (int i = 0; i < n; i++) 124 { 125 double rx = StdIn.readDouble(); 126 double ry = StdIn.readDouble(); 127 double vx = StdIn.readDouble(); 128 double vy = StdIn.readDouble(); 129 double radius = StdIn.readDouble(); 130 double mass = StdIn.readDouble(); 131 int r = StdIn.readInt(); 132 int g = StdIn.readInt(); 133 int b = StdIn.readInt(); 134 Color color = new Color(r, g, b); 135 particles[i] = new Particle(rx, ry, vx, vy, radius, mass, color); 136 } 137 } 138 class01 system = new class01(particles); // 模擬和輸出 139 system.simulate(10000); // 模擬事件 10s 140 } 141 }

● 粒子類

package package01;

import java.awt.Color;
import edu.princeton.cs.algs4.StdDraw;
import edu.princeton.cs.algs4.StdRandom;

public class class01
{
    private static final double INFINITY = Double.POSITIVE_INFINITY;

    private double rx, ry;
    private double vx, vy;
    private int count;              // 粒子已經碰撞的次數
    private final double radius;
    private final double mass;
    private final Color color;

    public class01(double inputRx, double inputRy, double inputVx, double inputVy, double inputRadius, double inputMass, Color inputColor)
    {
        rx = inputRx;
        ry = inputRy;
        vx = inputVx;
        vy = inputVy;
        radius = inputRadius;
        mass = inputMass;
        color = inputColor;
    }

    public class01()
    {
        rx = StdRandom.uniform(0.0, 1.0);
        ry = StdRandom.uniform(0.0, 1.0);
        vx = StdRandom.uniform(-0.005, 0.005);
        vy = StdRandom.uniform(-0.005, 0.005);
        radius = 0.02;
        mass = 0.5;
        color = Color.BLACK;
    }

    public void move(double dt)
    {
        rx += vx * dt;
        ry += vy * dt;
    }

    public void draw()              // 繪製粒子
    {
        StdDraw.setPenColor(color);
        StdDraw.filledCircle(rx, ry, radius);
    }

    public int count()
    {
        return count;
    }

    public double timeToHit(class01 that)
    {
        if (this == that)
            return INFINITY;
        double dx = that.rx - rx, dy = that.ry - ry, dvx = that.vx - vx, dvy = that.vy - vy;
        double dvdr = dx * dvx + dy * dvy;
        if (dvdr > 0)                   // Δx 與 Δv 同號,不會撞
            return INFINITY;
        double dvdv = dvx * dvx + dvy * dvy;
        if (dvdv == 0)                  // 速度完全相等,不會撞
            return INFINITY;
        double drdr = dx * dx + dy * dy;
        double dist = radius + that.radius;
        double d = (dvdr*dvdr) - dvdv * (drdr - dist * dist);
        return (d > 0) ? -(dvdr + Math.sqrt(d)) / dvdv : INFINITY;
    }

    public double timeToHitVerticalWall()
    {
        return (vx > 0) ? (1.0 - rx - radius) / vx : ((vx < 0) ? (radius - rx) / vx : INFINITY);
    }

    public double timeToHitHorizontalWall()
    {
        return (vy > 0) ? (1.0 - ry - radius) / vy : ((vy < 0) ? (radius - ry) / vy : INFINITY);
    }

    public void bounceOff(class01 that)
    {
        double dx = that.rx - rx, dy = that.ry - ry;
        double dvx = that.vx - vx, dvy = that.vy - vy;
        double dvdr = dx * dvx + dy * dvy;
        double dist = radius + that.radius;
        double magnitude = 2 * mass * that.mass * dvdr / ((mass + that.mass) * dist);
        double fx = magnitude * dx / dist, fy = magnitude * dy / dist;
        vx += fx / mass;
        vy += fy / mass;
        that.vx -= fx / that.mass;
        that.vy -= fy / that.mass;
        count++;
        that.count++;
    }

    public void bounceOffVerticalWall()
    {
        vx = -vx;
        count++;
    }

    public void bounceOffHorizontalWall()
    {
        vy = -vy;
        count++;
    }

    public double kineticEnergy()
    {
        return 0.5 * mass * (vx*vx + vy * vy);
    }
}