C++ の魔方陣問題

Adnan Ashraf 2024年2月15日
  1. 魔方陣問題
  2. C++ での魔方陣の実装
C++ の魔方陣問題

この記事では、魔方陣問題とその性質、および C++ を使用したその実装について説明します。 魔方陣を実装するために魔方陣問題を学びましょう。

魔方陣問題

魔方陣は、正の整数の 2D 配列で、各行、列、および対角線の合計が同じです。 合計は、Magic Sum と呼ばれることが多く、行、列、および対角の各定数の合計を指します。

魔方陣の各位置の整数値は一意でなければなりません。 N が魔方陣のサイズである場合、1 から N2 までの整数を含めることができます。

次の式は、魔法の合計を計算します。

$$ Magic\;Sum=N\left(N^{2}+1\right) / 2 $$

魔方陣のサイズは、行と列の数を示します。 N が 3 の場合、魔方陣は 3 行 3 列になります。

では、例を挙げて魔力和について説明しましょう。 N の値を 3 とすると、上記の式から次のように計算できます。

$$ Magic\;Sum=3\left(3^{2}+1\right) / 2 = 15 $$

ここで、15 は魔法の合計です。 これは、各列、行、対角線の合計が 15 になることを意味します。

サイズが 3 の魔方陣を下の図に示します。

魔法の合計 3

各行、列、および対角線の合計が魔法の合計に等しく、すべてのエントリが一意であることがわかります。

魔方陣問題を理解したら、実装してみましょう。

実装する魔方陣のサイズを n とします。 次に、最初の値 (つまり 1) が魔方陣の (n/2,n-1) の位置に格納されます。

この位置を (i,j) とすると、次の値は位置 (i-1,j+1) になります。 ここでは、すべての行と列にラップアラウンドがあると仮定します (つまり、循環型としても知られています)。

魔方陣問題の条件

  1. 次の値の位置は、現在の列番号に 1 を加算し、現在の行番号から 1 を引いて決定されます。
    • 列の新しい位置が n になると、列と行が循環するため 0 にラップされます。
    • 同様に、カラスの新しい位置が -1 になる場合、n-1 にラップされます。
  2. 新しく計算された場所にすでに値が含まれているとします。 その場合、現在の行番号は 1 を追加することで更新され、現在の列番号はそこから 2 を減算することで更新されます。
  3. 新しく計算された列番号が n で行番号が -1 の場合、更新された位置は (0,n-2) になります。

C++ での魔方陣の実装

詳細に入る前に、以下のコードを見てみましょう。

#include <bits/stdc++.h>
using namespace std;
// This function will generate an old-size magic square only.
void magicSquareGenerator(int s) {
  int magic_Square[s][s];
  // This will initialize all locations of the magic square to 0
  memset(magic_Square, 0, sizeof(magic_Square));
  // here, r is the row index, and c is the column index for the first number
  int r = s / 2;
  int c = s - 1;
  //  generating magic square
  for (int num = 1; num <= s * s;) {
    if (r == -1 && c == s)  // Third condition
    {
      c = s - 2;
      r = 0;
    } else {
      if (c == s) c = 0;
      if (r < 0) r = s - 1;
    }
    if (magic_Square[r][c])  // second condition
    {
      c -= 2;
      r++;
      continue;
    } else
      magic_Square[r][c] = num++;
    c++;
    r--;  // 1st condition
  }

  // Print magic square
  cout << "The Magic Square has:" << s << " rows and " << s << " columns.";
  cout << "\nThe Magic Sum is: " << s * (s * s + 1) / 2 << ":\n";
  for (r = 0; r < s; r++) {
    for (c = 0; c < s; c++)
      // displaying the magic square.
      cout << setw(4) << magic_Square[r][c] << " ";
    cout << endl;
  }
}

int main() {  // Code expects only odd sizes
  int s;
  cout << "Enter the size of the Magic Square: ";
  cin >> s;
  while (s % 2 == 0) {
    cout << "Plz Enter an odd number :" << endl;
    cout << "Enter the size of the Magic Square: ";
    cin >> s;
  }
  magicSquareGenerator(s);
  return 0;
}

上記の魔方陣プログラムを順を追って説明します。

  1. 値 1 の位置は = (3/2,3-1) = (1,2) = [1][2] です。
  2. 値 2 の位置は = (1-1,2+1) = (1,2) = [1][2] です。
  3. 値 3 の位置は = (0-1,0+1) = (3-1,1+1) = [2][1] です。
  4. 値 4 の位置は = (2-1,1+1) = (1,2) = [1][2] です。
    • [1][2] はすでに値 1 を持っているため、条件 2 により、新しい場所は (1+1,2-2) = [2][0] になります。
  5. 値 5 の位置は = (2-1,0+1) = (1,1) = [1][1] です。
  6. 値 6 の位置は = (1-1,1+1) = (1,2) = [0][2] です。
  7. 値 7 の位置 = (0-1,2+1) = (-1,3)
    • 条件 3 が成立するため、新しい位置は (0,3-2)=[0][1] になります。
  8. 値 8 の位置は = (0-1,1+1) = (-1,2) = [2][2] で、条件 1 行がラップされます。
  9. 値 9 の位置は = (2-1,2+1) = (1,3) = [1][0] で、条件 1 の列はラップされます。

これは、魔方陣 3 のサイズを入力したときの出力です。

Enter the size of the Magic Square: 3
The Magic Square has:3 rows and 3 columns.
The Magic Sum is: 15:
   2    7    6
   9    5    1
   4    3    8

これは、魔方陣 7 のサイズを入力したときの出力です。

Enter the size of the Magic Square: 7
The Magic Square has:7 rows and 7 columns.
The Magic Sum is: 175:
  20   12    4   45   37   29   28
  11    3   44   36   35   27   19
   2   43   42   34   26   18   10
  49   41   33   25   17    9    1
  40   32   24   16    8    7   48
  31   23   15   14    6   47   39
  22   21   13    5   46   38   30

注: 上記のプログラムは、奇数サイズの魔方陣を入力した場合にのみ機能します。

提案されたアルゴリズムの時間と空間の複雑さは $O(n^2)$ です。

関連記事 - C++ Math