Last week, I shared some code that, while imperfect, wasn’t that bad. I then issued a challenge: make it worse. Or better, if you really want. As many comments noted: one case covers only the first iteration of the loop, and one case only covers the last iteration of the loop. You could easily pull those out of the loop, and not need a for-case at all. Others noticed that this pattern looked like odd slices out of an identity matrix.

With that in mind, we got a few numpy, Matlab, or MatrixUtils based solutions generally were the “best” solutions to the problem: generate an identity matrix and take slices out of it. This is reasonable and fine. It makes perfect sense. Let’s see if we can avoid making sense.

I’ll start with Abner Qian’s Ruby solution.

module MagicalArrayGenerator
  def magical_array_generator
    main_array = []
    self.times do |i|
      inner_array = []
      self.times do |j|
        i == j ? inner_array << 1 : inner_array << 0
      end
      main_array << inner_array
    end

    e_1 = []
    n_1 = []
    e_n = []
    n_n = []

    self.times do |i|
      e_1 << [main_array[i].first]
      e_n << [main_array[i].last]
      n_1 << main_array[i][1..-1]
      n_n << main_array[i][0..-2]
    end

    [e_1, n_1, e_n, n_n]
  end
end

class Integer
  include MagicalArrayGenerator
end

e_1, n_1, e_n, n_n = 4.magical_array_generator

At it’s core, this is simply an implementation that generates an identity matrix and slices it up. The actual implementation, however, is a pitch-perfect parody of Ruby development: “There’s no problem that can’t be solved by monkey-patching a method into a built-in type”. That’s what happens here- the include statment injects this method into the build-in Integer data-type, meaning you can call 4.magical_array_generator and get your arrays. Abner also points out that Ruby uses 62-bit integers, just in case you want some 4611686018427387903 by 4611686018427387904 arrays.

Several folks looked at the idea of taking slices, and said, “Gee, I bet you I could do this with pointers in C”. My personal favorite in that category would have to be Ron P’s approach.

#include <stdio.h>

int main( int argc, char **argv)
{
    int *e_1 = 0;
    int *e_n = 0;
    int **n_1 = 0;
    int **n_n = 0;
    int *fugly = 0;
    int i,j;

    if ( argc != 2 ) return 1;

    int n = atoi(argv[1]);

    fugly = calloc( n*(n+1),sizeof(int));

    n_1 = calloc(n,sizeof(int *));
    n_n = calloc(n,sizeof(int *));

    for ( i = 0, j=n; i < n; ++i, j+=n+1 )
    {
        fugly[j]=1;
        n_1[i]=fugly+n*i;
        n_n[i]=n_1[i]+n;
    }
    e_1 = fugly+n;
    e_n = fugly+1;

    printf( "e_1\n" );
    for ( i = 0; i < n; ++i ) {
      printf( "  %d\n", e_1[i]);
    }

    printf( "\ne_n\n" );
    for ( i = 0; i < n; ++i ) {
      printf( "  %d\n", e_n[i]);
    }

    printf( "\nn_1\n" );
    for ( i = 0; i < n; ++i ) {
      printf( "  " );
      for ( j = 0; j < n-1; ++j ) {
        printf("%d ", n_1[i][j]);
      }
      printf("\n" );
    }

    printf( "\nn_n\n" );
    for ( i = 0; i < n; ++i ) {
      printf( "  " );
      for ( j = 0; j < n-1; ++j ) {
        printf("%d ", n_n[i][j]);
      }
      printf("\n" );
    }

    return 0;
}

Now, Martin Scolding gets bonus points for two reasons: first, he uses one of the worst languages in the world (not designed as an esolang), and second, this language doesn’t technically support multi-dimensional arrays. I speak, of course, of PL/SQL. Note the use of substrings to figure out what number to put in each position of the array.

DECLARE

    TYPE data_t  IS TABLE OF INTEGER INDEX BY PLS_INTEGER;
    TYPE array_t IS TABLE OF data_t  INDEX BY PLS_INTEGER;

   e_1 array_t;
   e_n array_t;
   n_1 array_t;
   n_n array_t;

   l_array_size INTEGER := 0;

PROCEDURE gen_arrays(n INTEGER, p_e_1 IN OUT array_t, p_e_n IN OUT array_t, p_n_1 IN OUT array_t, p_n_n IN OUT array_t)
--' Generate 4 Arrays of the form (example n=4)
--
--    '       | 1 |         | 0 0 0 |
--    ' e_1 = | 0 |   n_1 = | 1 0 0 |
--    '       | 0 |         | 0 1 0 |
--    '       | 0 |         | 0 0 1 |
--    '
--    '       | 0 |         | 1 0 0 |
--    ' e_n = | 0 |   n_n = | 0 1 0 |
--    '       | 0 |         | 0 0 1 |
--    '       | 1 |         | 0 0 0 |
--
IS
    l_n_string LONG := RPAD('1',n+1,'0');
BEGIN

    For i in 1..n Loop
        p_e_1(i)(1)   := TO_NUMBER(SUBSTR(l_n_string, 1, 1));
        p_e_n(i)(1)   := TO_NUMBER(SUBSTR(l_n_string, n, 1));
        For j in 1..n-1 Loop
            p_n_1(i)(j) := TO_NUMBER(SUBSTR(l_n_string, j+1, 1));
            p_n_n(i)(j) := TO_NUMBER(SUBSTR(l_n_string, j,   1));
        End Loop;
        l_n_string := LPAD(SUBSTR(l_n_string, 1, n), n+1, '0');
    End Loop;

END;

BEGIN
    l_array_size := &inp_array;

    gen_arrays(l_array_size, e_1, e_n, n_1, n_n);

    --==========================================================================
    -- DISPLAY RESULTS
    --==========================================================================
     DBMS_OUTPUT.PUT_LINE('e_1 = ');
     For i in 1..l_array_size Loop
        DBMS_OUTPUT.PUT_LINE('         | ' || e_1(i)(1) || ' |');
     End Loop;
     DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('e_n = ');
     For i in 1..l_array_size Loop
        DBMS_OUTPUT.PUT_LINE('         | ' || e_n(i)(1) || ' |');
     End Loop;
     DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('n_1 = ');
     For i in 1..l_array_size Loop
        DBMS_OUTPUT.PUT('         | ');
        For j in 1..l_array_size-1 Loop
            DBMS_OUTPUT.PUT(n_1(i)(j) || ' ');
        End Loop;
        DBMS_OUTPUT.PUT('|');
        DBMS_OUTPUT.NEW_LINE;
     End Loop;
     DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
     DBMS_OUTPUT.PUT_LINE('n_n = ');
     For i in 1..l_array_size Loop
        DBMS_OUTPUT.PUT('         | ');
        For j in 1..l_array_size-1 Loop
            DBMS_OUTPUT.PUT(n_n(i)(j) || ' ');
        End Loop;
        DBMS_OUTPUT.PUT('|');
        DBMS_OUTPUT.NEW_LINE;
     End Loop;
     DBMS_OUTPUT.PUT_LINE('--------------------------------------------------');
    --==========================================================================
    --
    --==========================================================================

END;
/

Finally, though, I have to give a little space to Airdrik. While the code may contain some errors, it is in Visual Basic, as was the original solution, and it knows that recursion makes everything better.

Public Sub GenerateIdentitySquare(ByVal n As Long, ByRef sq As Variant, ByVal i As Long, ByVal j As Long)
        Select Case j
        Case i:
                sq(i, j) = #1
                If i < n Then
                        GenerateIdentitySquare(n, sq, i, j+1)
                End If
        Case n:
                sq(i, j) = #0
                GenerateIdentitySquare(n, sq, i+1, 1)
        Case Else:
                sq(i, j) = #0
                GenerateIdentitySquare(n, sq, i, j+1)
        End Select
End Sub

Public Sub CopyRowValues(ByVal n As Long, ByRef sq As Variant, ByRef e As Variant, ByVal sq_i As Long, ByVal e_i As Long, ByVal j As Long)
        e(e_i, j) = sq(sq_i, j)
        if j < n Then
                CopyRowValues(n, sq, e, sq_i, e_i, j+1)
        End If
End Sub

Public Sub CopyRows(ByVal n As Long, ByRef sq As Variant, ByRef e_1 As Variant, ByRef e_n As Variant, ByRef n_1 As Variant, ByRef n_n As Variant, ByVal i As Long)
        Select Case i
        Case 1:
                CopyRowValues(n, sq, e_1, i, 1, 1)
                CopyRowValues(n, sq, n_n, i, i, 1)
                CopyRows(n, sq, e_1, e_n, n_1, n_n, i+1)
        Case n:
                CopyRowValues(n, sq, n_1, i, i-1, 1)
                CopyRowValues(n, sq, n_n, i, i, 1)
        Case Else:
                CopyRowValues(n, sq, n_1, i, i-1, 1)
                CopyRowValues(n, sq, e_n, i, 1, 1)
                CopyRows(n, sq, e_1, e_n, n_1, n_n, i+1)
        End Select
End Sub

Public Sub DefineProjectionArrays(ByVal n As Long, ByRef e_1 As Variant, ByRef e_n As Variant, ByRef n_1 As Variant, ByRef n_n As Variant)
    Dim i As Long, j As Long

    ' Generate 4 Arrays of the form (example n=4)
    '       | 1 |         | 0 0 0 |
    ' e_1 = | 0 |   n_1 = | 1 0 0 |
    '       | 0 |         | 0 1 0 |
    '       | 0 |         | 0 0 1 |
    '
    '       | 0 |         | 1 0 0 |
    ' e_n = | 0 |   n_n = | 0 1 0 |
    '       | 0 |         | 0 0 1 |
    '       | 1 |         | 0 0 0 |

        Dim sq(n, n) As Variant
        GenerateIdentitySquare(n, sq, 1, 1)

    ReDim e_1(n, 1)
    ReDim e_n(n, 1)
    ReDim n_1(n, n - 1)
    ReDim n_n(n, n - 1)

        CopyRows(n, sq, e_1, e_n, n_1, n_n, 1)
End Sub

Functional programming is always the best approach, obviously.

[Advertisement] Release! is a light card game about software and the people who make it. Play with 2-5 people, or up to 10 with two copies - only $9.95 shipped!