Since a die's roll is naturally represented as an integer, it might
seem equally natural to use an integer method of the Random object,
such as "nextInt", and just use the result modulo six.  But the
problem is that the number of possible integers that can be returned
(with even distribution) is not a multiple of six, so it is impossible
to map each possible return value to one of the six die rolls fairly.
Hence, the approach taken is to use the "nextFloat" method, which
returns floating point numbers evenly distributed between 0 and 1.0,
and multiplying the result by six, thus giving a distribution evenly
spread across a distance of six on the number line.

import java.io.*;
import java.util.*;

class Dice {

    public static void main(String[] args) {
	if (args.length < 1 || args.length > 3)
	    error("usage: Dice number_of_dice [roll_factor] [seed]");

	// get number of dice
	int dice = 1;
	try {
	    dice = Integer.parseInt(args[0]);
	    if (dice < 1)
		throw new NumberFormatException(args[0]);
	} catch (NumberFormatException e) {
	    error("invalid number of dice");
	}

	// default do 100 actual rolls per theoretical roll
	double rollFactor = (double)100.0;
	if (args.length >= 2) {
	    try {
		rollFactor = Double.valueOf(args[1]).doubleValue();
	    } catch (NumberFormatException e) {
		error("invalid number of rolls");
	    }
	}

	// create random number generator, use seed if specified
	Random random = new Random();
	if (args.length >= 3) {
	    try {
		random.setSeed(Long.parseLong(args[2]));
	    } catch (NumberFormatException e) {
		error("invalid random number seed");
	    }
	}

	int maxRoll = 6 * dice;	  // maximum possible roll with this many dice
	int[] theory = new int[maxRoll + 1];	// theoretical distribution
	int[] actual = new int[maxRoll + 1];	// experimental distribution

	// recursively calculate theoretical distribution
	calcTheory(theory, 0, dice);

	// perform experiemnt of actually rolling dice
	long count = (long)(Math.pow(6, dice) * rollFactor);
	for (long i = 0; i < count; i++) {
	    int sum = 0;
	    for (int j = 0; j < dice; j++) {
		int roll = Math.min((int)(random.nextFloat() * 6 + 1.0), 6);
		sum += roll;
	    }
	    actual[sum]++;
	}

	// output both distribution results
	System.out.println(
	    "Roll\tTheoretical Distribution\tActual Distribution");
	for (int i = dice; i <= maxRoll; i++)
	    System.out.println(String.valueOf(i) +
	                       "\t\t\t" + theory[i] +
	                       "\t\t\t" + actual[i]);
    }

    public static void calcTheory(int[] dist, int sum, int dice) {
	for (int i = 1; i <= 6; i++)
	    if (dice > 1)
		calcTheory(dist, sum + i, dice - 1);
	    else
		dist[sum + i]++;
    }

    public static void error(String err) {
	System.err.println("Dice: " + err);
	System.exit(1); // non-zero argument means "not good"
    }
}
