Speed up Magic square program











up vote
1
down vote

favorite












I have written a Java program to calculate magic squares with recursion and backtracking. A 3*3 Magic square is calculated in about 1 sec, but a 4*4 needs about 50 minutes on my laptop with Intel i5. How i can improve the performance?



import java.util.Scanner;

public class MagicSquare {

private byte square;
private byte magicNumber;
private long tmp = 0;

public MagicSquare() {

Scanner sc = new Scanner(System.in);
byte size = sc.nextByte();
square = new byte[size][size];
sc.close();
magicNumber = (byte) ((size * size * size + size) / 2);

long start = System.currentTimeMillis();
solve(0, 0);
printSquare();
long duration = System.currentTimeMillis() - start;
System.out.println(tmp);
System.out.println(duration);
}

private boolean solve(int x, int y) {
tmp++;
if (x == square.length && y == square.length - 1 && isMagic()) {
return true;
}

if (x == square.length) {
y++;
x = 0;
}

for (byte i = 1; i <= square.length * square.length; i++) {

if (containsNumber(i) == false) {

if (isValidRow(x) && isValidCol(y)) {

square[x][y] = i;

if (solve(x + 1, y) == true) {
return true;
}
}

}
}

if (x < square.length && y < square.length) {
square[x][y] = 0;
}

return false;
}

private boolean isMagic() {

int diagonal1 = 0;
int diagonal2 = 0;
int col = 0;
int row = 0;

for (int i = 0; i < square.length; i++) {

for (int j = 0; j < square[0].length; j++) {

col = col + square[j][i];
row = row + square[i][j];

if (i == 0) {
diagonal1 = diagonal1 + square[j][j];
diagonal2 = diagonal2 + square[j][square.length - j - 1];
}

}

if (col != magicNumber || row != magicNumber || diagonal1 != magicNumber || diagonal2 != magicNumber) {
return false;
}

row = 0;
col = 0;
}

return true;
}

private boolean isValidRow(int row) {

int sum = 0;

for (byte i = 0; i < square.length; i++) {
sum = sum + square[row][i];
}

if (sum <= magicNumber)
return true;

return false;
}

private boolean isValidCol(int col) {

int sum = 0;

for (byte i = 0; i < square.length; i++) {
sum = sum + square[i][col];
}

if (sum <= magicNumber)
return true;

return false;
}

public boolean containsNumber(byte value) {

for (int i = 0; i < square.length; i++) {
for (int j = 0; j < square[0].length; j++) {

if (square[i][j] == value) {
return true;
}

}
}
return false;
}

private void printSquare() {

for (int i = 0; i < square.length; i++) {
for (int j = 0; j < square[0].length; j++) {

System.out.print(square[i][j] + " ");

}
System.out.println();
}
}

public static void main(String args) {

MagicSquare m = new MagicSquare();

}
}


Any help is really appreciated.










share|improve this question




























    up vote
    1
    down vote

    favorite












    I have written a Java program to calculate magic squares with recursion and backtracking. A 3*3 Magic square is calculated in about 1 sec, but a 4*4 needs about 50 minutes on my laptop with Intel i5. How i can improve the performance?



    import java.util.Scanner;

    public class MagicSquare {

    private byte square;
    private byte magicNumber;
    private long tmp = 0;

    public MagicSquare() {

    Scanner sc = new Scanner(System.in);
    byte size = sc.nextByte();
    square = new byte[size][size];
    sc.close();
    magicNumber = (byte) ((size * size * size + size) / 2);

    long start = System.currentTimeMillis();
    solve(0, 0);
    printSquare();
    long duration = System.currentTimeMillis() - start;
    System.out.println(tmp);
    System.out.println(duration);
    }

    private boolean solve(int x, int y) {
    tmp++;
    if (x == square.length && y == square.length - 1 && isMagic()) {
    return true;
    }

    if (x == square.length) {
    y++;
    x = 0;
    }

    for (byte i = 1; i <= square.length * square.length; i++) {

    if (containsNumber(i) == false) {

    if (isValidRow(x) && isValidCol(y)) {

    square[x][y] = i;

    if (solve(x + 1, y) == true) {
    return true;
    }
    }

    }
    }

    if (x < square.length && y < square.length) {
    square[x][y] = 0;
    }

    return false;
    }

    private boolean isMagic() {

    int diagonal1 = 0;
    int diagonal2 = 0;
    int col = 0;
    int row = 0;

    for (int i = 0; i < square.length; i++) {

    for (int j = 0; j < square[0].length; j++) {

    col = col + square[j][i];
    row = row + square[i][j];

    if (i == 0) {
    diagonal1 = diagonal1 + square[j][j];
    diagonal2 = diagonal2 + square[j][square.length - j - 1];
    }

    }

    if (col != magicNumber || row != magicNumber || diagonal1 != magicNumber || diagonal2 != magicNumber) {
    return false;
    }

    row = 0;
    col = 0;
    }

    return true;
    }

    private boolean isValidRow(int row) {

    int sum = 0;

    for (byte i = 0; i < square.length; i++) {
    sum = sum + square[row][i];
    }

    if (sum <= magicNumber)
    return true;

    return false;
    }

    private boolean isValidCol(int col) {

    int sum = 0;

    for (byte i = 0; i < square.length; i++) {
    sum = sum + square[i][col];
    }

    if (sum <= magicNumber)
    return true;

    return false;
    }

    public boolean containsNumber(byte value) {

    for (int i = 0; i < square.length; i++) {
    for (int j = 0; j < square[0].length; j++) {

    if (square[i][j] == value) {
    return true;
    }

    }
    }
    return false;
    }

    private void printSquare() {

    for (int i = 0; i < square.length; i++) {
    for (int j = 0; j < square[0].length; j++) {

    System.out.print(square[i][j] + " ");

    }
    System.out.println();
    }
    }

    public static void main(String args) {

    MagicSquare m = new MagicSquare();

    }
    }


    Any help is really appreciated.










    share|improve this question


























      up vote
      1
      down vote

      favorite









      up vote
      1
      down vote

      favorite











      I have written a Java program to calculate magic squares with recursion and backtracking. A 3*3 Magic square is calculated in about 1 sec, but a 4*4 needs about 50 minutes on my laptop with Intel i5. How i can improve the performance?



      import java.util.Scanner;

      public class MagicSquare {

      private byte square;
      private byte magicNumber;
      private long tmp = 0;

      public MagicSquare() {

      Scanner sc = new Scanner(System.in);
      byte size = sc.nextByte();
      square = new byte[size][size];
      sc.close();
      magicNumber = (byte) ((size * size * size + size) / 2);

      long start = System.currentTimeMillis();
      solve(0, 0);
      printSquare();
      long duration = System.currentTimeMillis() - start;
      System.out.println(tmp);
      System.out.println(duration);
      }

      private boolean solve(int x, int y) {
      tmp++;
      if (x == square.length && y == square.length - 1 && isMagic()) {
      return true;
      }

      if (x == square.length) {
      y++;
      x = 0;
      }

      for (byte i = 1; i <= square.length * square.length; i++) {

      if (containsNumber(i) == false) {

      if (isValidRow(x) && isValidCol(y)) {

      square[x][y] = i;

      if (solve(x + 1, y) == true) {
      return true;
      }
      }

      }
      }

      if (x < square.length && y < square.length) {
      square[x][y] = 0;
      }

      return false;
      }

      private boolean isMagic() {

      int diagonal1 = 0;
      int diagonal2 = 0;
      int col = 0;
      int row = 0;

      for (int i = 0; i < square.length; i++) {

      for (int j = 0; j < square[0].length; j++) {

      col = col + square[j][i];
      row = row + square[i][j];

      if (i == 0) {
      diagonal1 = diagonal1 + square[j][j];
      diagonal2 = diagonal2 + square[j][square.length - j - 1];
      }

      }

      if (col != magicNumber || row != magicNumber || diagonal1 != magicNumber || diagonal2 != magicNumber) {
      return false;
      }

      row = 0;
      col = 0;
      }

      return true;
      }

      private boolean isValidRow(int row) {

      int sum = 0;

      for (byte i = 0; i < square.length; i++) {
      sum = sum + square[row][i];
      }

      if (sum <= magicNumber)
      return true;

      return false;
      }

      private boolean isValidCol(int col) {

      int sum = 0;

      for (byte i = 0; i < square.length; i++) {
      sum = sum + square[i][col];
      }

      if (sum <= magicNumber)
      return true;

      return false;
      }

      public boolean containsNumber(byte value) {

      for (int i = 0; i < square.length; i++) {
      for (int j = 0; j < square[0].length; j++) {

      if (square[i][j] == value) {
      return true;
      }

      }
      }
      return false;
      }

      private void printSquare() {

      for (int i = 0; i < square.length; i++) {
      for (int j = 0; j < square[0].length; j++) {

      System.out.print(square[i][j] + " ");

      }
      System.out.println();
      }
      }

      public static void main(String args) {

      MagicSquare m = new MagicSquare();

      }
      }


      Any help is really appreciated.










      share|improve this question















      I have written a Java program to calculate magic squares with recursion and backtracking. A 3*3 Magic square is calculated in about 1 sec, but a 4*4 needs about 50 minutes on my laptop with Intel i5. How i can improve the performance?



      import java.util.Scanner;

      public class MagicSquare {

      private byte square;
      private byte magicNumber;
      private long tmp = 0;

      public MagicSquare() {

      Scanner sc = new Scanner(System.in);
      byte size = sc.nextByte();
      square = new byte[size][size];
      sc.close();
      magicNumber = (byte) ((size * size * size + size) / 2);

      long start = System.currentTimeMillis();
      solve(0, 0);
      printSquare();
      long duration = System.currentTimeMillis() - start;
      System.out.println(tmp);
      System.out.println(duration);
      }

      private boolean solve(int x, int y) {
      tmp++;
      if (x == square.length && y == square.length - 1 && isMagic()) {
      return true;
      }

      if (x == square.length) {
      y++;
      x = 0;
      }

      for (byte i = 1; i <= square.length * square.length; i++) {

      if (containsNumber(i) == false) {

      if (isValidRow(x) && isValidCol(y)) {

      square[x][y] = i;

      if (solve(x + 1, y) == true) {
      return true;
      }
      }

      }
      }

      if (x < square.length && y < square.length) {
      square[x][y] = 0;
      }

      return false;
      }

      private boolean isMagic() {

      int diagonal1 = 0;
      int diagonal2 = 0;
      int col = 0;
      int row = 0;

      for (int i = 0; i < square.length; i++) {

      for (int j = 0; j < square[0].length; j++) {

      col = col + square[j][i];
      row = row + square[i][j];

      if (i == 0) {
      diagonal1 = diagonal1 + square[j][j];
      diagonal2 = diagonal2 + square[j][square.length - j - 1];
      }

      }

      if (col != magicNumber || row != magicNumber || diagonal1 != magicNumber || diagonal2 != magicNumber) {
      return false;
      }

      row = 0;
      col = 0;
      }

      return true;
      }

      private boolean isValidRow(int row) {

      int sum = 0;

      for (byte i = 0; i < square.length; i++) {
      sum = sum + square[row][i];
      }

      if (sum <= magicNumber)
      return true;

      return false;
      }

      private boolean isValidCol(int col) {

      int sum = 0;

      for (byte i = 0; i < square.length; i++) {
      sum = sum + square[i][col];
      }

      if (sum <= magicNumber)
      return true;

      return false;
      }

      public boolean containsNumber(byte value) {

      for (int i = 0; i < square.length; i++) {
      for (int j = 0; j < square[0].length; j++) {

      if (square[i][j] == value) {
      return true;
      }

      }
      }
      return false;
      }

      private void printSquare() {

      for (int i = 0; i < square.length; i++) {
      for (int j = 0; j < square[0].length; j++) {

      System.out.print(square[i][j] + " ");

      }
      System.out.println();
      }
      }

      public static void main(String args) {

      MagicSquare m = new MagicSquare();

      }
      }


      Any help is really appreciated.







      java performance algorithm recursion backtracking






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 14 hours ago

























      asked 14 hours ago









      Marten

      1235




      1235






















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          0
          down vote













          You should use try-with-resources when you create a Scanner, and should only ever open a Scanner on System.in in your main program. If you do this, you can pass the required size as an argument to the MagicSquare constructor:



          public static void main(String args) {
          try(Scanner sc = new Scanner(System.in)) {
          byte size = sc.nextByte();
          MagicSquare m = new MagicSquare(size);
          m.printSquare();
          }
          }


          I’ve left construction of the magic square in the constructor (seems appropriate), but moved printing of the square to the main program. After all, you might not always want to print the magic square.





          You have numerous inefficiencies in your implementation:



          You use square.length and (worse!) square[0].length when you could simply use size if you stored the magic square’s size as a size member.



          You are testing x < square.length && y < square.length before resetting square[x][y] = 0;. The x and y values should always be valid if you reach this step of the solve() method. But there is one small possibility of them becoming invalid. After filling in the last square...:



          if (x == square.length && y == square.length-1 && isMagic()) {
          return true;
          }


          If it turns out isMagic() returns false, the method continues, loops over all values looking for an unused one (there aren’t any), and exits the method, returning false, but only after resetting square[x][y] = 0; which is why the check for invalid coordinates is required. If instead you used:



          if (x == square.length && y == square.length-1) {
          return isMagic();
          }


          ... then the method always returns immediately, whether or not the completely filled in square is magic or not. Now, the if guarding square[x][y] = 0; becomes unnecessary.





          But the real issue comes from your algorithm as a whole. You loop over $N^2$ squares, and for each square try each of the $N^2$ values, and for each value check each of the $N^2$ squares to see if the value is already used. This is an $O(N^6)$ algorithm!



          The usage check can be reduced to $O(1)$ by storing a “used” flag for each number:



          boolean used = new boolean[size*size+1];


          or



          BitSet used = new BitSet(size*size + 1);


          Then, simply checking used[value] or used.get(value) will return whether the value has been used or not. Set the flag for the value when you store it in the square, and clear it when you replace the value. That one change will reduce your time complexity from $O(N^6)$ to $O(N^4)$.





          The next speed up can come from the observation that, if you take a solved NxN magic square, and erased one row and one column, you could trivially recreate the erased values. If you know N-1 values in a row or column, the remaining value must be the desired total less the sum of the filled in values. 1 + 8 + ? = 15 ... the missing value is 15-(1+8)=6! Of course, since you are generating candidate values, you need to ensure the computed value is (a) possible, and (b) unused.





          Adding up numbers takes time. Why keep adding the values? You could keep are running total for each row and column:



          square[x][y] = value;
          row_sum[x] += value;
          col_sum[y] += value;


          ... of course, you need to subtract the value out when backtracking, or replacing with a different candidate value.





          Magic Squares are horizontally, vertically, and rotationally symmetric. In a 4x4 magic square, there are only 3 unique locations the number “1” may appear in. The remaining 13 locations would all correspond to simple rotations or mirroring of the square. This would reduce the possible 4x4 squares from 16! permutations down to 3*15! ... and 81% reduction. However, you are not finding all permutations; you stop once the first magic square is found, so this reduction in search space likely won’t produce much savings, if any.






          share|improve this answer























            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209272%2fspeed-up-magic-square-program%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            0
            down vote













            You should use try-with-resources when you create a Scanner, and should only ever open a Scanner on System.in in your main program. If you do this, you can pass the required size as an argument to the MagicSquare constructor:



            public static void main(String args) {
            try(Scanner sc = new Scanner(System.in)) {
            byte size = sc.nextByte();
            MagicSquare m = new MagicSquare(size);
            m.printSquare();
            }
            }


            I’ve left construction of the magic square in the constructor (seems appropriate), but moved printing of the square to the main program. After all, you might not always want to print the magic square.





            You have numerous inefficiencies in your implementation:



            You use square.length and (worse!) square[0].length when you could simply use size if you stored the magic square’s size as a size member.



            You are testing x < square.length && y < square.length before resetting square[x][y] = 0;. The x and y values should always be valid if you reach this step of the solve() method. But there is one small possibility of them becoming invalid. After filling in the last square...:



            if (x == square.length && y == square.length-1 && isMagic()) {
            return true;
            }


            If it turns out isMagic() returns false, the method continues, loops over all values looking for an unused one (there aren’t any), and exits the method, returning false, but only after resetting square[x][y] = 0; which is why the check for invalid coordinates is required. If instead you used:



            if (x == square.length && y == square.length-1) {
            return isMagic();
            }


            ... then the method always returns immediately, whether or not the completely filled in square is magic or not. Now, the if guarding square[x][y] = 0; becomes unnecessary.





            But the real issue comes from your algorithm as a whole. You loop over $N^2$ squares, and for each square try each of the $N^2$ values, and for each value check each of the $N^2$ squares to see if the value is already used. This is an $O(N^6)$ algorithm!



            The usage check can be reduced to $O(1)$ by storing a “used” flag for each number:



            boolean used = new boolean[size*size+1];


            or



            BitSet used = new BitSet(size*size + 1);


            Then, simply checking used[value] or used.get(value) will return whether the value has been used or not. Set the flag for the value when you store it in the square, and clear it when you replace the value. That one change will reduce your time complexity from $O(N^6)$ to $O(N^4)$.





            The next speed up can come from the observation that, if you take a solved NxN magic square, and erased one row and one column, you could trivially recreate the erased values. If you know N-1 values in a row or column, the remaining value must be the desired total less the sum of the filled in values. 1 + 8 + ? = 15 ... the missing value is 15-(1+8)=6! Of course, since you are generating candidate values, you need to ensure the computed value is (a) possible, and (b) unused.





            Adding up numbers takes time. Why keep adding the values? You could keep are running total for each row and column:



            square[x][y] = value;
            row_sum[x] += value;
            col_sum[y] += value;


            ... of course, you need to subtract the value out when backtracking, or replacing with a different candidate value.





            Magic Squares are horizontally, vertically, and rotationally symmetric. In a 4x4 magic square, there are only 3 unique locations the number “1” may appear in. The remaining 13 locations would all correspond to simple rotations or mirroring of the square. This would reduce the possible 4x4 squares from 16! permutations down to 3*15! ... and 81% reduction. However, you are not finding all permutations; you stop once the first magic square is found, so this reduction in search space likely won’t produce much savings, if any.






            share|improve this answer



























              up vote
              0
              down vote













              You should use try-with-resources when you create a Scanner, and should only ever open a Scanner on System.in in your main program. If you do this, you can pass the required size as an argument to the MagicSquare constructor:



              public static void main(String args) {
              try(Scanner sc = new Scanner(System.in)) {
              byte size = sc.nextByte();
              MagicSquare m = new MagicSquare(size);
              m.printSquare();
              }
              }


              I’ve left construction of the magic square in the constructor (seems appropriate), but moved printing of the square to the main program. After all, you might not always want to print the magic square.





              You have numerous inefficiencies in your implementation:



              You use square.length and (worse!) square[0].length when you could simply use size if you stored the magic square’s size as a size member.



              You are testing x < square.length && y < square.length before resetting square[x][y] = 0;. The x and y values should always be valid if you reach this step of the solve() method. But there is one small possibility of them becoming invalid. After filling in the last square...:



              if (x == square.length && y == square.length-1 && isMagic()) {
              return true;
              }


              If it turns out isMagic() returns false, the method continues, loops over all values looking for an unused one (there aren’t any), and exits the method, returning false, but only after resetting square[x][y] = 0; which is why the check for invalid coordinates is required. If instead you used:



              if (x == square.length && y == square.length-1) {
              return isMagic();
              }


              ... then the method always returns immediately, whether or not the completely filled in square is magic or not. Now, the if guarding square[x][y] = 0; becomes unnecessary.





              But the real issue comes from your algorithm as a whole. You loop over $N^2$ squares, and for each square try each of the $N^2$ values, and for each value check each of the $N^2$ squares to see if the value is already used. This is an $O(N^6)$ algorithm!



              The usage check can be reduced to $O(1)$ by storing a “used” flag for each number:



              boolean used = new boolean[size*size+1];


              or



              BitSet used = new BitSet(size*size + 1);


              Then, simply checking used[value] or used.get(value) will return whether the value has been used or not. Set the flag for the value when you store it in the square, and clear it when you replace the value. That one change will reduce your time complexity from $O(N^6)$ to $O(N^4)$.





              The next speed up can come from the observation that, if you take a solved NxN magic square, and erased one row and one column, you could trivially recreate the erased values. If you know N-1 values in a row or column, the remaining value must be the desired total less the sum of the filled in values. 1 + 8 + ? = 15 ... the missing value is 15-(1+8)=6! Of course, since you are generating candidate values, you need to ensure the computed value is (a) possible, and (b) unused.





              Adding up numbers takes time. Why keep adding the values? You could keep are running total for each row and column:



              square[x][y] = value;
              row_sum[x] += value;
              col_sum[y] += value;


              ... of course, you need to subtract the value out when backtracking, or replacing with a different candidate value.





              Magic Squares are horizontally, vertically, and rotationally symmetric. In a 4x4 magic square, there are only 3 unique locations the number “1” may appear in. The remaining 13 locations would all correspond to simple rotations or mirroring of the square. This would reduce the possible 4x4 squares from 16! permutations down to 3*15! ... and 81% reduction. However, you are not finding all permutations; you stop once the first magic square is found, so this reduction in search space likely won’t produce much savings, if any.






              share|improve this answer

























                up vote
                0
                down vote










                up vote
                0
                down vote









                You should use try-with-resources when you create a Scanner, and should only ever open a Scanner on System.in in your main program. If you do this, you can pass the required size as an argument to the MagicSquare constructor:



                public static void main(String args) {
                try(Scanner sc = new Scanner(System.in)) {
                byte size = sc.nextByte();
                MagicSquare m = new MagicSquare(size);
                m.printSquare();
                }
                }


                I’ve left construction of the magic square in the constructor (seems appropriate), but moved printing of the square to the main program. After all, you might not always want to print the magic square.





                You have numerous inefficiencies in your implementation:



                You use square.length and (worse!) square[0].length when you could simply use size if you stored the magic square’s size as a size member.



                You are testing x < square.length && y < square.length before resetting square[x][y] = 0;. The x and y values should always be valid if you reach this step of the solve() method. But there is one small possibility of them becoming invalid. After filling in the last square...:



                if (x == square.length && y == square.length-1 && isMagic()) {
                return true;
                }


                If it turns out isMagic() returns false, the method continues, loops over all values looking for an unused one (there aren’t any), and exits the method, returning false, but only after resetting square[x][y] = 0; which is why the check for invalid coordinates is required. If instead you used:



                if (x == square.length && y == square.length-1) {
                return isMagic();
                }


                ... then the method always returns immediately, whether or not the completely filled in square is magic or not. Now, the if guarding square[x][y] = 0; becomes unnecessary.





                But the real issue comes from your algorithm as a whole. You loop over $N^2$ squares, and for each square try each of the $N^2$ values, and for each value check each of the $N^2$ squares to see if the value is already used. This is an $O(N^6)$ algorithm!



                The usage check can be reduced to $O(1)$ by storing a “used” flag for each number:



                boolean used = new boolean[size*size+1];


                or



                BitSet used = new BitSet(size*size + 1);


                Then, simply checking used[value] or used.get(value) will return whether the value has been used or not. Set the flag for the value when you store it in the square, and clear it when you replace the value. That one change will reduce your time complexity from $O(N^6)$ to $O(N^4)$.





                The next speed up can come from the observation that, if you take a solved NxN magic square, and erased one row and one column, you could trivially recreate the erased values. If you know N-1 values in a row or column, the remaining value must be the desired total less the sum of the filled in values. 1 + 8 + ? = 15 ... the missing value is 15-(1+8)=6! Of course, since you are generating candidate values, you need to ensure the computed value is (a) possible, and (b) unused.





                Adding up numbers takes time. Why keep adding the values? You could keep are running total for each row and column:



                square[x][y] = value;
                row_sum[x] += value;
                col_sum[y] += value;


                ... of course, you need to subtract the value out when backtracking, or replacing with a different candidate value.





                Magic Squares are horizontally, vertically, and rotationally symmetric. In a 4x4 magic square, there are only 3 unique locations the number “1” may appear in. The remaining 13 locations would all correspond to simple rotations or mirroring of the square. This would reduce the possible 4x4 squares from 16! permutations down to 3*15! ... and 81% reduction. However, you are not finding all permutations; you stop once the first magic square is found, so this reduction in search space likely won’t produce much savings, if any.






                share|improve this answer














                You should use try-with-resources when you create a Scanner, and should only ever open a Scanner on System.in in your main program. If you do this, you can pass the required size as an argument to the MagicSquare constructor:



                public static void main(String args) {
                try(Scanner sc = new Scanner(System.in)) {
                byte size = sc.nextByte();
                MagicSquare m = new MagicSquare(size);
                m.printSquare();
                }
                }


                I’ve left construction of the magic square in the constructor (seems appropriate), but moved printing of the square to the main program. After all, you might not always want to print the magic square.





                You have numerous inefficiencies in your implementation:



                You use square.length and (worse!) square[0].length when you could simply use size if you stored the magic square’s size as a size member.



                You are testing x < square.length && y < square.length before resetting square[x][y] = 0;. The x and y values should always be valid if you reach this step of the solve() method. But there is one small possibility of them becoming invalid. After filling in the last square...:



                if (x == square.length && y == square.length-1 && isMagic()) {
                return true;
                }


                If it turns out isMagic() returns false, the method continues, loops over all values looking for an unused one (there aren’t any), and exits the method, returning false, but only after resetting square[x][y] = 0; which is why the check for invalid coordinates is required. If instead you used:



                if (x == square.length && y == square.length-1) {
                return isMagic();
                }


                ... then the method always returns immediately, whether or not the completely filled in square is magic or not. Now, the if guarding square[x][y] = 0; becomes unnecessary.





                But the real issue comes from your algorithm as a whole. You loop over $N^2$ squares, and for each square try each of the $N^2$ values, and for each value check each of the $N^2$ squares to see if the value is already used. This is an $O(N^6)$ algorithm!



                The usage check can be reduced to $O(1)$ by storing a “used” flag for each number:



                boolean used = new boolean[size*size+1];


                or



                BitSet used = new BitSet(size*size + 1);


                Then, simply checking used[value] or used.get(value) will return whether the value has been used or not. Set the flag for the value when you store it in the square, and clear it when you replace the value. That one change will reduce your time complexity from $O(N^6)$ to $O(N^4)$.





                The next speed up can come from the observation that, if you take a solved NxN magic square, and erased one row and one column, you could trivially recreate the erased values. If you know N-1 values in a row or column, the remaining value must be the desired total less the sum of the filled in values. 1 + 8 + ? = 15 ... the missing value is 15-(1+8)=6! Of course, since you are generating candidate values, you need to ensure the computed value is (a) possible, and (b) unused.





                Adding up numbers takes time. Why keep adding the values? You could keep are running total for each row and column:



                square[x][y] = value;
                row_sum[x] += value;
                col_sum[y] += value;


                ... of course, you need to subtract the value out when backtracking, or replacing with a different candidate value.





                Magic Squares are horizontally, vertically, and rotationally symmetric. In a 4x4 magic square, there are only 3 unique locations the number “1” may appear in. The remaining 13 locations would all correspond to simple rotations or mirroring of the square. This would reduce the possible 4x4 squares from 16! permutations down to 3*15! ... and 81% reduction. However, you are not finding all permutations; you stop once the first magic square is found, so this reduction in search space likely won’t produce much savings, if any.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited 21 mins ago

























                answered 1 hour ago









                AJNeufeld

                3,814317




                3,814317






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f209272%2fspeed-up-magic-square-program%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Morgemoulin

                    Scott Moir

                    Souastre