import java.awt.*; import java.applet.*; import java.util.*; /** * An applet to demostrate central limit theorem of probability. * It utilizes an expression evaluator class by The-Son LAI. *

* The whole program is distributed under the Modified BSD * license with the kind permission from The-Son. See below. *

* Copyright (c) 2001 by Jarno Elonen and The-Son LAI *

* http://iki.fi/elonen/, http://lts.online.fr/java/ */ public class CentralLimitApplet extends Applet { /* * The distribution licence * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. Redistributions in * binary form must reproduce the above copyright notice, this list of * conditions and the following disclaimer in the documentation and/or other * materials provided with the distribution. The name of the author may not * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * Start the applet, create panels */ public void init() { this.setLayout( null ); // Create the parameter panel Panel p = new Panel(); p.setLayout( new FlowLayout( FlowLayout.LEFT, 0,0 )); p.add( new Label( "Examine sum of", Label.LEFT )); p.add( sumOfTF ); p.add( new Label( "occurences of a" )); p.add( new Label( "phenomenon" )); p.add( new Label( "whose min =" )); p.add( minTF ); p.add( new Label( "," )); p.add( new Label( "max =" )); p.add( maxTF ); p.add( new Label( "," )); p.add( new Label( "prob. func. P(x)=" )); p.add( funcTF ); p.add( new Label( "and repeat it" )); p.add( repeatTF ); p.add( new Label( "times. " )); p.add( goButt ); Panel p2 = new Panel( new BorderLayout()); p2.add( BorderLayout.CENTER, p ); Panel p3 = new Panel( new BorderLayout()); p3.add( BorderLayout.CENTER, goButt ); p2.add( BorderLayout.SOUTH, p3 ); add( p2 ); p2.setBounds( 0,0, 100, getSize().height ); myImg = createImage( getSize().width-100, getSize().height ); clearCanvas(); repaint(); } /** * Redraw the screen */ public void paint( Graphics g ) { g.drawImage( myImg, 100,0, null ); } /** * Listen for button strokes */ public boolean action( Event evt, Object what ) { if ( evt.target == goButt ) { simulate(); repaint(); return true; } return false; } /** * This can be called from Javascript. * Sets the text fields and simulates a round. */ public void simulate( int min, int max, int sum, int repeat, String func ) { minTF.setText( "" + min ); maxTF.setText( "" + max ); sumOfTF.setText( "" + sum ); repeatTF.setText( "" + repeat ); funcTF.setText( func ); simulate(); repaint(); } /** * Paints the canvas white and draws * black borders around it */ private void clearCanvas() { int w = myImg.getWidth(null), h = myImg.getHeight(null); Graphics g = myImg.getGraphics(); g.setColor( Color.white ); g.fillRect( 0,0, 99999, 99999 ); g.setColor( Color.black ); g.drawRect( 0,0, w-1,h-1 ); } /** * Runs a simulation with given parameters * and draws the result as a histogram to the screen. */ private void simulate() { try { int min = getLimited( minTF, "Min", -9999, 9999 ); int max = getLimited( maxTF, "Max", -9999, 9999 ); int sumOf = getLimited( sumOfTF, "Sum", 1, 1000 ); int repeat = getLimited( repeatTF, "Repeat count", 1, 10000000 ); if ( min >= max ) throw new Exception( "Min must be < Max." ); int totalMin = (min*sumOf); int totalMax = (max*sumOf); Graphics g = myImg.getGraphics(); int w = myImg.getWidth(null), h = myImg.getHeight(null); clearCanvas(); repaint(); // Draw the scale g.setColor( Color.black ); g.drawString( "" + totalMin, 2,h-8 ); String maxStr = "" + totalMax; g.drawString( maxStr, w-g.getFontMetrics().stringWidth( maxStr )-2, h-8 ); String avgStr = "" + (totalMin+totalMax)/2; g.drawString( avgStr, w/2-g.getFontMetrics().stringWidth( avgStr )/2-2, h-8 ); myEval.setExpression( funcTF.getText()); int range = max-min+1; int totalRange = totalMax-totalMin+1; // Calculate propabilities from the function double fTotal = 0; double prob[] = new double[ range ]; for ( int i=0; iprob[k] && k<=range ) d -= prob[k++]; sum += k; } freq[sum]++; if ( freq[sum] > maxFreq ) maxFreq = freq[sum]; } // Normalize freq to 0...1 for ( int i=0; i max ) throw new NumberFormatException(); return val; } catch( NumberFormatException nfe ) { String msg = "'" + name + "' must be an integer between [" + min + ", " + max +"]"; throw new Exception( msg ); } } private TextField sumOfTF = new TextField( "10", 12 ), minTF = new TextField( "1", 8 ), maxTF = new TextField( "6", 8 ), funcTF = new TextField( "1/6", 12 ), repeatTF = new TextField( "10000", 12 ); private Button goButt = new Button( "Simulate" ); private Image myImg; private MathEvaluator myEval = new MathEvaluator(); private Random myRnd = new Random(); /** * Mathematic expression evaluator. Supports the following functions: * +, -, *, /, ^, %, cos, sin, tan, acos, asin, atan, sqrt, sqr, log, min, max, ceil, floor, abs, neg, rndr.
* When the getValue() is called, a Double object is returned. If it returns null, an error occured.

*

	 * Sample:
	 * MathEvaluator m = new MathEvaluator("-5-6/(-2) + sqr(15+x)");
	 * m.addVariable("x", 15.1d);
	 * System.out.println( m.getValue() );
	 * 
* Refactored slightly for smaller size by Jarno Elonen * @version 1.1 * @author The-Son LAI, Lts@writeme.com * @date April 2001 **/ public static class MathEvaluator { protected static Operator[] operators = null; private Node node = null; private String expression = null; private Hashtable variables = new Hashtable(); /*** * adds a variable and its value in the MathEvaluator */ public void addVariable(String v, double val) { variables.put(v, new Double(val)); } /*** * sets the expression */ public void setExpression(String s) { if ( operators == null ) initializeOperators(); expression = s; } /*** * evaluates and returns the value of the expression */ public Double getValue() { if (expression == null) return null; try { node = new Node(expression); return evaluate(node); } catch (Exception e) { e.printStackTrace(); return null; } } private static Double evaluate(Node n) { if ( n.hasOperator() && n.hasChild() ) { if ( n.nOperator.getType() == 1 ) n.nValue = evaluateExpression( n.nOperator, evaluate( n.nLeft ), null ); else if ( n.nOperator.getType() == 2 ) n.nValue = evaluateExpression( n.nOperator, evaluate( n.nLeft ), evaluate( n.nRight ) ); } return n.nValue; } private static Double evaluateExpression(Operator o, Double f1, Double f2) { String op = o.getOperator(); Double res = null; if ( "+".equals(op) ) res = new Double( f1.doubleValue() + f2.doubleValue() ); else if ( "-".equals(op) ) res = new Double( f1.doubleValue() - f2.doubleValue() ); else if ( "*".equals(op) ) res = new Double( f1.doubleValue() * f2.doubleValue() ); else if ( "/".equals(op) ) res = new Double( f1.doubleValue() / f2.doubleValue() ); else if ( "^".equals(op) ) res = new Double( Math.pow(f1.doubleValue(), f2.doubleValue()) ); else if ( "%".equals(op) ) res = new Double( f1.doubleValue() % f2.doubleValue() ); else if ( "&".equals(op) ) res = new Double( f1.doubleValue() + f2.doubleValue() ); // todo else if ( "|".equals(op) ) res = new Double( f1.doubleValue() + f2.doubleValue() ); // todo else if ( "cos".equals(op) ) res = new Double( Math.cos(f1.doubleValue()) ); else if ( "sin".equals(op) ) res = new Double( Math.sin(f1.doubleValue()) ); else if ( "tan".equals(op) ) res = new Double( Math.tan(f1.doubleValue()) ); else if ( "acos".equals(op) ) res = new Double( Math.acos(f1.doubleValue()) ); else if ( "asin".equals(op) ) res = new Double( Math.asin(f1.doubleValue()) ); else if ( "atan".equals(op) ) res = new Double( Math.atan(f1.doubleValue()) ); else if ( "sqr".equals(op) ) res = new Double( f1.doubleValue() * f1.doubleValue() ); else if ( "sqrt".equals(op) ) res = new Double( Math.sqrt(f1.doubleValue()) ); else if ( "log".equals(op) ) res = new Double( Math.log(f1.doubleValue()) ); else if ( "min".equals(op) ) res = new Double( Math.min(f1.doubleValue(), f2.doubleValue()) ); else if ( "max".equals(op) ) res = new Double( Math.max(f1.doubleValue(), f2.doubleValue()) ); else if ( "exp".equals(op) ) res = new Double( Math.exp(f1.doubleValue()) ); else if ( "floor".equals(op) ) res = new Double( Math.floor(f1.doubleValue()) ); else if ( "ceil".equals(op) ) res = new Double( Math.ceil(f1.doubleValue()) ); else if ( "abs".equals(op) ) res = new Double( Math.abs(f1.doubleValue()) ); else if ( "neg".equals(op) ) res = new Double( - f1.doubleValue() ); else if ( "rnd".equals(op) ) res = new Double( Math.random() * f1.doubleValue() ); return res; } private void initializeOperators() { operators = new Operator[25]; operators[0] = new Operator("+" , 2, 0); operators[1] = new Operator("-" , 2, 0); operators[2] = new Operator("*" , 2, 10); operators[3] = new Operator("/" , 2, 10); operators[4] = new Operator("^" , 2, 10); operators[5] = new Operator("%" , 2, 10); operators[6] = new Operator("&" , 2, 0); operators[7] = new Operator("|" , 2, 0); operators[8] = new Operator("cos" , 1, 20); operators[9] = new Operator("sin" , 1, 20); operators[10] = new Operator("tan" , 1, 20); operators[11] = new Operator("acos" , 1, 20); operators[12] = new Operator("asin" , 1, 20); operators[13] = new Operator("atan" , 1, 20); operators[14] = new Operator("sqrt" , 1, 20); operators[15] = new Operator("sqr" , 1, 20); operators[16] = new Operator("log" , 1, 20); operators[17] = new Operator("min" , 2, 0); operators[18] = new Operator("max" , 2, 0); operators[19] = new Operator("exp" , 1, 20); operators[20] = new Operator("floor", 1, 20); operators[21] = new Operator("ceil" , 1, 20); operators[22] = new Operator("abs" , 1, 20); operators[23] = new Operator("neg" , 1, 20); operators[24] = new Operator("rnd" , 1, 20); } private Double getDouble(String s) { if ( s == null ) return null; Double res = null; try { res = Double.valueOf( s ); } catch(Exception e) { return (Double) variables.get(s); } return res; } protected class Operator { private String op; private int type; private int priority; public Operator(String o, int t, int p) { op = o; type = t; priority = p; } public String getOperator() { return op; } public void setOperator(String o) { op = o; } public int getType() { return type; } public int getPriority() { return priority; } } protected class Node { public String nString = null; public Operator nOperator = null; public Node nLeft = null; public Node nRight = null; public Node nParent = null; public int nLevel = 0; public Double nValue = null; public Node(String s) throws Exception { init(null, s, 0); } public Node(Node parent, String s, int level) throws Exception { init(parent, s, level); } private void init(Node parent, String s, int level) throws Exception { s = removeIllegalCharacters(s); s = removeBrackets(s); s = addZero(s); if ( checkBrackets(s) != 0 ) throw new Exception("Wrong number of brackets in [" + s + "]"); nParent = parent; nString = s; nValue = getDouble(s); nLevel = level; int sLength = s.length(); int inBrackets = 0; int startOperator = 0; for (int i=0; i= o.getPriority() ) { nOperator = o; startOperator = i; } } } } } if ( nOperator != null ) { // one operand, should always be at the beginning if ( startOperator==0 && nOperator.getType() == 1 ) { // the brackets must be ok if ( checkBrackets( s.substring( nOperator.getOperator().length() ) ) == 0 ) { nLeft = new Node( this, s.substring( nOperator.getOperator().length() ) , nLevel + 1); nRight = null; return; } else throw new Exception("Error during parsing... missing brackets in [" + s + "]"); } // two operands else if ( startOperator > 0 && nOperator.getType() == 2 ) { nOperator = nOperator; nLeft = new Node( this, s.substring(0, startOperator), nLevel + 1 ); nRight = new Node( this, s.substring(startOperator + nOperator.getOperator().length()), nLevel + 1); } } } private Operator getOperator(String s, int start) { String temp = s.substring(start); temp = getNextWord(temp); for (int i=0; i 'z' || c < 'a') && (c > '9' || c < '0') ) return s.substring(0, i); } return s; } /*** * checks if there is any missing brackets * @return true if s is valid */ protected int checkBrackets(String s) { int sLength = s.length(); int inBracket = 0; for (int i=0; i= 0 ) inBracket++; else if ( s.charAt(i) == ')' ) inBracket--; } return inBracket; } /*** * returns a string that doesnt start with a + or a - */ protected String addZero(String s) { if ( s.startsWith("+") || s.startsWith("-") ) { int sLength = s.length(); for (int i=0; i 2 && res.startsWith("(") && res.endsWith(")") && checkBrackets(s.substring(1,s.length()-1)) == 0 ) { res = res.substring(1, res.length()-1 ); } if ( res != s ) return removeBrackets(res); else return res; } /*** * Removes illegal characters */ public String removeIllegalCharacters(String s) { char[] illegalCharacters = { ' ' }; String res = s; for ( int j=0; j