ダイヤ表示をするための考察

前回Ohotech 特盛#4 にて、島田さんによるプログラム公開大会が行われた。
その中で最終問題でダイアモンドを表示させるってことで、
いくつか解答を考えてみた。


まずはじめに。
この後出てくる言語はC言語が多数、Pythonが少々だよ。
そして、出力結果はどれも以下の様になります。

  *
 ***
*****
 ***
  *
   *
  ***
 *****
*******
 *****
  ***
   *


はじめに、考えつくのは「上向きピラミッド+下向きピラミッド」を生成する考え方。
多分プログラミング覚え始めの頃はこの方法が一番思いつく方法だと思われる。

void diamond1(int n) {
  int i, j;
  for (i = 0; i <= n / 2; ++i) {
    for (j = n / 2 - i; j > 0; --j) {
      printf(" ");
    }

    for (j = 0; j < i * 2 + 1; ++j) {
      printf("*");
    }

    puts("");
  }

  for (i = n / 2 - 1; i >= 0; --i) {
    for (j = n / 2 - i; j > 0; --j) {
      printf(" ");
    }

    for (j = 0; j < i * 2 + 1; ++j) {
      printf("*");
    }

    puts("");
  }
}


最初のiのforで上向きピラミッドを生成し、
後のiのforで下向きピラミッドを生成してる。
しかし、慣れてくると分けるのがとても冗長に思えてくる。
そこで次のプランは「絶対値による上向き、下向きピラミッドの結合」である。


空白の数、アスタリスクの数の増減は0 <= i < n の増減の範囲で以下の用になる。

空白の数 = |n / 2 - i|
アスタリスクの数 = n - 2 * |n / 2 - i|


iの範囲を-n / 2ずらし、-n / 2 <= i <= -n / 2にすると以下の様になる

空白の数 = |i|
アスタリスクの数 = n - 2 * |i|


これらを含めたら以下のように実装できます。

void diamond2(int n) {
  int i, j;

  for (i = -n / 2; i <= n / 2; ++i) {
    int v = i < 0 ? -1 : 1;
    for (j = 0; j < i * v; ++j) {
      printf(" ");
    }

    for (j = 0; j  < n - 2 * i * v; ++j) {
      printf("*");
    }

    puts("");
  }
}


次に1行ごとに文字列処理を行った場合、
C言語だと多少長くなりますが、以下のようになります。

void diamond3(int n) {
  int i, j;
  char *a = NULL;
  char *s = NULL;
  char *d = NULL;
  a = calloc(n + 1, sizeof(char));
  if (!a) { return; }
  s = calloc(n + 1, sizeof(char));
  if (!s) { goto a_free; }
  d = calloc(n + 1, sizeof(char));
  if (!d) { goto s_free; }

  for (i = 0; i < n; ++i) {
    strncat(s, " ", 1);
    strncat(a, "*", 1);
  }

  for (i = -n / 2; i <= n / 2; ++i) {
    int v = i < 0 ? -1 : 1;
    memset(d, 0, n + 1);
    strncat(d, s, i * v);
    strncat(d, a, n - 2 * i * v);
    puts(d);
  }

  if (d) { free(d); d = NULL; }
s_free:
  if (s) { free(s); s = NULL; }
a_free:
  if (a) { free(a); a = NULL; }
}


文字列で扱う事により、更にfor文を減らす事ができました。
(但し、行数は増えました)


高級言語が使えた場合、リストにあらかじめ出力内容をまとめておき、
最後に改行コードでつなげて表示するとそこそこスマートに表示出来ます。

def diamond4(n):
    l = []
    l.append('*' * n)
    for i in range(n - 2, 0, -2):
        s = ' ' * ((n - i) / 2) + '*' * i
        l.insert(0, s)
        l.append(s)
    print '\n'.join(l)


以下、実行コード
C言語: http://ideone.com/fxaMNL
Python: http://ideone.com/YVB5bM