davidj has asked for the wisdom of the Perl Monks concerning the following question:

In reading through past posts lately, I have discovered that the Monks are very good at answering algorithms questions. Since my current implementation of this puzzle is in Perl, I thought I would seek wisdom here. If I do not explain the problem clearly, please do not flog me. It is not easy to describe.

First a quick definition: a Number Place Puzzle is an NxN puzzle where sqrt(N) is an integer >= 3. Accordingly, each puzzle has N columns, N rows, and N sqrt(N) x sqrt(N) internal grids. For example, if N=5, the puzzle has 25 columns, 25 rows, and 25 5x5 internal grids. The problem is to place the numbers 1 .. N^2 on each row, on each column, and in each internal grid without duplicating any numbers on the row, column, or grid.

I have written a little program in perl that will randomly generate solutions, but it is strictly brute force. And it has not successfully generated a solution for sqrt(N) > 5, or a puzzle that is 25x25.

Here is what I am doing:
1) I initialize arrays for each row, column, and grid with all the numbers that are available for placement.
2) I start at the top and go left to right, top to bottom, and place numbers one at a time.
3) To find a valid number for a given square, I generate a list of all the numbers that are common to the arrays for the row, column, and grid the given square is part of.
4) From this list I randomly choose 1 number and place it in the aquare.
5) After the number is placed, I remove it from the appropriate arrays.
6) If step #3 is successful, I proceed to the next square.
7) If step #3 fails (I cannot find any common numbers), I strip all the numbers on that row and start the row over.
8) If step #3 fails say 10,000 consecutive times for the same row, I bail out.

Using the above procedure I can currently generate 9x9, 16x16, and 25x25 solutions. The average time to generate a 16x16 solution is about 6 seconds, and for a 25x25 solution it is about 80-90 seconds. However, I cannot generate a 36x36 or above. I went so far as making 500,000 attempts allowing for 500,000 consecutive failures for a given row, and I still failed to generate even a single solution. The highest row it has successfully completed is row 27.

I will greatly appreciate any ideas or other algorithms any of you wise monks can think of. davidj
  • Comment on Better algorithm for Number Place Puzzle

Replies are listed 'Best First'.
Re: Better algorithm for Number Place Puzzle
by Abigail-II (Bishop) on May 23, 2004 at 23:03 UTC
    Not very hard. Start with the grid in the upper left corner. Assign the numbers to this grid (it doesn't matter in which order). Now copy this first grid all over, except that for each step sideways you rotate a row in the grid (for instance, put the top row at the bottom), and for each step downwards, you rotate a column (for instance, rotate the left most column to the right). Here's a program:
    #!/usr/bin/perl use strict; use warnings; no warnings qw /syntax/; my $N = 6; my $SN = $N * $N; my @chars = (0 .. 9, 'A' .. 'Z', 'a' .. 'z', '/', '+'); my $mask; foreach my $x (0 .. $N - 1) { foreach my $y (0 .. $N - 1) { $mask -> [$x] [$y] = $x * $N + $y; } } my $grid; foreach my $X (0 .. $N - 1) { foreach my $Y (0 .. $N - 1) { my $sx = $X * $N; my $sy = $Y * $N; foreach my $x ($sx .. $sx + $N - 1) { foreach my $y ($sy .. $sy + $N - 1) { $grid -> [$x] [$y] = $mask -> [($x + $Y) % $N] [($y + +$X) % $N]; } } } } # # Check correctness. # # Rows and columns. foreach my $x (0 .. $SN - 1) { my %hx; my %hy; foreach my $y (0 .. $SN - 1) { $hx {$grid -> [$x] [$y]} = 1; $hy {$grid -> [$y] [$x]} = 1; } die unless $SN == keys %hx && $SN == keys %hy; } # Grids. foreach my $x (0 .. $N - 1) { foreach my $y (0 .. $N - 1) { my %hg; foreach my $xx (0 .. $N - 1) { foreach my $yy (0 .. $N - 1) { $hg {$grid -> [$x * $N + $xx] [$y * $N + $yy]} = 1; } } die unless $SN == keys %hg; } } # Print results (assume $N <= 8). foreach my $x (0 .. @$grid - 1) { foreach my $y (0 .. @{$grid -> [$x]} - 1) { print $chars [$grid -> [$x] [$y]]; } print "\n"; } __END__ 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ 6789ABCDEFGHIJKLMNOPQRSTUVWXYZ012345 CDEFGHIJKLMNOPQRSTUVWXYZ0123456789AB IJKLMNOPQRSTUVWXYZ0123456789ABCDEFGH OPQRSTUVWXYZ0123456789ABCDEFGHIJKLMN UVWXYZ0123456789ABCDEFGHIJKLMNOPQRST 123450789AB6DEFGHCJKLMNIPQRSTOVWXYZU 789AB6DEFGHCJKLMNIPQRSTOVWXYZU123450 DEFGHCJKLMNIPQRSTOVWXYZU123450789AB6 JKLMNIPQRSTOVWXYZU123450789AB6DEFGHC PQRSTOVWXYZU123450789AB6DEFGHCJKLMNI VWXYZU123450789AB6DEFGHCJKLMNIPQRSTO 23450189AB67EFGHCDKLMNIJQRSTOPWXYZUV 89AB67EFGHCDKLMNIJQRSTOPWXYZUV234501 EFGHCDKLMNIJQRSTOPWXYZUV23450189AB67 KLMNIJQRSTOPWXYZUV23450189AB67EFGHCD QRSTOPWXYZUV23450189AB67EFGHCDKLMNIJ WXYZUV23450189AB67EFGHCDKLMNIJQRSTOP 3450129AB678FGHCDELMNIJKRSTOPQXYZUVW 9AB678FGHCDELMNIJKRSTOPQXYZUVW345012 FGHCDELMNIJKRSTOPQXYZUVW3450129AB678 LMNIJKRSTOPQXYZUVW3450129AB678FGHCDE RSTOPQXYZUVW3450129AB678FGHCDELMNIJK XYZUVW3450129AB678FGHCDELMNIJKRSTOPQ 450123AB6789GHCDEFMNIJKLSTOPQRYZUVWX AB6789GHCDEFMNIJKLSTOPQRYZUVWX450123 GHCDEFMNIJKLSTOPQRYZUVWX450123AB6789 MNIJKLSTOPQRYZUVWX450123AB6789GHCDEF STOPQRYZUVWX450123AB6789GHCDEFMNIJKL YZUVWX450123AB6789GHCDEFMNIJKLSTOPQR 501234B6789AHCDEFGNIJKLMTOPQRSZUVWXY B6789AHCDEFGNIJKLMTOPQRSZUVWXY501234 HCDEFGNIJKLMTOPQRSZUVWXY501234B6789A NIJKLMTOPQRSZUVWXY501234B6789AHCDEFG TOPQRSZUVWXY501234B6789AHCDEFGNIJKLM ZUVWXY501234B6789AHCDEFGNIJKLMTOPQRS

    Abigail

      There's even a much simpler algorithm:
      my $N = 6; my @nums = (0 .. $N * $N - 1); my $grid; foreach my $x (0 .. $N - 1) { foreach my $y (0 .. $N - 1) { push @$grid => [@nums]; push @nums => splice @nums => 0, $N; } foreach my $y (0 .. $N - 1) { splice @nums => $y * $N, $N => @nums [($y * $N + 1) .. (($y + 1) * $N - 1), $y * $N] } }

      Abigail

Re: Better algorithm for Number Place Puzzle
by hv (Prior) on May 23, 2004 at 21:44 UTC

    I think in your description you made one typo:

    For example, if N=5, the puzzle has 25 columns, ...

    .. I think should have said "if sqrt(N)=5".

    To approach this, first off I don't see any reason to choose random numbers: for a 36x36 square, there are at most 36 numbers to consider, so it seems strange to pick 10,000 random numbers rather than just try each of the 36 in turn.

    Second, the numbers have no intrinsic meaning, they are just labels - you could colour the squares using 36 different colours, and you'd still be solving the same problem. That implies in particular that given any solution you could swap, say, all the 1s and 2s around and have a new solution - it is just a different way of labelling the 36 colours using numbers. Given that, you might as well choose a fixed labelling by pre-filling the first row with the numbers in order.

    Third, there is a very easy way to construct a magic NxN square for any odd N, and I suspect a small tweak to that construction would also allow you to construct a solution for this problem for odd N. I've blanked it out below in case you don't want to know.

    Create a mask consisting of knight's moves, then shift and repeat. Eg for a 7x7 grid the mask would look like:

    x...... 1234567 ..x.... 6712345 ....x.. 4567123 ......x giving the square 2345671 .x..... 7123456 ...x... 5671234 .....x. 3456712

    Fourth, you may be able to get substantial speedups by dealing with bit vectors rather than arrays; however, I'd suggest posting your code here for more specific advice.

    Hugo

      hv,

      First, You are correct. I did make a typo which you successfully caught. Thank you.

      Second, I don't know how magic squares would help. The constraints are totally different. That is, I don't see how meeting the constraints of a magic square puzzle would successfully meet the constraints of the number place puzzle.

      Third, you are right. It would be easy to generate a single solution by simply shifting the numbers on each row, and then using matrix rules to generate other solutions, but that would take the fun out of it :)

      Finally, my concern at this point is not speed. Rather, it is the effectiveness of an algorithm to generate solutions of any size.

      davidj

        I don't know how magic squares would help. The constraints are totally different.

        If you take a standard (N x N) magic square, consisting of the numbers from 1 .. N^2, and then reduce each number module N, you will in some (all?) cases get a square consisting of the numbers 1 .. N such that each number appears exactly once in each row and column.

        The nature of your squares is identical to this case, except for the additional constraint that the N interior squares must each contain each number once. So solutions to this problem are a subset of the (n^2 x n^2) magic squares.

        For this sort of problem I'm more used to either a) showing that there is a solution (any solution) by finding one; or b) finding all solutions. In your case you seem to be aiming at a quite different goal, in which randomness plays a necessary part, but I'm not at all clear what your precise goal is.

        Finally, my concern at this point is not speed. Rather, it is the effectiveness of an algorithm to generate solutions of any size.

        Hmm, so when failing to find a solution in 500,000 random attempts you're concerned not with how long that took but with how many attempts it took? But given that you're selecting the numbers at random, I'm not sure how you'd reduce that except by weighting the numbers towards the ones more likely to give a solution, and I can't offhand see any way to do that without knowing in advance what solutions are possible.

        Sorry, I've realised that I really have no idea what you're asking for.

        Hugo

        Mmm. Seems like you think you have an easy solution, but you don't want to use it. What makes you think this problem has more than one solution?
        A massive flamewar beneath your chosen depth has not been shown here
Re: Better algorithm for Number Place Puzzle
by dash2 (Hermit) on May 23, 2004 at 22:20 UTC
    Just a guess. Perhaps you could use the other monk's blanked out solution to create an initial grid. Then, recreate this solution in the other grids, but transform it in some simple way in order to make sure it doesn't break the rules. My shot - just a first guess - is below, hidden:

    Hmm. Let's try a 2 x 2 version. The initial grid is

    12 34

    obviously. Now you need to fill in

    12-- 34-- ---- ----

    Clearly, as you move across, you need to shift rows:

    1234 3412 ---- ----

    And as you move down, you need to shift columns:

    1234 3412 21-- 43--

    note that the lower left grid is just the upper left grid with columns shifted along. Finally,

    1234 3412 2143 4321

    it turns out that the column shift from the grid above, and the row shift from the grid to the left, give identical results.

    Perhaps this will work with N>2. Or not. Enjoy.
    A massive flamewar beneath your chosen depth has not been shown here
      This can be extended to create a random solution, just randomize the initial block. (This is similar to Abigail-II's solution). Here @start is a N by N matrix whose entries are a random permutation of {1, .., N^2}.
      my $n = shift || 3; my @start = do { use List::Util 'shuffle'; my @perm = shuffle 1 .. $n*$n; map { my $row = $_; [ map { $perm[$row * $n + $_] } 0 .. $n-1 ] } 0 .. $n-1; }; my @solution; for my $x ( 0 .. $n*$n - 1 ) { for my $y ( 0 .. $n*$n - 1 ) { my $block_x = int ($x / $n); my $block_y = int ($y / $n); $solution[$x][$y] = $start[ ($block_x + $y) % $n ][ ($block_y + $x) % $n ]; } } print join "" => map { "@$_\n" } @solution;

      blokhead

Re: Better algorithm for Number Place Puzzle
by kvale (Monsignor) on May 23, 2004 at 21:49 UTC
    The description is a little vague, but I think you are describing a second-order Latin cube. As I remember from my combinatorial design class, there are efficient algorithms for generating them, none of which I have handly at the moment. A google search would probably be useful at this point.

    -Mark