Approximate a Bell Curve
Java
The method of generation is based off of the game Sugar, Sugar. In that game, sugar particles fall, traveling randomly left and right. In my program, Dust
objects fall until they hit the ground. I collect data based on where they land; this is the bell curve:
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.Stack;
import javax.imageio.ImageIO;
/**
*
* @author Quincunx
*/
public class ApproxBellCurve {
public static final int NUM_DUST = 10_000_000;
public static final int NUM_THREADS = Runtime.getRuntime().availableProcessors() - 1;
public static final int LOOP_LIMIT = NUM_DUST / (NUM_THREADS + 1);
public static void main(String[] args) {
BufferedImage output = new BufferedImage(2049, 2049, BufferedImage.TYPE_INT_RGB);
Stack<Thread> threads = new Stack();
for (int i = 0; i < NUM_THREADS; i++) {
threads.push(new Thread(new Duster()));
threads.peek().start();
}
Dust d;
Random r = new Random();
for (int i = 0; i < LOOP_LIMIT + NUM_DUST - (LOOP_LIMIT * (NUM_THREADS + 1)); i++) {
d = new Dust(r);
while (!d.end) {
d.step();
}
if ((i & 1024) == 0) {
r = new Random();
}
}
while (threads.size() > 0) {
try {
threads.pop().join();
} catch (InterruptedException ex) {
}
}
int maxy = 0;
for (int x = 0; x < Dust.data.length; x++) {
maxy = Dust.data[x] > maxy ? Dust.data[x] : maxy;
}
for (int x = 0; x < Dust.data.length; x++) {
for (int y = 0; y < output.getHeight(); y++) {
output.setRGB(x, y, (y >= output.getHeight() - (Dust.data[x] * output.getHeight() / maxy)) ? 6591981 : 16777215);
}
}
try {
ImageIO.write(output, "png", new File("Filepath"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
private static class Duster implements Runnable {
@Override
public void run() {
Dust d;
Random r = new Random();
for (int i = 0; i < LOOP_LIMIT; i++) {
d = new Dust(r);
while (!d.end) {
d.step();
}
if ((i & 1024) == 0) {
r = new Random();
}
}
}
}
private static class Dust {
public static int[] data = new int[2049];
static {
for (int i = 0; i < data.length; i++) {
data[i] = 0;
}
}
private int[] location;
private Random r;
public boolean end;
public Dust(Random rand) {
location = new int[]{1024, 0};
r = rand;
end = false;
}
public void step() {
if (end) {
return;
}
if (location[1] >= 1024) {
end = true;
synchronized (data) {
data[location[0]] += 1;
}
return;
}
location[1] += 1;
location[0] += r.nextInt(21) - 10;
}
}
}
Sample output (took 1 minute to create):
Because of the nature of the random number generation, it is possible to get an ArrayIndexOutOfBoundsException
.
I used multithreading to speed up the process. The program determines the number of processors available and creates that many threads to run the simulations.
To obtain a finer image, NUM_DUST
can be increased, but that would also lead to an increased risk of an ArrayIndexOutOfBoundsException
.
Each thread creates a new Random
after every 1024 Dust
objects are simulated. When that code was removed, the image was more coarse.
rand.nextInt(21) - 10
is to widen the distribution. A change to rand.nextInt(3) - 1
would remove all chance of an ArrayIndexOutOfBoundsException
.
Mathematica
I decided it should not only look like a bell curve, it should look like a bell. So hey, let's spin it around the Z-axis and make it 3D.
And hey, while we're at it, it should sound like a bell as well.
So, each time you evaluate the code, it makes a nice pleasing church-bell BWONNGGG!
Manipulate[
RevolutionPlot3D[PDF[NormalDistribution[mu, sigma], x], {x, -4, 4},
PlotRange -> {Automatic, Automatic, Automatic},
BoxRatios -> {1, 1, 1}, PlotStyle -> {Orange, Yellow},
ImageSize -> {500, 500}], {{mu, 0, "mean"}, -3, 3,
Appearance -> "Labeled"}, {{sigma, 1.47, "standard deviation"}, .5,
2, Appearance -> "Labeled"}]
EmitSound[Sound[SoundNote[0, 3, "TubularBells", SoundVolume -> 1]]]
Here's what it looks like:
And here's a brief video of the bell ringing.
Haskell, 9 LoC
Everyone's code is so ... huge, and their approximations are ... less than deterministic. Just to remind everyone of the simplicity of the actual task, here's a nine-liner in Haskell:
import Data.List; import Data.Ratio
main = do
putStrLn "please choose the width and the maximum height:"
[w, mh] <- (map read.words) `fmap` getLine
let dist = map length $ group $ sort $ map length $ subsequences [2..w]
scale = ceiling $ maximum dist % mh
maxD = scale * ceiling (maximum dist % scale)
putStrLn $ unlines $ map (row dist) [maxD, maxD - scale .. 0]
where row dist y = map (\x -> if x >= y then '*' else ' ') dist
OK... that wasn't exactly fast. Here's another go. Does it still count as not hard-coded?
import Data.List; import Data.Ratio
main = do
putStrLn "please choose the width and the maximum height:"
[w, mh] <- (map read.words) `fmap` getLine
let dist = map ((w-1) `choose`) [0..(w-1)]
scale = ceiling $ maximum dist % mh
maxD = scale * ceiling (maximum dist % scale)
putStrLn $ unlines $ map (row dist) [maxD, maxD - scale .. 0]
where row dist y = map (\x -> if x >= y then '*' else ' ') dist
factorial x = product [1..x]
n `choose` k = factorial n `div` factorial k `div` factorial (n-k)