Monte Carlo Simulation - java

I'm a student in a Java Programming class. My problem deals with an interpretation of the Monte Carlo Simulation. I'm supposed to find the probability that three quarters or three pennies will be picked out of a purse that has 3 quarters and 3 pennies. Once a coin is picked it is not replaced. The probability should be 0.1XXXXXXX. I keep getting 0 or 1 for my answer. This is what i have so far.
public class CoinPurse {
public static void main(String[] args) {
System.out.print("Probability of Drawing 3 coins of the Same Type - ");
System.out.println(coinPurseSimulation(100));
}
/**
Runs numTrials trials of a Monte Carlo simulation of drawing
3 coins out of a purse containing 3 pennies and 3 quarters.
Coins are not replaced once drawn.
#param numTrials - the number of times the method will attempt to draw 3 coins
#returns a double - the fraction of times 3 coins of the same type were drawn.
*/
public static double coinPurseSimulation(int numTrials) {
final int P = 1;
final int Q = 2;
int [] purse = {Q, Q, Q, P, P, P};
int [] drawCoins = new int[3];
for (int draw = 0; draw < 3; draw ++) {
int index = (int)(Math.random() * purse.length);
drawCoins[draw] = purse[index];
int [] newPurse = new int[purse.length-1];
int j = 0;
for (int i =0; i < purse.length; i++) {
if (i == index) {
continue;
}
newPurse[j] = purse[i];
j++;
}
purse = newPurse;
}
double number = 0.0;
double result = 0.0;
for (int i = 0; i < numTrials; i++) {
result++;
for (int j = 0; j < numTrials;j++) {
if(drawCoins[0] == drawCoins [1] && drawCoins[1] == drawCoins[2]) {
number++;
}
}
}
return number/result;
}
}

The reason you only ever get 0 or 1 is that you only draw (or pick) coins from the purse once, but you then test that draw numTrials * numTrials times. You have two loops (with indices i and j) iterating numTrials time - your logic is a little messed up there.
You can put the first loop (for drawing coins) within a second loop (for running trials) and your code will work. I've put a minimal refactor below (using your code as closely as possible), with two comments afterwards that might help you some more.
public class CoinPurse
{
public static void main(String[] args)
{
System.out.print("Probability of Drawing 3 coins of the Same Type - ");
System.out.println(coinPurseSimulation(100));
}
/**
* Runs numTrials trials of a Monte Carlo simulation of drawing 3 coins out
* of a purse containing 3 pennies and 3 quarters. Coins are not replaced
* once drawn.
*
* #param numTrials
* - the number of times the method will attempt to draw 3 coins
* #returns a double - the fraction of times 3 coins of the same type were
* drawn.
*/
public static double coinPurseSimulation(int numTrials)
{
final int P = 1;
final int Q = 2;
double number = 0.0;
double result = 0.0;
// Changed your loop index to t to avoid conflict with i in your draw
// loop
for (int t = 0; t < numTrials; t++)
{
result++;
// Moved your draw without replacement code here
int[] purse =
{ Q, Q, Q, P, P, P };
int[] drawCoins = new int[3];
for (int draw = 0; draw < 3; draw++)
{
int index = (int) (Math.random() * purse.length);
drawCoins[draw] = purse[index];
int[] newPurse = new int[purse.length - 1];
int j = 0;
for (int i = 0; i < purse.length; i++)
{
if (i == index)
{
continue;
}
newPurse[j] = purse[i];
j++;
}
purse = newPurse;
}
// Deleted the loop with index j - you don't need to test the same
// combination numTrials times...
if (drawCoins[0] == drawCoins[1] && drawCoins[1] == drawCoins[2])
{
number++;
}
}
return number / result;
}
}
Picking coins code
I have some comments on your routing for drawing coins:
It works correctly
It is rather cumbersome
It would have been easier for you to spot the problem if you had broken this bit of code into a separate method.
I'm going to address 3 and then 2.
Break the drawing code out into a method
private static int[] pickCoins(int[] purse, int numPicks)
{
//A little error check
if (numPicks > purse.length)
{
System.err.println("Can't pick " + numPicks +
" coins from a purse with only " + purse.length + " coins!");
}
int[] samples = new int[numPicks];
// Your sampling code here
return samples;
}
You can now simply call from within your second loop i.e.
drawCoins = pickCoins(purse, 3);
Sampling algorithm
#pjs's answer recommends using Collections.shuffle() and then taking the first 3 coins in your collection (e.g. an ArrayList). This is a good suggestion, but I'm guessing you haven't been introduced to Collections yet, and may not be 'allowed' to use them. If you are - do use them. If not (as I assume), you might want to think about better ways to randomly draw n items from an r length array without replacement.
One (well accepted) way is the Fisher-Yates shuffle and its derivatives. In effect it involves picking randomly from the unpicked subset of an array.
In Java - an working example could be as follows - it works by moving picked coins to the "end" of the purse and picking only from the first maxInd unpicked coins.
private static int[] pickCoins(int[] purse, int numCoins)
{
int[] samples = new int[numCoins];
int maxInd = purse.length - 1;
for (int i = 0; i < numCoins; i++)
{
int index = (int) (Math.random() * maxInd);
int draw = purse[index];
samples[i] = draw;
// swap the already drawn sample with the one at maxInd and decrement maxInd
purse[index] = purse[maxInd];
purse[maxInd] = draw;
maxInd -= 1;
}
return samples;
}
Expected results
You say your expected result is 0.1XXXXXXX. As you're learning Monte Carlo simulation - you might need to think about that a little more. The expected result depends on how many trials you do.
First, in this simple example, you can consider the analytic (or in some sense exact) result. Consider the procedure:
You draw your first coin - it doesn't matter which one it is
Whichever coin it was, there are 2 left in the bag that are the same - the probability of picking one of those is 2 / 5
If you picked one of the matching coins in step 2, there is now 1 matching coin left in the bag. The probability of picking that is 1 / 4
So, the probability of getting 3 matching coins (of either denomination) is 2 / 5 * 1 / 4 == 2 / 20 == 0.1
Your Monte Carlo programme is trying to estimate that probability. You would expect it to converge on 0.1 given sufficient estimates (i.e. with numTrials high enough). It won't always give a value equal to, or even starting with, 0.1. With sufficient number of trials, it's likely to give something starting 0.09 or 0.1. However, if numTrials == 1, it will give either 0 or 1, because it will draw once and the draw will either match or not. If numTrials == 2, the results can only be 0, 0.5 or 1 and so on.
One of the lessons of doing Monte Carlo simulation to estimate probabilities is to have a sufficiently high sample count to get a good estimate. That in turn depends on the accuracy you want - you can use your code to investigate this once it's working.

You need to move the loop where you generate draws down into the numTrials loop. The way you've written it you're making a single draw, and then checking that one result numTrials times.
I haven't checked the logic for your draw carefully, but that's because I'd recommend a different (and much simpler) approach. Use Collections.shuffle() on your set of quarters and pennies, and check the first three elements after each shuffle.
If done correctly, the answer should be 2 * (3/6) * (2/5) * (1/4), which is 0.1.

Related

Java: Does the amount of times you call Math.random() affect the outcome

I had to do homework to create a bingo game, so I created a method which returns an int array with 25 random numbers for each bingo card and 75 random numbers for gameplay, (the cards have to be 5x5 2d arrays populated with random numbers from 1 - 75 and for gameplay I need to be able to call from 1 - 75 in random order) I find that with 2 players the first one wins at least 80% of the times I tested it, although I would expect it to be closer to 50\50 at some times,
this is my method:
public int[] pickAllNumbers(){
boolean[] picks = new boolean[LARGEST_NUM + 1];
int numbersLength;
int[] numbers;
if (this instanceof Card) {
numbersLength = BOARD_SIZE * BOARD_SIZE;
} else {
numbersLength = LARGEST_NUM;
}
numbers = new int[numbersLength];
int i = 0;
while(i < numbersLength){
int pick = (int)(Math.random() * LARGEST_NUM) +1;
if(!picks[pick]){
picks[pick] = true;
numbers[i] = pick;
i++;
}
}
return numbers;
}
LARGEST_NUM is 75, and BOARD_SIZE is 5;
when I call it from my Card class I use it to populate a 2d array for the card, and when I call it in my Main class I use it to create an array of 75 length to call each number for the game
You haven't specified how many times you've ran this test, but over time and a lot(millions) of iterations it'll balance out to 50/50 or close enough. I'm assuming you haven't ran your test this much, and over a small amount of iterations it won't balance out to 50/50, as this is not how randomness works.

My Birthday Problem code is not printing anything

I am an absolute beginner to learning programming and I was given this assignment:
Birthday problem. Suppose that people enter a room one at a time. How people must enter until two share a birthday? Counterintuitively, after 23 people enter the room, there is approximately a 50–50 chance that two share a birthday. This phenomenon is known as the birthday problem or birthday paradox.
Write a program Birthday.java that takes two integer command-line arguments n and trials and performs the following experiment, trials times:
Choose a birthday for the next person, uniformly at random between 0 and n−1.
Have that person enter the room.
If that person shares a birthday with someone else in the room, stop; otherwise repeat.
In each experiment, count the number of people that enter the room. Print a table that summarizes the results (the count i, the number of times that exactly i people enter the room, and the fraction of times that i or fewer people enter the room) for each possible value of i from 1 until the fraction reaches (or exceeds) 50%.
For more information on the assignment
However, my code won't print. I would really appreciate if someone could help me find the problem to my assignment.
public class Birthday {
public static void main(String[] args) {
int n = Integer.parseInt(args[0]); //number of days
int trials = Integer.parseInt(args[1]);
boolean[] birthdays = new boolean[n];
int[] times = new int[n + 2]; //number of times i people entered the room
int r;
for (int t = 1; t <= trials; t++) {
for (int k = 0; k < n; k++) { //reset birthday
birthdays[k] = false;
}
for (int i = 1; i <= n; i++) { //number of times
r = (int) (Math.random() * (n - 1)); //random birthday
if (birthdays[r] = false) {
birthdays[r] = true;
continue;
}
else if (birthdays[r] = true) {
times[i]++; //number of times i people entered the room + 1
break;
}
}
}
int j = 1;
while ((double) times[j] / trials <= 0.5) {
System.out.print(j + "\t" + times[j] + "\t" + ((double) times[j] / trials));
j++;
System.out.println("");
}
}
}
I can spot two errors from your code
As Scary Wombat pointed out, you are miss double equal sign inside of your if statement.
The assignment is asking you to calculate "fraction of times that i or fewer people enter the room", meaning you need to do a summation for the first i indices and divided by trials.
For example, among 1 million trials, the fraction in which first duplicate birthday happens when 4th person enters is
(times[0] + times[1] + times[2] + times[3])/ 1000000
Here is what I got:
1 0 0.0
2 2810 0.00281
3 5428 0.008238
4 8175 0.016413
As you can see the fraction is calculated by adding the first three elements together and then divided by 1000000 (2810 + 5428 + 8175 = 16413) / 1000000 = 0.016413
The way you are calculating the fraction ((double) times[j] / trials) is not correct.
You are not adding the previous counts as shown in the example. To do so, you can create a new variable to store the sums of previous counts. and use it as your while loop condition. For instance, see below..
csum += times[j]; // this adds the previous counts into a cumulative sum.
This cumulative sum is supposed to be the one u use to divide by trials to get your probability. Cheers!

Java Biasing Random Numbers in a Triangular Array

This question is an extension of Java- Math.random(): Selecting an element of a 13 by 13 triangular array. I am selecting two numbers at random (0-12 inclusive) and I wanted the values to be equal.
But now, since this is a multiplication game, I want a way to bias the results so certain combinations come up more frequently (like if the Player does worse for 12x8, I want it to come up more frequently). Eventually, I would like to bias towards any of the 91 combinations, but once I get this down, that should not be hard.
My Thoughts: Add some int n to the triangular number and Random.nextInt(91 + n) to bias the results toward a combination.
private int[] triLessThan(int x, int[] bias) { // I'm thinking a 91 element array, 0 for no bias, positive for bias towards
int i = 0;
int last = 0;
while (true) {
int sum = 0;
for (int a = 0; a < i * (i + 2)/2; a++){
sum += bias[a]
}
int triangle = i * (i + 1) / 2;
if (triangle + sum > x){
int[] toReturn = {last,i};
return toReturn;
}
last = triangle;
i++;
}
}
At the random number roll:
int sum = sumOfArray(bias); // bias is the array;
int roll = random.nextInt(91 + sum);
int[] triNum = triLessThan(roll);
int num1 = triNum[1];
int num2 = roll - triNum[0]; //now split into parts and make bias[] add chances to one number.
where sumOfArray just finds the sum (that formula is easy). Will this work?
Edit: Using Floris's idea:
At random number roll:
int[] bias = {1,1,1,...,1,1,1} // 91 elements
int roll = random.nextInt(sumOfBias());
int num1 = roll;
int num2 = 0;
while (roll > 0){
roll -= bias[num2];
num2++;
}
num1 = (int) (Math.sqrt(8 * num2 + 1) - 1)/2;
num2 -= num1 * (num1 + 1) / 2;
You already know how to convert a number between 0 and 91 and turn it into a roll (from the answer to your previous question). I would suggest that you create an array of N elements, where N >> 91. Fill the first 91 elements with 0...90, and set a counter A to 91. Now choose a number between 0 and A, pick the corresponding element from the array, and convert to a multiplication problem. If the answer is wrong, append the number of the problem to the end of the array, and increment A by one.
This will create an array in which the frequencies of sampling will represent the number of times a problem was solved incorrectly - but it doesn't ever lower the frequency again if the problem is solved correctly the next time it is asked.
An alternative and better solution, and one that is a little closer to yours (but distinct) creates an array of 91 frequencies - each initially set to 1 - and keeps track of the sum (initially 91). But now, when you choose a random number (between 0 and sum) you traverse the array until the cumulative sum is greater then your random number - the number of the bin is the roll you choose, and you convert that with the formula derived earlier. If the answer is wrong you increment the bin and update the sum; if it is right, you decrement the sum but never to a value less than one, and update the sum. Repeat.
This should give you exactly what you are asking: given an array of 91 numbers ("bins"), randomly select a bin in such a way that the probability of that bin is proportional to the value in it. Return the index of the bin (which can be turned into the combination of numbers using the method you had before). This function is called with the bin (frequency) array as the first parameter, and the cumulative sum as the second. You look up where the cumulative sum of the first n elements first exceeds a random number scaled by the sum of the frequencies:
private int chooseBin(float[] freq, float fsum) {
// given an array of frequencies (probabilities) freq
// and the sum of this array, fsum
// choose a random number between 0 and 90
// such that if this function is called many times
// the frequency with which each value is observed converges
// on the frequencies in freq
float x, cs=0; // x stores random value, cs is cumulative sum
int ii=-1; // variable that increments until random value is found
x = Math.rand();
while(cs < x*fsum && ii<90) {
// increment cumulative sum until it's bigger than fraction x of sum
ii++;
cs += freq[ii];
}
return ii;
}
I confirmed that it gives me a histogram (blue bars) that looks exactly like the probability distribution that I fed it (red line):
(note - this was plotted with matlab so X goes from 1 to 91, not from 0 to 90).
Here is another idea (this is not really answering the question, but it's potentially even more interesting):
You can skew your probability of choosing a particular problem by sampling something other than a uniform distribution. For example, the square of a uniformly sampled random variate will favor smaller numbers. This gives us an interesting possibility:
First, shuffle your 91 numbers into a random order
Next, pick a number from a non-uniform distribution (one that favors smaller numbers). Since the numbers were randomly shuffled, they are in fact equally likely to be chosen. But now here's the trick: if the problem (represented by the number picked) is solved correctly, you move the problem number "to the top of the stack", where it is least likely to be chosen again. If the player gets it wrong, it is moved to the bottom of the stack, where it is most likely to be chosen again. Over time, difficult problems move to the bottom of the stack.
You can create random distributions with different degrees of skew using a variation of
roll = (int)(91*(asin(Math.rand()*a)/asin(a)))
As you make a closer to 1, the function tends to favor lower numbers with almost zero probability of higher numbers:
I believe the following code sections do what I described:
private int[] chooseProblem(float bias, int[] currentShuffle) {
// if bias == 0, we choose from uniform distribution
// for 0 < bias <= 1, we choose from increasingly biased distribution
// for bias > 1, we choose from uniform distribution
// array currentShuffle contains the numbers 0..90, initially in shuffled order
// when a problem is solved correctly it is moved to the top of the pile
// when it is wrong, it is moved to the bottom.
// return value contains number1, number2, and the current position of the problem in the list
int problem, problemIndex;
if(bias < 0 || bias > 1) bias = 0;
if(bias == 0) {
problem = random.nextInt(91);
problemIndex = problem;
}
else {
float x = asin(Math.random()*bias)/asin(bias);
problemIndex = Math.floor(91*x);
problem = currentShuffle[problemIndex];
}
// now convert "problem number" into two numbers:
int first, last;
first = (int)((Math.sqrt(8*problem + 1)-1)/2);
last = problem - first * (first+1) / 2;
// and return the result:
return {first, last, problemIndex};
}
private void shuffleProblems(int[] currentShuffle, int upDown) {
// when upDown==0, return a randomly shuffled array
// when upDown < 0, (wrong answer) move element[-upDown] to zero
// when upDown > 0, (correct answer) move element[upDown] to last position
// note - if problem 0 is answered incorrectly, don't call this routine!
int ii, temp, swap;
if(upDown == 0) {
// first an ordered list:
for(ii=0;ii<91;ii++) {
currentShuffle[ii]=ii;
}
// now shuffle it:
for(ii=0;ii<91;ii++) {
temp = currentShuffle[ii];
swap = ii + random.nextInt(91-ii);
currentShuffle[ii]=currentShuffle[swap];
currentShuffle[swap]=temp;
}
return;
}
if(upDown < 0) {
temp = currentShuffle[-upDown];
for(ii = -upDown; ii>0; ii--) {
currentShuffle[ii]=currentShuffle[ii-1];
}
currentShuffle[0] = temp;
}
else {
temp = currentShuffle[upDown];
for(ii = upDown; ii<90; ii++) {
currentShuffle[ii]=currentShuffle[ii+1];
}
currentShuffle[90] = temp;
}
return;
}
// main problem posing loop:
int[] currentShuffle = new int[91];
int[] newProblem;
int keepGoing = 1;
// initial shuffle:
shuffleProblems( currentShuffle, 0); // initial shuffle
while(keepGoing) {
newProblem = chooseProblem(bias, currentShuffle);
// pose the problem, get the answer
if(wrong) {
if(newProblem > 0) shuffleProblems( currentShuffle, -newProblem[2]);
}
else shuffleProblems( currentShuffle, newProblem[2]);
// decide if you keep going...
}

pascal triangle proper formatting java

so I'm currently working on an assignment that I just can't seem to finish. Well I have everything finished but would like the extra credit. I've been looking around the web and can't really seem to find exactly what I'm looking for.
public class PascalTester
{
public static void main(String[] args)
{
Scanner kb = new Scanner(System.in);
System.out.println("Welcome to the Pascal's Triangle program!");
System.out.println("Please enter the size of the triangle you want");
int size = kb.nextInt();
int[][] myArray = new int[size][size];
myArray = fillArray(myArray);
//myArray = calculateArray(myArray);
printArray(myArray); //prints the array
}
private static int[][] fillArray(int[][] array)
{
array[0][1] = 1;
for (int i = 1; i < array.length; i++)
{
for (int j = 1; j < array[i].length; j++)
{
array[i][j] = array[i-1][j-1] + array[i-1][j];
}
}
return array;
}
private static void printArray(int[][] array)
{
for (int i = 0; i < array.length; i++)
{
for (int j = 0; j < array[i].length; j++)
{
if(array[i][j] != 0)
System.out.print(array[i][j] + " ");
}
System.out.println();
}
}
}
The only issue that I'm having now is to properly format the output to look like an actual triangle. Any suggestions would be very helpful at this point in time. Thanks in advance
One approach to this, is, assuming you have all numbers formatted to the same width, is to treat the problem as that of centering the lines.
Java Coding left as exercise to reader but essentially:
for lineText : triange lines
leadingSpacesCount = (80/2) - lineText.length();
print " " x leadingSpacesCount + lineText
Try to use the technique at http://www.kodejava.org/examples/16.html to make an array with array.length - i - 1 spaces (need to add the number spaces between numbers.. and 2 number of 2 digit numbers if any..).
Print this array at the start of the outer for loop.
The challenge here is that you want to start printing at the top of the triangle, but you don't know where to center each row until you get to the last (and widest) row of the triangle. The trick is to not print anything until you know how wide the last row is. One way to do this is to generate all the rows as String (or StringBuilder) objects and compute the maximum width. Then, from the top, center each line by first printing an appropriate number of spaces. The correct number of spaces will be
(maxLineLength - currentLine.length()) / 2
Alternatively, you can simply assume a maximum line length and center all lines in that width. If the longer lines exceed the maximum width, then the triangle will be distorted below a certain row. (Just be sure to not try printing a negative number of spaces!)
If anyone is looking for the actual code to do this take a look at my implementation in Java, it's similar to what Craig Taylor mentioned (numbers formatted to the same width) plus it uses an algorithm to compute the elements without memory (or factorials).
The code has comments explaining each step (calculation and printing):
/**
* This method will print the first # levels of the Pascal's triangle. It
* uses the method described in:
*
* https://en.wikipedia.org/wiki/Pascal%27s_triangle#Calculating_a_row_or_diagonal_by_itself
*
* It basically computes the Combinations of the current row and col
* multiplied by the previous one (which will always be 1 at the beginning
* of each pascal triangle row). It will print each tree element to the output
* stream, aligning the numbers with spaces to form a perfect triangle.
*
* #param num
* # of levels to print
*/
public static void printPascalTriangle(int num) {
// Create a pad (# of spaces) to display between numbers to keep things
// in order. This should be bigger than the # of digits of the highest
// expected number and it should be an odd number (to have the same
// number of spaces to the left and to the right between numbers)
int pad = 7;
// Calculate the # of spaces to the left of each number plus itself
// (this is the width of the steps of the triangle)
int stepsWidth = pad / 2 + 1;
// Now calculate the maximum # of spaces from the left side of the
// screen to the first triangle's level (we will have num-1 steps in the
// triangle)
int spaces = (num - 1) * stepsWidth;
for (int n = 0; n < num; n++) {
// Print the left spaces of the current level, deduct the size of a
// number in each row
if (spaces > 0) {
System.out.printf("%" + spaces + "s", "");
spaces -= stepsWidth;
}
// This will represent the previous combination C(n k-1)
int prevCombination = 1;
for (int k = 1; k <= n + 1; k++) {
System.out.print(prevCombination);
// Calculate how many digits this number has and deduct that to
// the pad between numbers to keep everything aligned
int digits = (int) Math.log10(prevCombination);
if (digits < pad) {
System.out.printf("%" + (pad - digits) + "s", "");
}
// Formula from Wikipedia (we can remove that "+1" if we start
// the row loop at n=1)
prevCombination = prevCombination * (n + 1 - k) / k;
}
// Row separator
System.out.println();
}
}
Hope it helps someone!

In Java finding numbers that are both a Triangle Number and a Star Number

This is the question I've been assigned:
A so-called “star number”, s, is a number defined by the formula:
s = 6n(n-1) + 1
where n is the index of the star number.
Thus the first six (i.e. for n = 1, 2, 3, 4, 5 and 6) star numbers are: 1, 13, 37,
73, 121, 181
In contrast a so-called “triangle number”, t, is the sum of the numbers from 1 to n: t = 1 + 2 + … + (n-1) + n.
Thus the first six (i.e. for n = 1, 2, 3, 4, 5 and 6) triangle numbers are: 1, 3, 6, 10, 15, 21
Write a Java application that produces a list of all the values of type int that are both star number and triangle numbers.
When solving this problem you MUST write and use at least one function (such as isTriangeNumber() or isStarNumber()
or determineTriangeNumber() or determineStarNumber()). Also you MUST only use the formulas provided here to solve the problem.
tl;dr: Need to output values that are both Star Numbers and Triangle Numbers.
Unfortunately, I can only get the result to output the value '1' in an endless loop, even though I am incrementing by 1 in the while loop.
public class TriangularStars {
public static void main(String[] args) {
int n=1;
int starNumber = starNumber(n);
int triangleNumber = triangleNumber(n);
while ((starNumber<Integer.MAX_VALUE)&&(n<=Integer.MAX_VALUE))
{
if ((starNumber==triangleNumber)&& (starNumber<Integer.MAX_VALUE))
{
System.out.println(starNumber);
}
n++;
}
}
public static int starNumber( int n)
{
int starNumber;
starNumber= (((6*n)*(n-1))+1);
return starNumber;
}
public static int triangleNumber( int n)
{
int triangleNumber;
triangleNumber =+ n;
return triangleNumber;
}
}
Here's a skeleton. Finish the rest yourself:
Questions to ask yourself:
How do I make a Triangle number?
How do I know if something is a Star number?
Why do I only need to proceed until triangle is negative? How can triangle ever be negative?
Good luck!
public class TriangularStars {
private static final double ERROR = 1e-7;
public static void main(String args[]) {
int triangle = 0;
for (int i = 0; triangle >= 0; i++) {
triangle = determineTriangleNumber(i, triangle);
if (isStarNumber(triangle)) {
System.out.println(triangle);
}
}
}
public static boolean isStarNumber(int possibleStar) {
double test = (possibleStar - 1) / 6.;
int reduce = (int) (test + ERROR);
if (Math.abs(test - reduce) > ERROR)
return false;
int sqrt = (int) (Math.sqrt(reduce) + ERROR);
return reduce == sqrt * (sqrt + 1);
}
public static int determineTriangleNumber(int i, int previous) {
return previous + i;
}
}
Output:
1
253
49141
9533161
1849384153
You need to add new calls to starNumber() and triangleNumber() inside the loop. You get the initial values but never re-call them with the updated n values.
As a first cut, I would put those calls immediatly following the n++, so
n++;
starNumber = starNumber(n);
triangleNumber = triangleNumber(n);
}
}
The question here is that "N" neednt be the same for both star and triangle numbers. So you can increase "n" when computing both star and triangle numbers, rather keep on increasing the triangle number as long as its less the current star number. Essentially you need to maintain two variable "n" and "m".
The first problem is that you only call the starNumber() method once, outside the loop. (And the same with triangleNumber().)
A secondary problem is that unless Integer.MAX_VALUE is a star number, your loop will run forever. The reason being that Java numerical operations overflow silently, so if your next star number would be bigger than Integer.MAX_VALUE, the result would just wrap around. You need to use longs to detect if a number is bigger than Integer.MAX_VALUE.
The third problem is that even if you put all the calls into the loop, it would only display star number/triangle number pairs that share the same n value. You need to have two indices in parallel, one for star number and another for triangle numbers and increment one or the other depending on which function returns the smaller number. So something along these lines:
while( starNumber and triangleNumber are both less than or equal to Integer.MAX_VALUE) {
while( starNumber < triangleNumber ) {
generate next starnumber;
}
while( triangleNumber < starNumber ) {
generate next triangle number;
}
if( starNumber == triangleNumber ) {
we've found a matching pair
}
}
And the fourth problem is that your triangleNumber() method is wrong, I wonder how it even compiles.
I think your methodology is flawed. You won't be able to directly make a method of isStarNumber(n) without, inside that method, testing every possible star number. I would take a slightly different approach: pre-computation.
first, find all the triangle numbers:
List<Integer> tris = new ArrayList<Integer>();
for(int i = 2, t = 1; t > 0; i++) { // loop ends after integer overflow
tris.add(t);
t += i; // compute the next triangle value
}
we can do the same for star numbers:
consider the following -
star(n) = 6*n*(n-1) + 1 = 6n^2 - 6n + 1
therefore, by extension
star(n + 1) = 6*(n+1)*n + 1 = 6n^2 + 6n +1
and, star(n + 1) - star(n - 1), with some algebra, is 12n
star(n+1) = star(n) + 12* n
This leads us to the following formula
List<Integer> stars = new ArrayList<Integer>();
for(int i = 1, s = 1; s > 0; i++) {
stars.add(s);
s += (12 * i);
}
The real question is... do we really need to search every number? The answer is no! We only need to search numbers that are actually one or the other. So we could easily use the numbers in the stars (18k of them) and find the ones of those that are also tris!
for(Integer star : stars) {
if(tris.contains(star))
System.out.println("Awesome! " + star + " is both star and tri!");
}
I hope this makes sense to you. For your own sake, don't blindly move these snippets into your code. Instead, learn why it does what it does, ask questions where you're not sure. (Hopefully this isn't due in two hours!)
And good luck with this assignment.
Here's something awesome that will return the first 4 but not the last one. I don't know why the last won't come out. Have fun with this :
class StarAndTri2 {
public static void main(String...args) {
final double q2 = Math.sqrt(2);
out(1);
int a = 1;
for(int i = 1; a > 0; i++) {
a += (12 * i);
if(x((int)(Math.sqrt(a)*q2))==a)out(a);
}
}
static int x(int q) { return (q*(q+1))/2; }
static void out(int i) {System.out.println("found: " + i);}
}

Categories