東京工芸大学 工学部  電子機械学科 2年 前期 

基礎プログラミング 第4回
 2017.5.2 
藤木 文彦

インデックスへ戻る

fujiki.tv/t-kougei/kisoprog
ffujiki@em.t-kougei.ac.jp

                        
配列

 同じ種類のデータを、多数扱いたいときに、そのデータを入れる変数として、配列変数、というものを使用できます。
 番号 0〜9番までの人の成績が、それぞれ何点か、というデータを処理したい場合などに使用します。

 1.配列の宣言と使用法


 配列は、使用するデータの型と、個数を宣言して使用します。
 配列名が a 、型が整数型、使用する個数が 10個の場合は、
int a[10]
 と宣言します。

 ただし、実際に使用できるのは、 1〜10 ではなく、 0〜9 までですので、注意してください。
 ※範囲を超えた場合(たとえば、添字に10を使用した場合などでも、エラーとはなりませんが、動作がおかしくなりますので注意が必要です。

 コンピュータでは、数字は、1から始まるのではなく、0から始まると考えます。

  (例題1) 

Project0401



int _tmain(int argc, _TCHAR* argv[])
{
       int m[10];
       for( int i=0; i<10; i++){
        m[i]=i*10;
       }
       for( int i=0; i<10; i++){
        printf("m[%d]=%d\n",i,m[i]);
       }
         getchar();
        return 0;
}

出力結果

m[0]=0
m[1]=10
m[2]=20
m[3]=30
m[4]=40
m[5]=50
m[6]=60
m[7]=70
m[8]=80
m[9]=90 

 注意:プログラムを上の表示から、そのままコピーペースとして実行すると、「日本語空白(全角空白)」が入るので、実行前にエラーと成ります。こんな感じのエラーです−>( '0x3000': この文字を識別子で使用することはできません)
 日本語空白を、英語空白(半角空白)に置き換える必要があります。

  【演習問題1】 
 Project0401 を修正し、次のような出力となるようにしなさい。
(プログラムの書き方は何通りも考えられる。)

出力結果

m[0]=100
m[1]=90
m[2]=80
m[3]=70
m[4]=60
m[5]=50
m[6]=40
m[7]=30
m[8]=20
m[9]=10 

できたらメールに、プログラムの主要部分と、出力結果のコピーを貼り付け、あるいは添付して送りなさい。

今後は、メールのタイトルなど、特別な場合を除き、逐一指示しないので、各自で指定するように。


 2.配列の計算


 配列にデータの初期値を入れ、それを計算するプログラムの実例です。

  (例題2) 

Project0402


int _tmain(int argc, _TCHAR* argv[])
      {
       int m[10] = {5,6,7,8,9,10,1,2,3,4};
      
       for( int i=0; i<10; i++){
        printf("m[%d]=%d\n",i,m[i]);
       }
      
       for( int i=0; i<10; i++){
        m[i]*=111;
       }
       printf("-------------------\n");
       for( int i=0; i<10; i++){
        printf("m[%d]=%d\n",i,m[i]);
       }
      
        getchar();
        return 0;
      }
      
      

m[0]=5
m[1]=6
m[2]=7
m[3]=8
m[4]=9
m[5]=10
m[6]=1
m[7]=2
m[8]=3
m[9]=4
---------
m[0]=555
m[1]=666
m[2]=777
m[3]=888
m[4]=999
m[5]=1110
m[6]=111
m[7]=222
m[8]=333
m[9]=444

  【演習問題2】 
 Project0402 に次のように 変更を加えなさい。
もう一つ配列、 n[10] を宣言し、初期値を入れ、次のように表示されるようにしなさい。

m[0]=5 n[0]=3
m[1]=6 n[1]=5
m[2]=7 n[2]=3
m[3]=8 n[3]=7
m[4]=9 n[4]=9
m[5]=10 n[5]=6
m[6]=1 n[6]=4
m[7]=2 n[7]=2
m[8]=3 n[8]=8
m[9]=4 n[9]=7
---------
m[0] * n[0] = 5 * 3 = 15
m[1] * n[1] = 6 * 5 = 30
m[2] * n[2] = 7 * 3 = 21
m[3] * n[3] = 8 * 7 = 56
m[4] * n[4] = 9 * 9 = 81
m[5] * n[5] = 10 * 6 = 60
m[6] * n[6] = 1 * 4 = 4
m[7] * n[7] = 2 * 2 = 4
m[8] * n[8] = 3 * 8 = 24
m[9] * n[9] = 4 * 7 = 28




 3.直接データを入力する

 配列に、その都度キーボードから入力するプログラムを作ります。
 キーボードからの入力は、

a[i] << cin;

のような方法を用います。
出力も簡略化して、 cout

を用います。

  (例題3) 

Project0403

※下のプログラムには一カ所間違いがあります。
ある変数の初期化がしていないことです。
一度そのまま入力してコンパイルしてみるとエラーが出るはずですので、まず、間違いを修正する前に下のプログラムを入力、コンパイルし(実行までいかないはず)、その後、間違いを修正して実行してみなさい。

#include "stdafx.h"
#include <iostream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	int a[5],total;
	float heikin;
	for (int i = 0; i < 5; i++){
		cout << i + 1; cout << "番目のデータを入力してください ";
		cin >> a[i];
		total += a[i];
		heikin = (float)total / (i + 1);
		printf("入力は %d  ここまでの合計 %d  ここまでの平均 %f \n", a[i], total, heikin);
	};

	getchar();
	cout << "何かキーを押すと終了します。";
	getchar();
	return 0;
}



  【演習問題3】 

 Project0403 に変更を加え、次のように、5個のデータを入力後、それぞれのデータの平均との差を出力するようにします。


1番目のデータを入力してください 10
入力は 10 ここまでの合計 10 ここまでの平均 10.000000
2番目のデータを入力してください 11
入力は 11 ここまでの合計 21 ここまでの平均 10.500000
3番目のデータを入力してください 13
入力は 13 ここまでの合計 34 ここまでの平均 11.333333
4番目のデータを入力してください 15
入力は 15 ここまでの合計 49 ここまでの平均 12.250000
5番目のデータを入力してください 17
入力は 17 ここまでの合計 66 ここまでの平均 13.200000
0 番目のデータ 10 平均との差 -3.2
1 番目のデータ 11 平均との差 -2.2
2 番目のデータ 13 平均との差 -0.2
3 番目のデータ 15 平均との差 1.8
4 番目のデータ 17 平均との差 3.8
何かキーを押すと終了します。


追加するプログラムのヒント。
出力部分は次のような感じになります。 ● の部分には自分で適切な命令などを補いなさい。

cout << i; cout << " 番目のデータ ";
cout << a[i]; cout << " 平均との差 "; cout << (float)●;
cout << "\n";


 4.1次元配列に、2次方程式の値を入れる

 2次方程式の決められた部分での値を求めて表示するような場合は、次のようにします。

  (例題4) 

 2次関数
y=-x^2 + 8x -6
 は、次のように平方完成した関数となります。
 頂点が、 x=4,y=10 にあります。

y= -(x-4)^2 + 10

これを、 x=0 〜 9
までの範囲で数値的に計算して結果を表示します。

Project0404


#include "stdafx.h"
#define FUNC01 -(x-4)*(x-4) + 10
	int _tmain(int argc, _TCHAR* argv[])
	{
		int i, x;
		int m[10];
		for (x = 0; x<10; x++){
			m[x] = FUNC01;
		}
		for (i = 0; i<10; i++){
			printf("m[%d]=%d\n", i, m[i]);
		}
		getchar();
		return 0;
	}

m[0]=-6
m[1]=1
m[2]=6
m[3]=9
m[4]=10
m[5]=9
m[6]=6
m[7]=1
m[8]=-6
m[9]=-15

 べき乗(累乗)の演算子は^でも**でもありません。pow (x, y)という関数を用います。簡単な場合は、上記の様に同じものを掛けます。
間違えないように気をつけましょう。特に^の場合、コンパイルエラーとはなりませんので注意が必要です。

  【演習問題4】 
 Project0404 に 図示する部分を追加して、次のような出力が出るようにしてください。

m[0]=-6
m[1]=1
m[2]=6
m[3]=9
m[4]=10
m[5]=9
m[6]=6
m[7]=1
m[8]=-6
m[9]=-15



 プログラムの後半に次のようなものを追加します。
なお、一部分伏せ字 ● にしてありますので、●の部分は自分で考えて補ってください。

		for (i = 0; ●; i++){
			printf("%3d: ", m[i]);
			for (int j = -20; j< m[i]; j++){
				if (●== 0){
					printf("|");
				}
				●{
					printf(" ");
				}
			}
			printf("*\n");
		}


 5.1次元配列で、最大値を求める


 まず、前の問題と同じ問題で、配列の中での最大値を表示するプログラムを作ります。
 最大値は、0以上になっていることがわかっていますから、とりあえずの最大値を、0として始めます。

  (例題5) 

Project0405


#include "stdafx.h"
#define FUNC01 -(x-4)*(x-4) + 10

int _tmain(int argc, _TCHAR* argv[])
{
	int max;
	int m[10];
	for (int x = 0; x<10; x++){
		m[x] = FUNC01;
		printf("m[%d]=%d\n", x, m[x]);
	}

	for (int i = 0; i<10; i++){
		printf("%3d: ", m[i]);
		for (int j = -20; j< m[i]; j++){
			if (j == 0){
				printf("|");
			}
			else{
				printf(" ");
			}
		}
		printf("*\n");
	}

//ここまでは前のプログラムと同じです。(いくつかの変数宣言が追加されている)
//ここから、配列の中の最大値を max に入れる部分です。

	max = 0;
	for (int i = 0; i<10; i++){
		if ( ● > ●){
			max = m[i];
		}
	}
	printf("max=%d\n", max);

	getchar();
	return 0;
}


実行結果はこんな感じ。
m[0]=-6
m[1]=1
m[2]=6
m[3]=9
m[4]=10
m[5]=9
m[6]=6
m[7]=1
m[8]=-6
m[9]=-15
 -6:               *
  1:                     |*
  6:                     |     *
  9:                     |        *
 10:                     |         *
  9:                     |        *
  6:                     |     *
  1:                     |*
 -6:               *
-15:      *
max=10

      

  【演習問題5】 

 
#define FUNC01 
の部分を次のように書き換えてみましょう。

#define FUNC01 -(x-6)*(x-6) - 5

 これにより、この関数は、 x=6 のとき、最大値、 −5 となる2次関数となります。
 グラフが、マイナス側に寄っていますので、グラフ表示部分の j= の初期値を、マイナス50にします。

 実行すると、データとグラフは表示されるはずですが、最大値の表示が変になるはずです。本当なら、−5 が最大値のはずですが、最大値が 0 となっています。
 これは、最初の max の値が、 0 だったため、最大値がマイナスの場合に対応出来ていないからです。
 そこで、マイナスの場合にも対応するにはどうすればよいかを考えます。
 最初に、非常に小さいマイナスの値を入れておけばよいのですが、いったいどのような数を入れて良いのかわかりません。どんな数を入れても、最大値がそれ以下の場合があり得ます。(変数として使える最大値、最小値については、とりあえず考えないとします。)

 このようなときには、配列の最初の値を、初期値に入れます。

 max = 0 を max = m[0] と書き換えたプログラムを作成し、コンパイル、実行してみると、正しく実行できるはずです。


6.1次元配列のソート

 配列のデータを大きさの順に並べ替えるプログラムを作ります。
いきなり作成するのは難しいので、まず、配列の中で、一番大きな値を、 m[0] に入れるプログラムを考えます。その際、元もと m[0] に入って居た値がなくならないように、現在の m[0] より大きな値を見つけたら、そのデータと交換して、元の値がなくならないようにします。
 データを交換する際には、一時的にデータを取っておく場所が必要なので、そのための変数として、 tmp とうい変数を使います。

  (例題6) 

 最大値を m[0] に入れる。


Project0406

int _tmain(int argc, _TCHAR* argv[])
{
	int tmp;
	int m[10] = { 5, 6, 7, 8, 9, 10, 1, 2, 3, 4 };
	for (int i = 0; i<10; i++){
		printf("m[%d]=%d\n", i, m[i]);
	}

	printf("---------------\n");

	//これ以下のところで、一番大きな値をm[0]に入れている。

	for (int i = 1; i<10; i++){
		if (m[0] < m[i]){
			tmp = m[0];
			m[0] = m[i];
			m[i] = tmp;
		}
	}

	for (int i = 0; i<10; i++){
		printf("m[%d]=%d\n", i, m[i]);
	}
	getchar();
	return 0;
}


実行結果は次のようになるはず。
m[0]=5
m[1]=6
m[2]=7
m[3]=8
m[4]=9
m[5]=10
m[6]=1
m[7]=2
m[8]=3
m[9]=4
---------------
m[0]=10
m[1]=5
m[2]=6
m[3]=7
m[4]=8
m[5]=9
m[6]=1
m[7]=2
m[8]=3
m[9]=4


  【演習問題6】 

 最小値を、m[0] に入れて表示するプログラムに変えて実行してみなさい。


 7.1次元配列のソート

 次に、上のプログラムを応用して、数値を大きさの順に並べ替えるプログラムを作成します。
 forループが2重になっています。

 なぜこのような方法で、大きさの順に並ぶのかの説明をし、図を書きますので、図を、ノートに書き留めて置いて下さい。

  (例題7) 

Project0407


      
int _tmain(int argc, _TCHAR* argv[]) {	int  tmp;
	int m[10] = { 5, 6, 7, 8, 9, 10, 1, 2, 3, 4 };
	for (int i = 0; i<10; i++){
		printf("m[%d]=%d\n", i, m[i]);
	}
	printf("---------------\n");
	for (int i = 0; i<9; i++){
		for (int j = i + 1; j<10; j++){
			if (m[i] < m[j]){
				tmp = m[i];
				m[i] = m[j];
				m[j] = tmp;
			}
		}
	}
	for (int i = 0; i<10; i++){
		printf("m[%d]=%d\n", i, m[i]);
	}
	getchar();        return 0;
}


実行結果は次のよう。
後半を見ると大きいものから小さいものに向かって並んでいることがわかる。
m[0]=5
m[1]=6
m[2]=7
m[3]=8
m[4]=9
m[5]=10
m[6]=1
m[7]=2
m[8]=3
m[9]=4
---------------
m[0]=10
m[1]=9
m[2]=8
m[3]=7
m[4]=6
m[5]=5
m[6]=4
m[7]=3
m[8]=2
m[9]=1

  【演習問題7】 
 並べ直した結果を、小さいものから大きいものへと並べるようにプログラムを変更して実行しなさい。つまり、上記と逆順に並べるものとします。


 8.2次元配列


1〜100 までの数値を、縦横に並べて表示するプログラムを作ってみます。

  (例題8) 

Project0408


#include "stdafx.h"

#define XMAX 10
#define YMAX 10

int _tmain(int argc, _TCHAR* argv[]) { int m[XMAX][YMAX]; for (int x = 0; x<XMAX; x++){ for (int y = 0; y<YMAX; y++){ m[x][y] = x * 10 + y; printf("%3d ", m[x][y]); } printf("\n"); } getchar(); return 0; }

実行結果はこんな感じです。
  0   1   2   3   4   5   6   7   8   9
 10  11  12  13  14  15  16  17  18  19
 20  21  22  23  24  25  26  27  28  29
 30  31  32  33  34  35  36  37  38  39
 40  41  42  43  44  45  46  47  48  49
 50  51  52  53  54  55  56  57  58  59
 60  61  62  63  64  65  66  67  68  69
 70  71  72  73  74  75  76  77  78  79
 80  81  82  83  84  85  86  87  88  89
 90  91  92  93  94  95  96  97  98  99



 【注意】 2次元配列の書き方は、言語により大きく異なります。元は同じ言語であるC++ と C# でも、書き方に相違があります。詳しくは、後期の「応用プログラミング」で説明します。

  【演習問題8】 
 
 実行結果の表示が次のようになるように、プログラムを書き換えなさい。

  0  10  20  30  40  50  60  70  80  90
  1  11  21  31  41  51  61  71  81  91
  2  12  22  32  42  52  62  72  82  92
  3  13  23  33  43  53  63  73  83  93
  4  14  24  34  44  54  64  74  84  94
  5  15  25  35  45  55  65  75  85  95
  6  16  26  36  46  56  66  76  86  96
  7  17  27  37  47  57  67  77  87  97
  8  18  28  38  48  58  68  78  88  98
  9  19  29  39  49  59  69  79  89  99




 【応用 演習問題9】 

例題5 でのグラフの出力では、  0 の場所に  |    を引きましたが、値がマイナスの部分には、 | が引かれていませんでした。そこで、 マイナスの場所にも | が引かれるようにプログラムを改造してみましょう。

 Project0409 として開始しますが、最初は、 Project0405 をコピーして貼り付け、その後、書き直していきます。

 考え方
  元のプログラムでは、横に空白を一つずつ書いていき、    j=< m[i];   がなり立たなくなった点で、 "*" を書いていましたが、m[i] の値にかかわらず、横40文字分出力するように変更すればいいでしょう。
 そのためには、
 for 文での 横の表示数を変更する。
 for 文を抜け出してから "*" を出力するのでは無く、 for 文の中で、 j==m[i] となったときに "*" を打つようにする。(注意:今はスケールが整数で、値が1刻みなので、 == で比較できるが、スケールが違うときには、不等式で比較しなければならない。)

 次のような出力が出るようにしてみましょう。
   

 
m[0]=-6
m[1]=1
m[2]=6
m[3]=9
m[4]=10
m[5]=9
m[6]=6
m[7]=1
m[8]=-6
m[9]=-15
 -6:               *     |
  1:                     |*
  6:                     |     *
  9:                     |        *
 10:                     |         *
  9:                     |        *
  6:                     |     *
  1:                     |*
 -6:               *     |
-15:      *              |
max=10






















時間があれば、次のような課題も行います。


2次曲線の図を描く縦横の入れ替え
円を描いてみる
sinカーブを描く









【追加問題】

2015.4.3追記

1から12までの数値の書かれたカードがある。
適当に6枚ずつに分け、A組、B組とする。

A組から1枚、B組から1枚選んで交換する作業を、最大6回行うことで、
A組に 1〜6まで
B組に 7〜12まで
のカードを配置することができることを証明し、そのようなプログラムを書け。


(2)

適当に分けた A組のカードを a1 a2 a3 a4 a5 a6
B組のカードを b1 b2 b3 b4 b5 b6
とする。
B組に入っているカードのうち、 a1 より小さいものの枚数を m1
a2 より小さいものの枚数を m2
以下同様に、
a6 より小さいものの枚数を m6
とすると、

配り方にかかわらず、

( a1 + a2 + + a6 ) - ( m1 + m2 + + m6 )
は、一定になることを証明し、それを計算するプログラムを作れ。

解法
 最初に、(1)のように、順番に並べる

  1 2 3 4 5 6      7 8 9 10 11 12

中央から両側にたどり、いくつ目かを交換する

  1 2 3 4 10 6   7 8 9 5 11 12

このとき、 a の数は、いくつか大きくなるが、その分、b組の中で、a 組より小さいものの数が増える。その数は、交換した部分の内側にあるカードの枚数+1であり、これが、大きくなった数と同じなので、上記計算結果に変化が無いことを使う。