2012年01月13日

コンソールに三角形を2つ表示する

 大学などのプログラミング課題で、文字を使った図形を表示させる問題はよく出題されます。今回の内容もそのひとつで、アスタリスク(*)を使って三角形を2つ表示させます。完成すると、次の図形を出力します。
PS U:\Projects\Cpp\Cui> .\Test.exe
*
**
***
****
*****
*
**
***
****
*****
PS U:\Projects\Cpp\Cui>
人気ブログランキング

 このような図形表示の問題では「各行に何文字出力するか」を考えると問題が複雑になることが多くあります。出力する文字数自体はどの行も固定で、何も図形を表示したくない場所には半角空白を出力する方が簡単です。この場合、「それぞれの場所に出力すべき文字は空白か*か」を考えれば済むことになります。

 今回の課題では出力する行数は10行、桁数は5文字です。

 *と空白のどちらを出力するか判定するところは複雑なので、とりあえず三角形1個分だけを考えてプログラムを書いておき、一度動かしてから調整します。図形が複雑な場合、とりあえず同じような形の図形を出力するプログラムを作っておき、少しずつ修正していくことも大事です。

 それぞれ、y行x桁(y=0〜4, x=0〜4)に*と空白のどちらを出力するべきか考えるわけですが、下のように表にしてみました。*を出力する条件は、いずれもx<=yであることが分かります。
y*の出力条件
0行目(y=0)x<=0のとき
1行目(y=1)x<=1のとき
2行目(y=2)x<=2のとき
3行目(y=3)x<=3のとき
4行目(y=4)x<=4のとき

 まとめると次のようになります。
  • 10行出力する。
  • 各行に5文字ずつ出力する。
  • 出力する文字は、x<=yであれば*、それ以外は空白。

 ここまでの段階で、次のプログラムができます。
#include <stdio.h>

int main(void)
{
  int x,y;
  for(y=0; y<10; y++)
  {
    for(x=0; x<5; x++)
    {
      if(x<=y) printf("*");
      else printf(" ");
    }
    printf("\n");
  }
  return 0;
}

 これを実行した結果は次の通りです。
PS U:\Projects\Cpp\Cui> .\Test.exe
*
**
***
****
*****
*****
*****
*****
*****
*****
PS U:\Projects\Cpp\Cui>

 一見すると正しく出力されていないように見えますが、最初の三角形(先頭5行分)は問題なく出力されています。*を出力する条件はx<=yなので、この条件式のみで考えれば10×10サイズの大きな三角形になりますが、各行5文字しか出力しないので右半分が切り取られたような形になっています。

 さて、先ほどの表と同様にy=5〜9における*の出力条件を見てみます。
y*の出力条件
5行目(y=5)x<=0のとき
6行目(y=6)x<=1のとき
7行目(y=7)x<=2のとき
8行目(y=8)x<=3のとき
9行目(y=9)x<=4のとき

 これを見ると、y>=5ではx<=y-5のときに*を出力すれば良いことが分かります。ただし、ここでいきなり次のように書くのはあまり賢いやり方ではありません。
if(y<5 && x<=y || y>=5 && x<=y-5) printf("*");
else printf(" ");

 このように書くと、「次は三角形を3個表示しよう」とか「10個表示しよう」などとプログラムを拡張したくなったときに融通が利きません。それどころか、だんだん条件文が長くなってしまいます。

 ここは、もう少し考えて法則を見つけるのが賢明です。三角形3個目は次のような条件で出力します。
y*の出力条件
10行目(y=10)x<=0のとき
11行目(y=11)x<=1のとき
12行目(y=12)x<=2のとき
13行目(y=13)x<=3のとき
14行目(y=14)x<=4のとき

 表の右側の列を見てほしいのですが、1個目の三角形も、2個目の三角形も、3個目の三角形も、右側の列が変わっていません。この時点で気付いてほしいのですが、yが5の倍数のときはx<=0が*の出力条件になっています。同様に、yが5の倍数+1のときはx<=1、yが5の倍数+2のときはx<=2……です。
 要するに、

x<=(yを5で割った余り)

が*の出力条件なのです。余りを計算するのは%演算子ですから、*を出力する条件は

x<=y%5

です。これに従ってプログラムを書き換えてみましょう。さきほどのプログラムに「%5」の2文字を追加しただけです。

#include <stdio.h>

int main(void)
{
  int x,y;
  for(y=0; y<10; y++)
  {
    for(x=0; x<5; x++)
    {
      if(x<=y%5) printf("*");
      else printf(" ");
    }
    printf("\n");
  }
  return 0;
}


 実行結果は次の通りになります。
PS U:\Projects\Cpp\Cui> .\Test.exe
*
**
***
****
*****
*
**
***
****
*****
PS U:\Projects\Cpp\Cui>

 最初に作ろうとしていた図形が出力されました。

 今回のように、図形の問題で、しかも何かしらの繰り返しのパターンがある場合は % を使って割り算の余りを求めると、うまく解決できる場合が多いので覚えておくと良いかもしれません。
web拍手 by FC2
posted by 北条利彦 at 12:23 | Comment(0) | TrackBack(0) | C言語 | このブログの読者になる | 更新情報をチェックする

2011年06月08日

[C言語] ランダムな順列を出力する

 0〜5の6通りの値から重複のないように、かつランダムに数字を3つ選び、選んだ順番に表示します。

 これを実装するときに難しいのは、どうやって重複を避けるかということです。単純に0〜5の6通りの値を3回乱数で選んでしまうと、もちろん一定確率で重複する数字が出てくるので目的の機能を実現できません。過去に出現した値を記憶しておいて、乱数で選んだ数字が重複していたら選び直す……という方法も可能ですが、重複したときに無駄な感じがします。

 これから紹介する方法は、「表示できる候補」を配列の中に格納しておき、数字を1つ表示するたびにその数字を配列から削除するというものです。この方法の概要を次の図に示します。
Permutations.png

 図に出てきた変数を解説していきます。まず、iはループ変数で、1件出力するたびに1増えていきます。ここでは3桁出力するので0〜2の値をとることになります。patternsは、初期状態における表示可能な候補数です。ここでは0〜5の6通りですから、常にpatterns==6となります。stockは出力できる値の候補を格納しておく配列です。1度出力した値はもう出力できないのでstockから削除していきます。stockに格納されている値のうち、先頭からpatterns-i個のみが出力可能です。最後に、indexは、stockの何番目の値を出力するかを表す変数で、この値は乱数で決まります。

 ソースコードは次の通りです。実行すると、3桁の順列が出力されます。
人気ブログランキング

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void)
{
  int i,j;
  int patterns=6; /* 6通りの数字を表示 */
  int stock[]={1,2,3,4,5,6}; /* この中の数を表示 */
  
  srand(time(NULL)); /* ランダムにする。 */
  for(i=0; i<3; i++)
  {
    /* stockの何番目か決める */
    int index=rand()%(patterns-i);
    /* index番目を出力 */
    printf("%d",stock[index]);
    /* 出力した値はstockから削除 */
    for(j=index; j<patterns-1; j++)
      stock[j]=stock[j+1];
  }
  return 0;
}
web拍手 by FC2
posted by 北条利彦 at 23:52 | Comment(1) | TrackBack(0) | C言語 | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。