How to convert recursion to iteration? [closed] - java

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
This question is asked few times but I still find it quite difficult to convert easily readable and intuitive code into iterative code. For example I was practicing a coding question and I am given 26 integers which indicate how many times each character appears in the string. I should print all possible strings. Following is my recursive code
private static void combinatorial(String prefix, ArrayList<Integer> remainingToFill,
int totalLength) {
if (prefix.length() == totalLength) {
System.out.println(prefix);
}
for (int i = 0; i < remainingToFill.size(); i++) {
if (remainingToFill.get(i) > 0) {
ArrayList<Integer> toFill = new ArrayList<>(remainingToFill);
toFill.set(i, toFill.get(i) - 1);
combinatorial(prefix + (char) ('a' + i), toFill, totalLength);
}
}
}
I coded iterative version of this but the resultant function is much more complex and not readable and took more time for me to code it. How do I tackle this kind of problem? Is there any simple technique I can follow which would lead to easy and readable code?

Well, the reason programming languages support recursive expression of programs is that it's often simpler than explicit iterative forms. So your question is almost self-answering.
However, there really is a methodical way to convert recursive forms to iterative forms that always works. It helps to have a language with goto, which Java doesn't.
First let's clean up the java. We want to use a minimum number of arguments and local variables because what's left must go on our explicit stack. Here we go. We do away with all except i and prefix.
class CombinationLister {
private final int[] counts;
private final int length;
CombinationLister(int[] counts) {
this.counts = counts.clone();
this.length = Arrays.stream(counts).sum();
}
private void list(String prefix) {
if (prefix.length() == length) {
System.out.println(prefix);
}
for (int i = 0; i < counts.length; i++) {
if (counts[i] > 0) {
--counts[i];
list(prefix + (char) ('a' + i));
++counts[i];
}
}
}
void run() {
list("");
}
}
Now let's transcribe to C, which has goto. Here it's easy to eliminate even prefix by adding a global string buffer.
#include <stdio.h>
char str[100];
int counts[] = { 1, 2, 3 };
int n_counts = 3;
int total_count = 6;
int len = 0;
void list(void) {
if (len == total_count) printf("%.*s\n", total_count, str);
for (int i = 0; i < n_counts; i++) {
if (counts[i] > 0) {
str[len] = 'a' + i;
--counts[i];
++len;
list();
--len;
++counts[i];
}
}
}
Now, the rules are:
Build a stack of records with one field for each local variable and parameter. Here we have only i left, so we don't even need records. A stack of ints will do.
Replace the recursive call site with
a push onto the stack, then
reset the parameters to their new values (here we don't have any), then
jump to the start of the function, and
immediately after the jump, put a label rtn:.
At the end of the function, add an epilog that checks whether the stack is empty. If not, it pops the stack and jumps to rtn:.
These rules essentially mimic the code the compiler will generate to handle recursive calls.
Putting it all together, we have:
int stk[100];
int p = 0; // stack pointer
void list(void) {
int i;
start:
if (len == total_count) printf("%.*s\n", total_count, str);
for (i = 0; i < n_counts; i++) {
if (counts[i] > 0) {
str[len] = 'a' + i;
--counts[i];
++len;
stk[p++] = i; // push i on stack
goto start;
rtn:
--len;
++counts[i];
}
}
// epilog
if (p > 0) {
i = stk[--p]; // restore i from stack
goto rtn;
}
}
If you follow the steps carefully, your code will run first try every time. The only additional tip is that when there is more than one recursive call site, you'll need one return label for each rtn1:, rtn2:, etc. and an extra int field in the stack that connotes the return site, with a switch statement in the epilog to jump to the correct one.
Of course this isn't pretty code. We'd like to get rid of the gotos. It turns out this is always possible by doing "algebra" to convert the gotos to loops. There are a couple of dozen transformation techniques...too many to describe here. It's a lot like simplifying an equation in math class. Sometimes it's necessary to add Boolean flags. In this case, though, we don't need to. I finished with this:
void list(void) {
for (int i = 0;;) {
while (i < n_counts && counts[i] == 0) i++;
if (i < n_counts) {
--counts[i];
str[len] = 'a' + i;
stk[p++] = i;
if (++len == total_count) printf("%.*s\n", total_count, str);
i = 0;
} else if (p > 0) {
i = stk[--p];
--len;
++counts[i++];
}
else break;
}
}
Just for fun, back to Java:
class CombinationLister {
private final int[] counts;
private final char[] str;
private final int[] stk;
private int p = 0;
private int len = 0;
CombinationLister(int[] counts) {
this.counts = counts.clone();
this.str = new char[Arrays.stream(counts).sum()];
this.stk = new int[str.length];
}
void run() {
for (int i = 0;;) {
while (i < counts.length && counts[i] == 0) i++;
if (i < counts.length) {
--counts[i];
str[len] = (char) ('a' + i);
stk[p++] = i;
if (++len == str.length) System.out.println(str);
i = 0;
} else if (p > 0) {
i = stk[--p];
--len;
++counts[i++];
} else break;
}
}
}

public static void combinatorial(ArrayList<Integer> remainingToFill, int totalLength) {
Stack st = new Stack();
st.push( new Pair<String,Integer>("", 0) );
while( !st.empty() ){
Pair<String,Integer> top = (Pair<String,Integer>) st.pop();
String prefix = top.getKey();
Integer i = top.getValue();
if (prefix.length() == totalLength) {
System.out.println(prefix);
int index= prefix.charAt(prefix.length()-1 )-'a' ;
remainingToFill.set( index , remainingToFill.get(index) + 1 );
}
else{
if( i== remainingToFill.size() ){
if( prefix.length()>0 ){
int index= prefix.charAt(prefix.length()-1 )-'a' ;
remainingToFill.set( index , remainingToFill.get(index) + 1 );
}
}
else{
st.push( new Pair<String,Integer>(prefix, i+1) );
if( remainingToFill.get(i) > 0 ){
remainingToFill.set(i, remainingToFill.get(i) - 1);
st.push( new Pair<String,Integer>(prefix+(char)('a'+i), 0) );
}
}
}
}
}

Related

Recursive method to replace all occurrences of a value in a 2D array

I have created a recursive method that replaces all occurrences of an element in a two dimensional double array. The issue is that I cannot seem to get this working without encountering a stack overflow error. Could someone please look at my code below and show me how to fix this? I have tried setting this up several times over the past few days. Thank you. Note that my arrays are 2 x 3, so the first if means that if you are at column 1 row 2, you are at the end of the array, and in that case you are done searching.
private static int replaceAll(double number, double replacementTerm) {
int i = 0;
int j = 0;
double searchFor = number;
double replace = replacementTerm;
if (i == 1 && j == 2) {
System.out.println("Search complete!");
}
if (twoDimArray2[i][j] == searchFor) {
System.out.println("Replaced An Element!");
twoDimArray2[i][j] = replace;
System.out.println(twoDimArray2[i][j]);
j++;
return replaceAll(searchFor, replace);
}
if (j == twoDimArray2.length) {
i++;
return replaceAll(searchFor, replace);
} else {
j++;
return replaceAll(searchFor, replace);
}
}
i and j should be method parameters instead of local variables so changes to their values can be tracked. Try to move right and down recursively if it does not exceed the bounds of the array. Note that this is much less efficient that iteration with two layers of for loops, as it will check multiple positions in the array more than once; to mitigate this, one can use a visited array to store all positions previous visited so they will not be checked again. See the below code in action here.
private static void replaceAll(double number, double replacementTerm, int i, int j) {
double searchFor = number;
double replace = replacementTerm;
if (twoDimArray2[i][j] == searchFor) {
System.out.println("Replaced An Element!");
twoDimArray2[i][j] = replace;
System.out.println(twoDimArray2[i][j]);
}
if (i == twoDimArray2.length - 1 && j == twoDimArray2[0].length - 1) {
System.out.println("Reached the end!");
return;
}
if (i + 1 < twoDimArray2.length) {
replaceAll(number, replacementTerm, i + 1, j);
}
if (j + 1 < twoDimArray2[0].length) {
replaceAll(number, replacementTerm, i, j + 1);
}
}

Java: Optimize two searches and two lists to one

I'm trying to optimize my program by getting rid of duplicate searches or just by generally make things optimized throughout my program and I came across a method in which I can't find any "better" solution what so ever and would love to know if anyone could point me in a direction for refineing it.
First off here is my code that I'm trying to optimize:
public Player spaceBattle(Player player1, Player player2) {
ArrayList<Ship> listOfShipsPlayer1 = this.getShipsOfPlayer(player1);
ArrayList<Ship> listOfShipsPlayer2 = this.getShipsOfPlayer(player2);
Random random = new Random();
int player1hits = 0, player2hits = 0, rolledDie;
for (Ship aShip : listOfShipsPlayer1) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= aShip.getShipType().getCombatValue()) {
player1hits += 1;
}
}
for (Ship aShip : listOfShipsPlayer2) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= aShip.getShipType().getCombatValue()) {
player2hits += 1;
}
}
for (int i = 0; i < player1hits; ++i) {
if (this.getShipsOfPlayer(player2).size() > 0) {
this.listOfShips.remove(listOfShipsPlayer2.get(i));
} else {
break;
}
}
for (int i = 0; i < player2hits; ++i) {
if (this.getShipsOfPlayer(player1).size() > 0) {
this.listOfShips.remove(listOfShipsPlayer1.get(i));
} else {
break;
}
}
As you can see here I run the same algorithm twice in order to check for first Player1 and then Player2 and add update their respective hits. And then for the amount of hits for each player I then remove a ship.
What I would like to know if its possible to change this bit of code to be able to not have the duplicate code for each player but that it automatically can go through player1 first and then player2 in one loop.
Looking forward to hear from you
You can just create methods.
private int hitShips(List<Ship> ships) {
int result = 0;
for (Ship ship : ships) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= ship.getShipType().getCombatValue()) {
result++;
}
}
return result;
}
which makes your code
int player1hits = hitShips(listOfShipsPlayer1);
int player2hits = hitShips(listOfShipsPlayer2);
and similar for the removal of the ships from the list.
void removeShips(List<Ship> ships, int remove) {
int removeCount = Math.max(ships.size(), remove);
Iterator<Ship> it = ships.iterator();
for (int i = 0; i < removeCount; i++) {
it.remove();
}
}
with
removeShips(getShipsOfPlayer(player2), player1hits);
removeShips(getShipsOfPlayer(player1), player2hits);
I'm not sure why you always remove ships from the head of the lists, since combat values seems to be a thing, but that's just a side note.
I wouldn't call this "optimization" so much, but the removal of duplicate code is always good.
You could optimize removeShips as:
void removeShips(List<Ship> ships, int numHits) {
int numToRemove = Math.min(ships.size(), numHits);
if (numToRemove > 0) {
ships.subList(0, numToRemove).clear();
}
}
This method call will result in only one System.arraycopy call, which will shift all remaining elements to the left.
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#subList-int-int-

Optimisation of string & conversion operations

I had a go at writing this bit of java code which allows me to add and subtract (large) numbers which are represented using a String rather than a numeric datatype. This allows me to do basic calculations on numbers much bigger than the numeric data types can handle.
It is a bit rough and ready, I don't perform any checks on the input strings at all for instance, but for the constraints that I am using the code works fine.
I am not that familiar with java library functions (is there something instead of Character#getNumericValue that works better?) and I was wondering if there is any scope for improving the execution speed. I guess when the input numbers differ considerably in length, using the '0's to keep the loop running isn't very efficient. Perhaps I am not using a very good algorithm to do this at all. Any pointers would be welcome.
public static String addBig(String a, String b)
{
// no checks or exception handling at all ...
String result = "";
int carry = 0;
int a_int, b_int, res_int;
for(int current = 1; ; current++)
{
if(current > a.length() && current > b.length())
{
if(carry == 1)
result = "1" + result;
break;
}
a_int = a.length() - current < 0 ? 0
: Character.getNumericValue(a.charAt(a.length() - current));
b_int = b.length() - current < 0 ? 0
: Character.getNumericValue(b.charAt(b.length() - current));
res_int = a_int + b_int + carry;
if(res_int > 9)
{
res_int -= 10;
carry = 1;
}
else
carry = 0;
result = Integer.toString(res_int) + result;
}
return result;
}
public static String subtractBig(String a, String b)
{
// no checks or exception handling at all, a is expected to be bigger than b
String result = "";
int borrow = 0;
int a_int, b_int, res_int;
for(int current = 1; current <= a.length(); current++)
{
// subtract the digits
a_int = Character.getNumericValue(a.charAt(a.length() - current));
b_int = b.length() - current < 0 ? 0
: Character.getNumericValue(b.charAt(b.length()-current));
res_int = (a_int-borrow)-b_int;
if(res_int < 0)
{
res_int += 10;
borrow = 1;
}
else
borrow = 0;
result = Integer.toString(res_int) + result;
}
// tidy zeros
while(result.length() > 1 && result.charAt(0) == '0')
result = result.substring(1);
return result;
}

How to re-write or modify this code to work the same but without using a break statement

How can I get the following code work without using break statement?
I updated and added i = child to bottom of the while loop. I'm also adding these additional comments because someone edited my post and now I can't update it because I need to add more comments. Please don't alter my question. Those comments are there for extra insight. It also hinders my ability to edit my question or update my code.
private void percDown(int [] a, int i, int n) {
numOfCalls++;
int child = 0;
int tmp = a[i];
while(leftChild(i) < n) {
child = leftChild(i);
if( child != n - 1 && a[child] < a[child + 1]){
numOfComparisons++;
child++;
}
if( tmp < a[child]){
numOfComparisons++;
a[i] = a[child];
}
else
break;
i= child;
a[i] = tmp;
}
}
Just set n = 0; instead, or Integer.MIN_VALUE if these values can go negative. That will stop your loop. But don't be averse to break statements. They are used all the time out here in the real world.
Try this while condition:
while(leftChild(i) < n && (( leftChild(i) != n - 1 && a[leftChild(i)] < a[leftChild(i) + 1]) || tmp < a[leftChild(i)]))
Basically I have clubbed both your if conditions in the while condition. Everything else remains the same.
Your code is in structure like this:
while(A) {
B;
if(C){
D;
} else {
break;
}
E;
}
To tidy it up a bit; it can be written as
while(A) {
B;
if(C){
D;
E;
} else {
break;
}
}
To avoid using break, it can be done by:
while(A && !C) {
B;
if(C){
D;
E;
}
}
(You may make up a meaningful boolean flag to represent C so that it is more readable instead of putting a long, complex, hard-to-read predicate in while condition.)

How to solve exception in thread "main", java.lang.ArithmeticException: / by zero? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I keep getting an exception in thread "main":
java.lang.ArithmeticException: / by zero
at PersonalityTest.percentage(PersonalityTest.java:85)
at PersonalityTest.main(PersonalityTest.java:19)
And I want to add the count every time the scanner reads A or B and get the percentage of B.
import java.io.*;
import java.util.*;
public class PersonalityTest {
public static final int dimen = 4;
public static void main(String [] args) throws FileNotFoundException {
Scanner input = new Scanner(new File("personality.txt"));
PrintStream out = new PrintStream(new File("output.txt"));
int[] a = new int[4];
int[] b = new int[4];
welcome();
while (input.hasNextLine()) {
String letter = letter(input, out);
countNum(a, b, out, letter);
int[] percentage = percentage(a, b, out);
type(out, percentage);
out.println("");
}
}
public static void welcome () {
System.out.println("The Keirsey Temperament Sorter is a test that measures four independent dimensions of your personality: ");
System.out.println("1. Extrovert versus Introvert (E vs. I): what energizes you");
System.out.println("2. Sensation versus iNtuition (S vs. N): what you focus on");
System.out.println("3. Thinking versus Feeling (T vs. F): how you interpret what you focus on");
System.out.println("4. Judging versus Perceiving (J vs. P): how you approach life");
System.out.println("");
}
public static String letter (Scanner input, PrintStream out) {
String name = input.nextLine();
out.println(name + ":");
String letter = input.nextLine();
return letter;
}
public static void countNum(int[] a, int[] b, PrintStream out, String letter) {
int[] countA = new int[7];
int[] countB = new int[7];
int n = 0;
out.print("answers: [");
for (int i = 0; i < letter.length(); i++) {
int type = i % 7;
if (letter.charAt(i) == 'A' || letter.charAt(i) == 'a') {
n = 1;
}
else if (letter.charAt(i) == 'B' || letter.charAt(i) == 'b') {
n = 1;
}
countA[type] += n;
countB[type] += n;
if (type == 2 || type == 4 || type == 6) {
a[type / 2] = countA[type - 1]+ countA[type];
b[type / 2] = countB[type - 1]+ countA[type];
} else if (type == 0) {
a[0] = countA[0];
b[0] = countB[0];
}
for (int j = 0; j < dimen; j++) {
out.print(a[j] + "A-" + b[j] + "B," + " ");
}
}
out.print("]");
}
public static int[] percentage (int[] a, int[] b, PrintStream out) {
int[] percentage = new int [4];
out.print("percent B: [");
double n = 0.0;
for (int i = 0; i < dimen; i++) {
n = b[i] * 100 / (a[i] + b[i]); // <--- This is where I get error
percentage [i] = (int) Math.round(n);
out.print(percentage[i]);
}
out.print("]");
return percentage;
}
public static void type (PrintStream out, int[] percentage) {
String[] type = new String [4];
out.print("type: ");
for (int i = 0; i <= dimen; i++) {
while (percentage[i] > 50) {
if (i == 0) {
type[1] = "I";
}
else if (i == 1) {
type[2] = "N";
}
else if (i == 2) {
type[3] = "F";
}
else {
type[4] = "P";
}
}
while (percentage[i] < 50) {
if (i == 0) {
type[1] = "E";
}
else if (i == 1) {
type[2] = "S";
}
else if (i == 2) {
type[3] = "T";
}
else {
type[4] = "J";
}
}
out.print(Arrays.toString(type));
}
}
}
Your logic is very difficult to follow with all the a, b, +1, -1, /2, etc. You should try to reduce it to the smallest amount of code that demonstrates your problem. Odds are that you'll find the problem while you're doing that. You might also provide some sample input. Without one or both of these, it's very difficult to help with your problem.
Update: I'm seeing a number of things that look like problems now that I see what you're trying to do. Unless I'm mistaken, your input file has a name on the first line followed by 70 lines, each with a single letter on them?
For one thing, when read a letter and send it into countNum(), you only have one variable called "n" that you increment whether you see an A or a B, and then you add "n" to both A and B. That's not adding one to either the number of A's or the number of B's. It will always add one to both of them.
Next, since you only send a single letter into countNum(), the outer for loop will only execute one time. That means you'll only ever put a value into a[0] and b[0]. Further, because of the "n" problem, both values will always be 1. Thus the one time you get to the inner for loop, it will always print "1A-1B, 0A-0B, 0A-0B, 0A-0B" for the first part of the answer.
After that, it should be obvious why your percentage() method doesn't work. All but the first position of the array have zeroes in them.
Additional stuff: You define a constant "dimen" equal to 4 but you sometimes use the constant and sometimes use a literal "4". Pick one and stick with it. I'd recommend the constant, and I'd recommend naming it something meaningful, like "NUMBER_OF_PERSONALITY_DIMENSIONS", if that's what it is. For that matter, give all of your variables better names, and it will make things easier for you and me both. Also, in your type() method, you say for ( int i = 0; i <= dimen; i++ ) { to iterate over an array which I think only has 4 elements. That's going to break. Finally, as you kind of mentioned elsewhere, don't pass arrays around, mutating them in multiple different methods. That's a good way to get lost. In general, make methods non-side-effecty. Instead of modifying the things you pass to them, return the important values from the method.
In general, I think you need to take a break and straighten out in your head what you're trying to do. The logic doesn't seem to make any sense.
I don't know if you've learned about this kind of thing yet, but you might consider splitting this into two classes: one to read in the data and one to tally it up. The tallying one might look something like:
class Tallier {
private int numberOfAs;
private int numberOfBs;
private int totalEntriesSoFar;
private int[] typeATotals;
private int[] typeBTotals;
public void gotNewA() {...}
public void gotNewB() {...}
}
You are dividing by zero, the problem is that. On the mentioned line, you have:
n = b[ i ] * 100 / ( a[ i ] + b[ i ] );
Sometimes, a[i]+b[i] is zero. Maybe the problem will be solved by a check like this:
for( int i = 0; i < dimen; i++ ) {
if (a[ i ] + b[ i ]!= 0)
n = b[ i ] * 100 / ( a[ i ] + b[ i ] );
else
//assign a number to n for this situation
percentage [ i ] = ( int ) Math.round( n );
out.print( percentage[ i ] );
}
But logically, you should not divide a number by zero. Then maybe you have to correct your algorithm.

Categories

Resources