Java tutorial
/******************************************************************************* * Copyright (c) 2013 See AUTHORS file. * * This file is part of SleepFighter. * * SleepFighter is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * SleepFighter is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with SleepFighter. If not, see <http://www.gnu.org/licenses/>. ******************************************************************************/ package se.toxbee.sleepfighter.challenge.math; import java.util.Random; import org.apache.commons.math3.linear.ArrayRealVector; import org.apache.commons.math3.linear.RealMatrix; import org.apache.commons.math3.linear.RealVector; import se.toxbee.sleepfighter.R; import se.toxbee.sleepfighter.utils.debug.Debug; import se.toxbee.sleepfighter.utils.math.MatrixUtil; import android.content.Context; /* * Challenge: Solve a system of linear equations with three variables. * For the system of linear equations Ax = b, where x = (x1 x2 x3), the user is to solve * for either x1 or x2(just solving for x3 is too easy). * The systems of linear equations generated by this class, have an unique solution, * and are thus trivially solved using Gaussian elimination. */ public class LinearEquationProblem implements MathProblem { private final Context context; // we use a 3x3 coefficent matrix. private static final int MATRIX_SIZE = 3; private int solution; private String renderedString; private Random rng = new Random(); // The coefficient-matrix. private RealMatrix A; // The constant vector b. private RealVector b; private int variableToSolveFor; public String render() { return this.renderedString; } public int solution() { return this.solution; } // Create A private RealMatrix createCoefficients() { return MatrixUtil.createRandomMatrix(rng, MATRIX_SIZE, -5, 16, true); } // create a fun,fun,fun solution vector x! private RealVector createSolutionVector() { RealVector s; do { s = MatrixUtil.createRandomVector(rng, MATRIX_SIZE, -10, 10, true); } while (isBoringSolution(s)); return s; } public void newProblem() { generateProblem(); Debug.d("coeff: " + this.A); Debug.d("constants: " + this.b); // now render the problem doRender(); Debug.d("rendered problem: " + this.renderedString); } // find a system of linear equations with a real solution(because real solutions are easy to input with the android keyboard) private void generateProblem() { this.A = createCoefficients(); int attempts = 0; RealVector solution; boolean foundb = false; do { Debug.d("try again"); // if after 40 attempts we still can't find a good constants vector, we should give up on this // coefficient matrix and try creating a new one. if (attempts == 40) { Debug.d("new coefficients matrix"); this.A = createCoefficients(); attempts = 0; } // Generate a solution. solution = this.createSolutionVector(); // Now compute Ax = b(where x is the solution) this.b = multiply(this.A, solution); // does b satisfy our requirements? if (isGoodb(b)) { foundb = true; } attempts++; } while (!foundb); Debug.d("solution: " + solution.toString()); // solve for either x1 or x2 this.variableToSolveFor = rng.nextBoolean() ? 0 : 1; this.solution = (int) solution.getEntry(this.variableToSolveFor); } // multiply a 3x3 matrix by a 3 vector private static RealVector multiply(RealMatrix matrix, RealVector vector) { double[] m1 = matrix.getRow(0); double[] m2 = matrix.getRow(1); double[] m3 = matrix.getRow(2); double[] v = vector.toArray(); return new ArrayRealVector(new double[] { m1[0] * v[0] + m1[1] * v[1] + m1[2] * v[2], m2[0] * v[0] + m2[1] * v[1] + m2[2] * v[2], m3[0] * v[0] + m3[1] * v[1] + m3[2] * v[2], }); } // ensure that b isn't too big or too little private static boolean isGoodb(RealVector b) { final int MIN = -20; final int MAX = 59; return ((int) b.getEntry(0) > MIN && (int) b.getEntry(0) < MAX) && ((int) b.getEntry(1) > MIN && (int) b.getEntry(1) < MAX) && ((int) b.getEntry(2) > MIN && (int) b.getEntry(2) < MAX); } // boring solutions are solution that are small, boring numbers like 1 and 2. // We will not allow boring solutions! private static boolean isBoringSolution(int solution) { return solution == 0 || Math.abs(solution) == 1 || Math.abs(solution) == 2; } private static boolean isBoringSolution(RealVector v) { return isBoringSolution((int) v.getEntry(0)) || isBoringSolution((int) v.getEntry(1)) || isBoringSolution((int) v.getEntry(2)); } private void doRender() { // begin table. this.renderedString = "$\\{\\table "; for (int row = 0; row < MATRIX_SIZE; ++row) { // current row double[] r = this.A.getRow(row); // render the terms this.renderedString += renderTerm(r[0], getVariableString(0), true) + renderTerm(r[1], getVariableString(1), false) + renderTerm(r[2], getVariableString(2), false); // now the constant. this.renderedString += " , = , " + (int) this.b.getEntry(row) + ";"; } // close the table this.renderedString += "$"; // now the description. String format = context.getResources().getString(R.string.linear_equation_challenge_desc); this.renderedString += "<br>" + String.format(format, "$" + getVariableString(this.variableToSolveFor) + "$"); } private static String getVariableString(int variable) { if (variable == 0) { return "x"; } else if (variable == 1) { return "y"; } else if (variable == 2) { return "z"; } else { throw new IllegalArgumentException("This should not happen!"); } } private static String renderTerm(double value, String variable, boolean isFirstTerm) { int v = ((int) value); String s; if (isFirstTerm) { s = renderTermHelper(v, variable); } else { if (v < 0) { s = ", - , " + renderTermHelper(Math.abs(v), variable); } else { s = " , + , " + renderTermHelper(v, variable); } } return s + " "; } // 1x should be shown as x instead. public static String renderTermHelper(int value, String variable) { return (value == 1 ? variable : Integer.toString(value) + variable); } public LinearEquationProblem(final Context context) { this.context = context; } }