Art & Science of Java Chapter 4, Exercise 8 - java

I'm trying to write a program that does a countdown to liftoff, but using a while loop, instead of an for loop.
So far, all I succeed in doing is creating an infinite loop, even though I'm using the same basic principles as the for loop code.
import acm.program.*;
public class CountDownWhile extends ConsoleProgram {
public void run() {
int t = START;
while (t >= 0); {
println(t);
t = t--;
}
println("Liftoff!");
}
private static final int START = 10;
}

There are two error in your code. And that's the reason you're getting an infinite loop
1.
while (t >= 0);
You shouldn't add a semi-colon after this line, because it is actually means a while loop with nothing in it.
2.
t = t--;
you can check this question to learn more about this syntax : Is there a difference between x++ and ++x in java?
In short, the value of t-- are still 10, so t = t-- dose not change the value of t.
The loop should look like this:
while (t >= 0) {
println(t);
t--;
}
println("Liftoff!");

The first problem is the ; after the while loop. Try removing the ;...
public void run() {
int t = START;
while (t >= 0); { /// <------ Problem 1. Correct: while(t>=0)
println(t);
t = t--; /// <------ Problem 2. Correct: t--;
}
println("Liftoff!");
}
and the second problem is
t=t--;
as the value of t remains unchanged.

Related

Find bug in implementation of algorithm that finds minimum of an array of integers

Recently, I tried to write a Java program which searches for the minimum of an array.
I tried to write it in a different way, I know there are more simple ways to do that but I want to know why my program does not work.
Here is the source code :
public int minimum(int [] t) {
int min,i,j;
i=j=t.length/2;
min=t[t.length/2];
while(j!=0 || i!=t.length-1) {
while( t[i]>=min) {
i++;
if(i==t.length) {
i=t.length-1;
continue;
}
}
while(t[j]>=min) {
j--;
if(j==-1) {
j=0;
continue;
}
}
if(t[i]<=min && t[j]<=min) {
if(t[i]<=t[j]) min=t[i];
else min=t[j];
}
}
return min;
}
Thanks.
Before you read the answer you should try debugging your code to figure this out by yourself.
I think your code loops infinitely in one of those inner while loops because the end condition
if(i==t.length) {
i=t.length-1;
continue;
}
only resets the i one step back and the continue restarts the while loop. You probably meant to have the break keyword there instead of the continue in which case your code will continue with the other inner while loop.
there is some logic errors in my code , and it get infinitely going through the two loops , i fixed the loops by changing continue with break and i modify the last condition by setting || instead of && (that was a logic mistake), and it works now .
thanks guys.
here is the new source code:
public int minimum(int [] t) {
int min,i,j;
i=j=t.length/2;
min=t[t.length/2];
while(j!=0 || i!=t.length-1) {
while( t[i]>=min) {
i++;
if(i==t.length) {
i=t.length-1;
break;
}
}
while(t[j]>=min) {
j--;
if(j==-1) {
j=0;
break;
}
}
if(t[i]<=min || t[j]<=min) {
if(t[i]<=t[j]) min=t[i];
else min=t[j];
}
}
return min;
}

Shifting history in command-pattern with undo/redo?

I'm having a problem concerning a command pattern with undo/redo function. The simple problem is, when my history is full, I want to remove the least recently used command from the history and add the new one on execute.
I got this code snippet from my professor:
public class CommandHistory implements CommandInterface{
private static final int MAX_COMMANDS = 2;
private Command[] history = new Command[MAX_COMMANDS];
private int current = -1;
#Override
public void execute(Command command) {
current++;
if (current == MAX_COMMANDS){ // if full, then shift
for (int i = 0; i < MAX_COMMANDS - 1; i++){
history[i] = history[i+1];
}
}
history[current] = command;
history[current].execute();
}
In really doubt the if-clause is incorrect, because the current command index remains 2 and only command at index 0 is shifted to 1. But he says this is the way to go. What am I missing?
The loop itself is fine, but two problems:
You're quite correct that when current == MAX_COMMANDS is true and you do the loop, current is incorrect and needs adjusting.
From a maintenance perspective, current == MAX_COMMANDS is the wrong comparison, it should be current == history.length. (Otherwise, it's easy to change the initialization of history to use something other than MAX_COMMANDS but forget to change every check like current == MAX_COMMANDS.)
I would check current before incrementing it, and only increment it if you're not shifting the contents down:
public void execute(Command command) {
if (current == history.length - 1){ // if full, then shift
for (int i = 0; i < history.length - 1; i++) {
history[i] = history[i+1];
}
} else {
current++;
}
history[current] = command;
history[current].execute();
}

How to Make Program flow control jump back to a former loop in java?

So I have written a code that allows a user to find a word in a TextArea. I have nearly succeeded but for one thing. here, I will show you all the code and tell my problem.
if(ta.getText().length() != 0 && t1.getText().length() != 0)
{
char c1[] = ta.getText().trim().toCharArray();
char c2[] = t1.getText().trim().toCharArray();
for(int i=startFlag;i<c1.length;i++)
{
if(c1[i]==c2[0])
{
start = i;
break;
}
}
k=start;
for(int i=0;i<c2.length;i++)
{
if(c2[i] != c1[start++])
{
}
else
countFlag++;
}
if(countFlag==c2.length)
{
ta.select(k,c2.length);
startFlag++;
}
}
For reference, ta is the TextArea and t1 is the TextField where the user enters a word to find. i have a problem in the second for loop. What should I write in the if () block there so that whenever c2[i] != c1[start++] the control is shifted to the first for loop, that would again determine the value of start?
Create a method to get "start" that you can then call whenever you want.
if(ta.getText().length() != 0 && t1.getText().length() != 0)
{
char c1[] = ta.getText().trim().toCharArray();
char c2[] = t1.getText().trim().toCharArray();
k=getStart(startFlag, c1.length);
for(int i=0;i<c2.length;i++)
{
if(c2[i] != c1[start++])
{
start = getStart(startFlag, c1.length);
}
else
countFlag++;
}
if(countFlag==c2.length)
{
ta.select(k,c2.length);
startFlag++;
}
}
And getStart() is:
public int getStart(int startFlag, int length) {
for(int i=startFlag;i<length;i++)
{
if(c1[i]==c2[0])
{
return i;
}
}
}
You may need different inputs to getStart(), but hopefully this gets across the general idea.
The way your code is set up right now, what you're asking for is impossible. To do what you're asking, you'll need to refactor your current code into different methods. More specifically, you'll need to put the for loops into their own methods and then call these methods.
So what you would need to do is make a separate method for the for loop.
public static int startForLoop(int i) {
for(int i=startFlag;i<c1.length;i++)
{
if(c1[i]==c2[0])
{
start = i;
break;
}
}
}
Then you can just call startForLoop(0) initially and in the 2nd for loops if statment:
if(c2[i] != c1[start++])
{
startForLoop(start+1)
}
This will continue the for loop where it left off. If you need to run the 2nd for loop again then you have to make a separate method for it as well and basically place both of them in a while loop that continues till you find the result you want in the 2nd for loop.
May be this code piece help you what you are looking for.
Basically it moves along with the string to be searched in keeping in mind the index of the string to be searched for.
Sorry but i have implemented it in java, but the notion is same and the result returned is the best what i got.you must give it a try!
private static String searchString(String searchIN,String searchFOR){
if (searchFOR != "") {
int index = searchIN.toUpperCase().indexOf(searchFOR.toUpperCase());
String before = "";
String highlighted = "";
String after = "";
while (index >= 0) {
int len = searchFOR.length();
before = searchIN.substring(0, index);
highlighted = "\"" + searchFOR + "\"";//do what ever you want to do with searched string
after = searchIN.substring(index + len);
searchIN = before + highlighted + after;
index = searchIN.toUpperCase().indexOf(searchFOR.toUpperCase(), index + highlighted.length());
}
}
return searchIN;
}

Project Euler prob. 3 IndexOutOfBoundsException

I'm trying to use a Sieve of Eratosthenes method for finding the largest prime factor of a large number (problem 3 in Project Euler).
My syntax seems to be correct, and i am using Long (not int), but I'm getting the following error message:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at problem3.ProblemThree.Factor(ProblemThree.java:49)
at problem3.ProblemThree.Recursion(ProblemThree.java:37)
at problem3.ProblemThree.main(ProblemThree.java:83)
I don't know why this is happening. Could somebody please tell me what I'm doing wrong here?
package problem3;
import java.util.List;
import java.util.ArrayList;
public class ProblemThree
{
//initializing variables and lists
long factorNo;
long nowTesting;
int i;
List<Long> allPrimeList = new ArrayList<Long>();
List<Long> ourPrimes = new ArrayList<Long>();
ProblemThree(long x) //constructor; the input "x" is the number whose highest prime factor is being sought
{
factorNo = x;
}
void initialize() //use the workaround initialization (add 2 to the allPrimesList, set nowTesting to 3).
//If the factorNo is even, add 2 to the primes list
//TODO: need more elegant solution
{
allPrimeList.add((long) 2);
nowTesting=3;
if(factorNo % 2 == 0) ourPrimes.add((long) 2);
i = 0;
}
void recursion() //keep factoring the next nowTesting until the next nowTesting is greater than half of the factorNo
{
while (nowTesting <= (factorNo/2))
{
nowTesting = factor(nowTesting);
}
System.out.println(ourPrimes);
}
long factor(long t) //The factorization algorithm. Lists all the factors of long t
{
nowTesting = t;
// Line 49:
if ((nowTesting % allPrimeList.get(i)) == 0)
{
i = 0;
return (nowTesting + 2);
}
else
if(i <= allPrimeList.size()) //if we have not yet reached the end of ourPrimeList
{
i++;
return nowTesting;
}
else //if the end of ourPrimeList has been reached without a single modulus==0, this number is a prime
{
allPrimeList.add(nowTesting);
if(factorNo%nowTesting==0) //if the nowTesting is a prime factor of factorNo, it will be perfectly divisible
{
ourPrimes.add(nowTesting);
}
i=0;
return (nowTesting+2);
}
}
public static void main (String[] args)
{
ProblemThree pt = new ProblemThree(600851475143L);
pt.initialize();
pt.recursion();
}
}
thank you everyone for patiently wading through my code, I realize that it must have been excruciatingly painful :)
I have just solved the problem. My previous approach seems very complicated in retrospect. This is the final solution I used, quite a bit more elegant, although it still has room for improvement:
//second attempt from the ground up!
package problem3;
public class BiggestPrime
{
long lInput;
long factorTest;
long currentHeight;
boolean divided;
public BiggestPrime(long n)
{
factorTest = 2;
currentHeight = n;
System.out.println("The prime factors of " + n + " are:");
while (factorTest<currentHeight)
{
if (divided == true) {factorTest = 2; divided = false;}
if (factorTest > currentHeight) {System.out.println("factorTest is greater than currentHeight; breaking"); break;}
if (currentHeight%factorTest==0)
{
System.out.println(factorTest);
currentHeight /= factorTest;
divided = true;
}
else { factorTest = factorTest + 1L; divided = false;}
}
if (factorTest == currentHeight)
{
System.out.println(factorTest);
}
System.out.println("The end");
}
public static void main (String[] args)
{
BiggestPrime bp = new BiggestPrime(600851475143L);
}
}
An interesting approach. Of course, nobody should solve your Euler challenges. But did you know that the second time, you enter 'factor' nowTesting is 3?
// The factorization algorithm. Lists all the factors of long t
long factor (final long nowTesting)
{
System.out.println ("entering factor: " + nowTesting);
Minor ideas:
allPrimeList.add ((long) 2);
can be written:
allPrimeList.add (2L);
and you pobably recognized the "final" in front of the 'long' parameter in factor? It helps reasoning about code, if you mark everything which isn't changed final. In practise, the consequence is, that your Javacode is cluttered with 'final' modifiers, but that's how it is. It's a sign of good code - maybe not of good design. Final could have been the default.
At line 49, shouldn't you be checking if nowTesting is divisible by i, not the ith element of allPrimes?

Why is my loop skipping this conditional?

Time for my daily newbie Java question :-D
I must not be understanding conditionals in a while loop correctly.
I have this:
while (true){
if (){
...
} else {
...
}
if (){
...
} else {
...
}
if (SENTINEL){
break;
}
}
The first if/else statement is working, and the sentinel is working, but the second if statement gets skipped. If I flip the first and second if statement, then the first if statement still always gets executed and skips the second. What am I missing?
Can I have two if/else statements in one block like this?
I'll include the whole code, though it's pretty ugly, and I'm sure I'll get lots of people telling me better ways of doing this. I don't mind learning better ways, but for now, I just want an answer to this looping question. thanks!
public class FindRange extends ConsoleProgram {
private static final int SENTINEL = 0;
int value = 0;
int highNumber = 0;
int latestValue = 0;
int lowNumber = 0;
public void run() {
addNumbers();
}
private void addNumbers(){
value = readInt("Enter number:");
while(true){
if (value == SENTINEL){
break;
}
latestValue = readInt("Enter number:");
getHighNumber();
getLowNumber();
if (latestValue == SENTINEL){
break;
}
}
println("High Number is "+highNumber+".");
println("Low Number is "+lowNumber+".");
}
private void getHighNumber(){
if (latestValue >= value){
highNumber = latestValue;
}else {
highNumber = value;
}
}
private void getLowNumber(){
if (latestValue <= value){
lowNumber = latestValue;
}else {
lowNumber = value;
}
}
}
Are you trying to find the minimum and maximum of a series of numbers? If so, you should definitely use Math.min() and Math.max(). It's much clearer that way and you can do away with the if statements. It's also simple enough to do it in the loop with local variables instead of fields.
The common idiom is something like this:
minValue = Math.min(minValue, candidateValue);
maxValue = Math.max(maxValue, candidatevalue);
It's possible that the behavior you're seeing comes from the fact that you are always comparing the latest value to the initial value. The initial value will never change-- so if you put in the following input:
20, 60, 50
the high value that gets reported would be 50. That's because 50 is the most recent value to be greater than 20. I think you probably mean to compare the latest value to the high value, no?
You can definitely have 2 if/else blocks within the loop; however if your sentinel gets hit the loop will exit.
Posting the entire block would help.
What will happen (after reading the posted code) is when any new value you enter within the loop is greater than the original value, lowNumber is set back to the original. So for example if your input is:
7 6 5 8
Your corresponding low number values will be:
7 6 5 7
Which is incorrect. What you could do is toast the "value" variable altogether, set your low and high to the original value, then compare latest with low and high in the get* methods.
Shouldn't you be setting value = latestValue at the bottom of your while loop?
Value never gets updated after the initial read... maybe something like this:
public class FindRange extends ConsoleProgram {
private static final int SENTINEL = 0;
public void run() {
addNumbers();
}
private void addNumbers() {
int value = 0;
// Set this to highest possible value
int highNumber = Integer.MIN_VALUE;
// Set this to lowest possible value
int lowNumber = Integer.MAX_VALUE;
while (true) {
value = readInt("Enter number:");
if (value == SENTINEL)
break;
lowNumber = Math.min(lowNumber, value);
highNumber = Math.max(highNumber, value);
}
println("High Number is " + Integer.toString(highNumber) + ".");
println("Low Number is " + Integer.toString(lowNumber) + ".");
}
}

Categories