Пределы магических квадратов

 

   Пусть  M - магический квадрат, составленный из последовательных натуральных чисел, начиная с 1.

   В этом примере рассматриваем магические квадраты нечетного размера, потому что используем простой способ построения такого квадрата.

   Для пятерки способ построения показан ниже. Составляется таблица по-порядку по диагоналям, затем наружные числа сдвигаются на свободные места.

 

        1        
      6   2      
    11   7   3    
  16   12   8   4  
21   17   13   9   5
  22   18   14   10  
    23   19   15    
      24   20      
        25        

 

11 24 7 20 3
4 12 25 8 16
17 5 13 21 9
10 18 1 14 22
23 6 19 2 15

 

 

  Пусть s - сумма чисел по строке, по столбцу или по диагонали.

   Обозначим через  A  матрицу  M / s

Например, для квадрата 3 на 3 получается матрица

4/15 9/15 2/15
3/15 5/15 7/17
8/15 1/15 6/15

 

   Теперь рассматриваем  предел при  n  стремящемся к бесконечности матрицы 

A^n

Замечание 1. Если делить не на s, то в пределе получается либо 0, либо бесконечность. Доказательство - не очень простое упражнение по курсу алгебра для первого курса.

Замечание 2. В ответе получается матрица из одинаковых элементов, равных 1/dim (1/размер квадрата).

 

Предлагается программа на Java,

у которой параметром является размер квадрата dim,

и которая сначала строит магический квадрат согласно приведенной для пятерки схеме,

а затем вычисляется указанный предел путем многократного умножения матрицы A на себя.

Предел для двойной точности получается уже при 20 степени, но у меня сделан большой цикл с целью померить время исполнения.

   При суперкомпиляции этой программы происходит следующее

 - процесс построения магического квадрата полностью специализируется, то есть матрица получается как заданная, причем не массивом, а набором переменных.

 - каждый элемент матрицы получается одним оператором Явы, а не несколькими, как было раньше.

 - матрицы промежуточных вычислений почему-то превращаются в набор одномерных массивов. Если бы в набор чисел, то ускорение исполнения было бы еще больше.

 - скорость исполнения повышается примерно в два раза.

В поддиректориях Result3, Result5, Result7 приведены результаты суперкомпиляции для указанных размеров квадратов.

   Копию данного текста помещаю в приложение.

   Если пример представляет интерес для других, то разрешаю разослать его более широкой аудитории без специального разрешения.

   Привожу текст программы.

class MagicSquare {

    public static final int dim=3;  /* Dimension of matrix */
    public static int iter;

    public static double[][] a = new double[dim][dim];


    public static void main (String args[])
        {
        long start = System.currentTimeMillis();

        iter = 100000;

        test();

        long end = System.currentTimeMillis();
        System.out.println("Total time = "+ (end-start)*0.001);

        for (int i = 0; i < dim; i++)
            {
            for (int j = 0; j < dim; j++)
            System.out.print(a[i][j] + " " );
            System.out.println(" ");
            };
        System.out.println(" " );
        }


    public static void test()
        {
        double[][] magic = new double[dim][dim];
        double[][] c  = new double[dim][dim];
        double[][] c1 = new double[dim][dim];
        
        int x = (dim - 1)/2 + (dim + 1);
        int y = -(dim - 1)/2 + (dim - 1);
        int n = 1;
        
        for (int i = 0; i < dim; i++)
            {
            x = x - (dim + 1);
            y = y - (dim - 1);
            for (int j = 0; j < dim; j++)
                {
                if (x >= dim) magic[x-dim][y] = n; else
                if (x <  0  ) magic[x+dim][y] = n; else
                if (y >= dim) magic[x][y-dim] = n; else
                if (y <  0  ) magic[x][y+dim] = n; else
                magic[x][y] = n;
                n++;
                x++;
                y++;
                }
            }
        
        int summa = dim * (dim*dim + 1) /2;
        for (int i = 0; i < dim; i++)
            for (int j = 0; j < dim; j++)
                magic[i][j] = magic[i][j] / summa;


        for (int i = 0; i < dim; i++)
            for (int j = 0; j < dim; j++)
                c[i][j] = 0.0;

        for (int i = 0; i < dim; i++)
                c[i][i] = 1.0;


        for (int iiii = 0; iiii < iter; iiii++)
            {
            for (int i = 0; i < dim; i++) 
                for (int j = 0; j < dim; j++)
                    {
                    c1[i][j] = 0.0;
                    for (int k = 0; k < dim; k++)
                        c1[i][j] = c1[i][j] + c[i][k]*magic[k][j];
                    };

            for (int i = 0; i < dim; i++)
                for (int j = 0; j < dim; j++)
                    c[i][j] = c1[i][j];
            };

        for (int i = 0; i < dim; i++)
            for (int j = 0; j < dim; j++)
                a[i][j] = c[i][j]; 


        }
}

Результат суперкомпиляции для тройки

public static void test () {
      final double[][] daa9 = new double[3][3] ;
      final double d96 = 4D / 15D;
      final double d100 = 3D / 15D;
      final double d104 = 8D / 15D;
      final double d113 = 9D / 15D;
      final double d117 = 5D / 15D;
      final double d121 = 1D / 15D;
      final double d130 = 2D / 15D;
      final double d134 = 7D / 15D;
      final double d138 = 6D / 15D;
      final double[] da6 = daa9[0];
      da6[0] = 0D;
      da6[1] = 0D;
      da6[2] = 0D;
      final double[] da7 = daa9[1];
      da7[0] = 0D;
      da7[1] = 0D;
      da7[2] = 0D;
      final double[] da8 = daa9[2];
      da8[0] = 0D;
      da8[1] = 0D;
      da8[2] = 0D;
      da6[0] = 1D;
      da7[1] = 1D;
      da8[2] = 1D;
      for (int iiii_397 = 0; iiii_397 < MagicSquare.iter; iiii_397++) {
        final double d441 = da6[0] * d100 + da6[1] * d117 + da6[2] * d134;
        final double d463 = da6[0] * d104 + da6[1] * d121 + da6[2] * d138;
        final double d490 = da7[0] * d96 + da7[1] * d113 + da7[2] * d130;
        final double d512 = da7[0] * d100 + da7[1] * d117 + da7[2] * d134;
        final double d534 = da7[0] * d104 + da7[1] * d121 + da7[2] * d138;
        final double d561 = da8[0] * d96 + da8[1] * d113 + da8[2] * d130;
        final double d583 = da8[0] * d100 + da8[1] * d117 + da8[2] * d134;
        final double d605 = da8[0] * d104 + da8[1] * d121 + da8[2] * d138;
        da6[0] = da6[0] * d96 + da6[1] * d113 + da6[2] * d130;
        da6[1] = d441;
        da6[2] = d463;
        da7[0] = d490;
        da7[1] = d512;
        da7[2] = d534;
        da8[0] = d561;
        da8[1] = d583;
        da8[2] = d605;
        continue;}
      MagicSquare.a[0][0] = da6[0];
      MagicSquare.a[0][1] = da6[1];
      MagicSquare.a[0][2] = da6[2];
      MagicSquare.a[1][0] = da7[0];
      MagicSquare.a[1][1] = da7[1];
      MagicSquare.a[1][2] = da7[2];
      MagicSquare.a[2][0] = da8[0];
      MagicSquare.a[2][1] = da8[1];
      MagicSquare.a[2][2] = da8[2];
      return;
    }
//-----------------------------------------------------------