We're simulating a public health scenario: the
spread of an infection.
We will simplify for the sake of exposition and ease
of programming. For example, a realistic simulation will
also feature the period of infectivity (after which
an infected person becomes normal), but in ours once
a person is infected, they stay infected.
We start with N particles representing N people (or
animals), one of which is infected.
The N particles move about randomly to simulate random
contact between individuals.
If an uninfected particle comes in close contact
with an infected one, it gets infected.
As the simulation showed, it takes a while for the
infection to spread.
Our goal is to study some quantitative aspects
of the spread:
Given a fixed size population of N, how long before
half the population gets infected? Is this a fixed
number that does not change with N? If it does change
with N, how does it change?
Suppose we fix N.
What fraction of the population is infected after 10
days, after 20 days, and so on? Does this increase
gradually or rapidly?
Note: simplifying the simulation at this time is fine
because we can always come back and modify the simulation
to add realism.
3.2 Exercise:
Before we study these two questions, do you have any
intuition at this point?
Let's now examine some of the code:
public class Simulation {
// Number of particles (N), the xy location of each, and color.
static int N = 10;
static double[] x;
static double[] y;
static boolean[] isRed;
// The bounds of the region.
static double size = 10;
static int numSteps = 100;
// How close do you have to be to get infected?
static double infectionDistance = 0.5;
public static void main (String[] argv)
{
DrawTool.display ();
DrawTool.setXYRange (0, size, 0, size);
DrawTool.startAnimationMode ();
initialize ();
for (int step=1; step<=numSteps; step++) {
// Spread.
infectionSpread ();
// Move each particle and draw
for (int i=0; i<N; i++) {
move (i);
draw (i);
}
DrawTool.animationPause (100);
}
DrawTool.endAnimationMode ();
}
Note:
We're using two arrays to track the location
of each particular particle.
static double[] x;
static double[] y;
Here
x[i]
will have the x coordinate of the i-th particle,
and
[i]
will have its y coordinate.
We use a Boolean array to track whether or not (true or false)
a particular particle is infected ("red" indicates infected):
static boolean[] isRed;
When an uninfected particle is close enough to an infected
one, it gets infected. This "close enough" distance is stored here:
static double infectionDistance = 0.5;
The main loop is quite simple:
for (int step=1; step<=numSteps; step++) {
infectionSpread ();
for (int i=0; i<N; i++) {
move (i);
draw (i);
}
}
At each step, we determine which particles get infected, then
we move each particle randomly, and update the drawing.
All that's left is to understand
the
infectionSpread()
and
move()
methods.
Let's take a closer look at the
infectionSpread()
and
move()
methods:
The main idea is:
for each particle {
compute the distance to each red particle
if it's close enough, set the color of this one to red
}
This idea turns into:
for (int n=0; n<N; n++) { // for each particle...
if (! isRed[n]) { // if it's not infected
// See if this is near some other red
for (int m=0; m<N; m++) {
// We must cycle through all to know which are red
if (isRed[m]) {
// Check distance.
double d = distance (x[n],y[n], x[m], y[m]);
if (d < infectionDistance) {
isRed[n] = true;
}
}
}
}
}
Two random values (such as 0.63 and -0.19) are generated.
These are added to
x[i]
and
y[i]
respectively.
Which has the effect of changing the location of the i-th point.
We could reduce the range to (-0.5,0.5) if we wanted
smoother (less jumpy, but slower) motion.
We've created the "helper" method
randomDouble()
to generate a random number within a specified range.
Let's point out something about animation that will be useful later:
The effect of animation is created when there is a deliberate
pause in time between movements.
To see this, change the pause time from 100 to 10 in
the main loop.
Animation, however, is not what's desired if we want
to run the simulation for purely statistical purposes (next section).
⇒
It slows down overall execution.
Step 2: Write code to specialize the simulation
You may have noticed two incomplete methods in
Simulation.java:
public static int simulateNumSteps (
int numParticles, // Desired # particles
double infDist, // Infection distance
int targetCount) // Simulate until so many infected.
{
// INSERT YOUR CODE HERE
}
public static int simulateNumInfected (
int numParticles, // Desired # particles
double infDist, // Infection distance
int desiredNumSteps) // Simulate for this many steps
{
// INSERT YOUR CODE HERE
}
Let's discuss these next:
Before explaining, notice the different style we've
used to write the parameter variables of the methods.
⇒
It's easier to read when there are many parameters.
Your goal here is to remove drawing and animation altogether
and simulate movement and infection spread in two specific ways.
In the first method, you are given as input:
The number of particles.
The infection distance (we've used 0.5).
A number that tells you when to stop the simulation: when
targetCount
particles are infected.
Accordingly, write a different version of the main loop here
without drawing and animation and that simulates until
targetCount
particles are infected. What you return is the number of steps
it took.
In the second method, you simulate for a given number
of steps. You return the number of particles that got infected
during this time.
The reasons for writing these two methods will become clear
in the next section.
3.3 Exercise:
Implement the above methods.
3.4 Audio:
Step 3: Write code to study the research questions
Recall the research questions:
Given population N, how long does it take for half the
population to get infected? How does this vary with N? (Plot a graph.)
For fixed N, how does the number infected vary with
increasing time.
To pursue these questions, we want to:
simulate many times so that we get
a statistically reasonable average
Plot a curve so that we get a qualitative sense of the
behavior: does it change linearly, slow, fast, what?
3.5 Exercise:
Download
SimulationStudy.java
and examine the file. Then proceed with completing the
two methods.
Let's look at the details for the above exercise:
There are two studies to be performed, with one method for each.
When you've completed the first study, you can comment it
out, and work on the second one.
Most of the code is already given to you.
What you need to do is write a loop that calls the
methods you wrote in
Simulation.java
and calls them repeatedly to take an average.
3.6 Audio:
3.7 Exercise:
Which one of these studies qualifies as "linear" and which
one is "nonlinear"?
About simulations:
What we've seen is one of many types of simulation.
One can simulate many types of behaviors: the motion of
planets, falling raindrops, internet traffic, social phenomena ...
the list is long.
A simulation is a powerful tool to explore with curiosity
as well as concretely answer questions.
It turns out that the code for many kinds of simulations is
actually not long, but often intricate.
Writing simulations is a specialized form of programming.
And it's fun, because you get to see all kinds of cool
results.