3
\$\begingroup\$

I am doing the 100 Doors problem as an exercise to practice user input and loops. I do understand the problem can be solved by outputting every square number up to the maximum number, but it defeats the purpose of the exercise. The 100 Doors problem goes like this:

\$N\$ doors are closed. In the first pass, I open all of them. In the second pass, I toggle every second door. In the third pass, I toggle every third door. I continue this until I have completed the \$N\$th pass. Find all the doors that will remain open after \$N\$ passes.

Here is the code:

import java.io.IOException;
import java.util.Scanner;

public class hundredDoors {
    static boolean hasNum = false;

    public static void main(String[] args) throws IOException {
        while (!hasNum) {
            Scanner input = new Scanner(System.in);
            int numOfDoors = 0;

            System.out.print("Please enter the number of doors: ");
            if (input.hasNextInt()) {
                hasNum = true;
                numOfDoors = input.nextInt();
                if (numOfDoors < 1) {
                    hasNum = false;
                    System.out.println("Please enter an integer.");
                    continue;
                }
                boolean[] doors = new boolean[numOfDoors];

                for (boolean door : doors) {
                    door = false;
                }

                for (int index = 1; index <= numOfDoors; index++) {
                    for (int door = index - 1; door <= numOfDoors - 1; door += index) {
                        doors[door] = !doors[door];
                    }
                }

                String output = "Doors still opened: \n";
                int loops = 0;
                for (int door = 0; door <= numOfDoors - 1; door++) {
                    if (loops == 10) {
                        output += "\n";
                        loops = 0;
                    }
                    if (doors[door]) {
                        output += (door + 1 + " ");
                        loops += 1;
                    }
                }
                input.close();
                System.out.println(output);
            }

            else {
                System.out.println("Please enter an integer.");
            }
        }
    }
}

One note for this program is that \$N\$ is supplied by the user. The other is that for formatting sakes, each line will have a maximum of 10 numbers (# of door).

My question here isn't how to make it more efficient (the most efficient method is to output every square since they have an odd number of factors as explained here) but rather if the code can be more clean/clear (ie, names of indentifiers, loops can be shortened, input validation check can be better, etc).

\$\endgroup\$
  • \$\begingroup\$ Have you tried running this code? It has an obvious bug: you have to enter the same number twice, because the first input.nextInt() result is discarded. \$\endgroup\$ – 200_success Sep 1 '18 at 17:52
  • \$\begingroup\$ @200_success I see what you mean, the bug has been solved and the question edited so it will not reappear \$\endgroup\$ – Anthony Pham Sep 1 '18 at 18:01
1
\$\begingroup\$

The program is difficult to read because all code is in the main() function. As an example, one has to jump to the end of the program in order to understand how the “get a positive integer from the user” loop works.

It is generally better to separate the I/O from the computation, and have dedicated functions for a single purpose. In your example, the overall structure could for example look like this:

static int readPositiveInteger(String prompt) throws IOException {
    // ...
}

static boolean[] openDoors(int numOfDoors) {
    // ...
}

static void printOpenDoors(boolean[] doors) {
    // ...
}

public static void main(String[] args) throws IOException {

    int numOfDoors = readPositiveInteger("Enter the number of doors: ");
    boolean[] doors = openDoors(numOfDoors);
    printOpenDoors(doors);
}

More remarks:

  • The naming convention for Java classes is upper camelcase, in your case HundredDoors.
  • The boolean hasNum should be a local variable in main().
  • boolean variables are by default initialized to false, the explicit initialization in a loop is not needed.
  • You create a new Scanner instance for each attempt to read a positive integer. A single instance is sufficient if you read lines from standard input with nextLine().
  • The message “Please enter an integer” should be “Please enter a positive integer" since that is what you need and check for.
  • Array indices start at zero whereas the door numbers start at one. If you allocate one additional boolean element for the doors array then the door number can be used as array index directly, instead of adding/subtracting one at various places. That makes the code better understandable.
  • The output can be written directly to standard output instead of concatenating it to a string first.

Putting it all together, the program could look like this:

import java.io.IOException;
import java.util.Scanner;

public class HundredDoors {

    static int readPositiveInteger(String prompt) throws IOException {
        Scanner scanner = new Scanner(System.in);
        int i = 0;
        do {
            System.out.print(prompt);
            try {
                i = Integer.parseInt(scanner.nextLine());
            } catch (NumberFormatException e){
                i = 0;
            }
            if (i <= 0) {
                System.out.println("Please enter a positive integer!");
            }
        } while (i <= 0);
        return i;
    }

    static boolean[] openDoors(int numOfDoors) {
        boolean[] doors = new boolean[numOfDoors + 1];
        for (int index = 1; index <= numOfDoors; index++) {
            for (int door = index; door <= numOfDoors; door += index) {
                doors[door] = !doors[door];
            }
        }
        return doors;
    }

    static void printOpenDoors(boolean[] doors) {
        System.out.println("Doors still opened:");
        int count = 0;
        for (int door = 1; door < doors.length; door++) {
            if (doors[door]) {
                if (count == 10) {
                    System.out.println();
                    count = 0;
                }
                count += 1;
                System.out.print(door + " ");
            }
        }
        System.out.println();
    }

    public static void main(String[] args) throws IOException {

        int numOfDoors = readPositiveInteger("Please enter the number of doors: ");
        boolean[] doors = openDoors(numOfDoors);
        printOpenDoors(doors);
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.