2 部データ構造とアルゴリズムI レポート課題

本講義では 2 通のレポートで評価します。 レポートは A4 縦の様式で PDF 形式で作成して下さい。 またプログラムは C 言語で作成しなさい。

変更履歴

2022/6/2
課題1初出
2022/6/2-3
誤字訂正
2022/7/11
課題2初出
2022/7/14
課題2-4 問題訂正

課題2

レポート締切日: 2022年7月27日(水)23:59
提出先: BOX にファイル名を 学籍番号-2.pdfとして、 アップロードすること

問題

課題1で使用した構造体 SEISEKI を前提とした、 次の構造体 NODE に関する関数を作成する。


#include "kadai1.h"    
typedef struct node {
  SEISEKI* seiseki;
  struct node* left;
  struct node* right;
} NODE;

ヘッダファイルなどはまとめて後ろに示してある。 また、kadai1.h も使用する。

なお、レポートにおいては、作成したプログラムの説明が必須であるが、課 題1で作成した関数に関しては説明する必要は無い。

問題2-1

以下のnodedata.c中のr1, r2, r3 が参照するデータ構造について、各 NODE型の中のデータの値を明示し、さらにポインターの接続を矢印で結んだ 図を作図しなさい。 なお、各変数の extern 宣言は kadai1.h, kadai2.h で宣言されている。

nodedata.c


#include <stdlib.h>
#include "kadai2.h"
NODE n10={seisekidata21+2};
NODE n11={seisekidata21+1,&n10};
NODE n12={seisekidata21};
NODE r1={seisekidata21+3,&n11,&n12};
NODE n20={seisekidata22+6};
NODE n21={seisekidata22+4};
NODE n22={seisekidata22+5,&n20,&n21};
NODE n23={seisekidata22+2};
NODE n24={seisekidata22};
NODE n25={seisekidata22+1,&n23,&n24};
NODE r2={seisekidata22+3,&n22,&n25};
NODE n30={seisekidata22+5};
NODE n31={seisekidata22+4,&n30};
NODE n32={seisekidata22+6,NULL,&n31};
NODE n33={seisekidata22+1};
NODE n34={seisekidata22+2,NULL, &n33};
NODE n35={seisekidata22,&n34};
NODE r3={seisekidata22+3,&n32,&n35};
NODE* nodearray[]={&r1,&r2,&r3,NULL};

seisekidata2.c


#include <stdlib.h>
#include "kadai1.h"
SEISEKI seisekidata21[]={{"21nc993",70},{"21nc990",90},{"21nc991",80},
{"21nc994",80},{NULL}};
SEISEKI seisekidata22[]={{"埼玉",7350},{"千葉",6259},{"東京",13921},
{"神奈川",9198},{"栃木",1934},{"茨城",2860},{"群馬",1942},{NULL}};
SEISEKI* seisekiarray2[]={seisekidata21, seisekidata22, NULL};
作図例

問題2-2

NODE 型のデータ同士で作った二分木の根を指すポインタ p を引数として、 木の持つSEISEKIデータを左から順に表示する関数 void nodeprint(NODE* p) を作成しなさい。 但し、nodeprint関数内で課題1で作成したseisekiprintを使用して良い。

そして、以下のプログラム ex22.c とnodedata.c seisekidata2.c と結合し、 出力を示しなさい。

ex22.c


#include <stdio.h>
#include <stdlib.h>
#include "kadai2.h"

int main(void){
  NODE** p;
  for(p=nodearray; *p!=NULL; p++){
    nodeprint(*p);
    printf("---\n");
  }
  return 0;
}
出力例2-2
21nc991:80
21nc990:90
21nc994:80
21nc993:70
---
群馬:1942
茨城:2860
栃木:1934
神奈川:9198
東京:13921
千葉:6259
埼玉:7350
---
群馬:1942
茨城:2860
栃木:1934
神奈川:9198
東京:13921
千葉:6259
埼玉:7350
---

問題2-3

2つの SEISEKI 型のポインタ、 p,q を受け取った時、以下の条件に従って値を返す int seisekicomp(SEISEKI* p, SEISEKI* q) を作成しなさい。

  1. p->point と q->point が等しけれれば 0
  2. p->point が q->point より小さければ -1
  3. それ以外は 1

これと、以下のプログラム ex23.c と seisekidata.c を結合して実行し、出 力結果を示せ。

ex23.c


#include <stdio.h>
#include <stdlib.h>
#include "kadai2.h"
int main(void){
  SEISEKI** p;
  SEISEKI* q;
  SEISEKI* r;
  for(p=seisekiarray2; (*p)!=NULL; p++){
    for(q=*p; q->id != NULL; q++){
      printf("\t%s",q->id);
    }
    printf("\n");
    for(q=*p; q->id != NULL; q++){
      printf("%s",q->id);
      for(r=*p; r->id != NULL; r++){
	printf("\t%+d",seisekicomp(q,r));
      }
      printf("\n");
    }
    printf("\n---\n");
  }
  return 0;
}
出力例2-3
	21nc993	21nc990	21nc991	21nc994
21nc993	+0	-1	-1	-1
21nc990	+1	+0	+1	+1
21nc991	+1	-1	+0	+0
21nc994	+1	-1	+0	+0

---
	埼玉	千葉	東京	神奈川	栃木	茨城	群馬
埼玉	+0	+1	-1	-1	+1	+1	+1
千葉	-1	+0	-1	-1	+1	+1	+1
東京	+1	+1	+0	+1	+1	+1	+1
神奈川	+1	+1	-1	+0	+1	+1	+1
栃木	-1	-1	-1	-1	+0	-1	-1
茨城	-1	-1	-1	-1	+1	+0	+1
群馬	-1	-1	-1	-1	+1	-1	+0

---

問題2-4

管理変数の番地とSEISEKIの番地を受け取り、順序木に格納する関数 NODE* nodeadd(NODE** root, SEISEKI* s) を作成しなさい。 但し、順序に関しては 問題2-3で作成した seisekicomp を使用しなさい。 また、戻り地は受け取ったSEISEKI の番地を含んでいる NODE 構造体の番 地を返しなさい。

これと、下記のテストプログラムex24.c, kadai2.h, nodeprint.c, seisekicomp.c, seisekiprint.c, seisekidata2.c を結合して実行し、 出力結果を示せ。

ex24.c


#include <stdio.h>
#include <stdlib.h>
#include "kadai2.h"
void nodedel(NODE* node){
  if(node==NULL) return;
  nodedel(node->left);
  nodedel(node->right);
  free(node);
}
int main(void){
  SEISEKI** p;
  SEISEKI* q;
  NODE* root;
  NODE* s;
  for(p=seisekiarray2; *p!=NULL; p++){
    root=NULL;
    for(q=*p; q->id!=NULL; q++){
      s=nodeadd(&root,q);
      if(s==NULL){
	fprintf(stderr,"メモリーエラー\n");
	nodedel(root);
	return 1;
      }
      seisekiprint(s->seiseki);
      printf("\n");
    }
    printf("---\n");
    nodeprint(root);
    nodedel(root);
    printf("------\n");
  }
  return 0;
}
出力例2-4
21nc993:70
21nc990:90
21nc991:80
21nc994:80
---
21nc993:70
21nc991:80
21nc994:80
21nc990:90
------
埼玉:7350
千葉:6259
東京:13921
神奈川:9198
栃木:1934
茨城:2860
群馬:1942
---
栃木:1934
群馬:1942
茨城:2860
千葉:6259
埼玉:7350
神奈川:9198
東京:13921
------

kadai2.h、Makefile

kadai2.h


#include "kadai1.h"
typedef struct node {
  SEISEKI* seiseki;
  struct node* left;
  struct node* right;
} NODE;
extern NODE* nodearray[];
extern SEISEKI seisekidata21[];
extern SEISEKI seisekidata22[];
extern SEISEKI* seisekiarray2[];
void nodeprint(NODE* p);
int seisekicomp(SEISEKI* a, SEISEKI* b);
NODE* nodeadd(NODE** root, SEISEKI* s);
void nodedel(NODE* node);

Makefile


ex24.exe: ex24.o nodeprint.o seisekidata2.o seisekiprint.o nodeadd.o seisekicomp.o
	$(CC) -o $@ $^
ex23.exe: ex23.o seisekicomp.o seisekidata2.o 
	$(CC) -o $@ $^
ex22.exe: ex22.o nodedata.o nodeprint.o seisekidata2.o seisekiprint.o
	$(CC) -o $@ $^

ex22.o: kadai2.h
ex23.o: kadai2.h
ex24.o: kadai2.h
seisekidata2.o: kadai1.h
seisekiprint.o: kadai1.h
nodedata.o: kadai2.h
nodeprint.o: kadai2.h
seisekicomp.o: kadai2.h
nodeadd.o: kadai2.h

課題1

レポート締切日: 2022年6月15日(水)23:59
提出先: BOX https://tdu.app.box.com/f/e793958f40074a0ba324efe6f514b989 にファイル名を 学籍番号-1.pdfとして、 アップロードすること

解答法として、分割コンパイルによる別ファイルにプログラムを書く方 法と、下記のテストプログラムに関数を追記する方法の二通りがあるが、 どちらでも良い。 プログラムの解説は、自分で作成した関数について行うこと。 こちらで提供したテストプログラムの説明は基本的には不要である。 但し、自分で作成した関数を説明するのに必要な内容には言及すること。

Makefile は使用してもしなくても良い。 また、問題そのものの改変は不合格であるが、プログラムを結合するためな どのやむを得ない必要な変更は許される。

なお、レポートは締め切り後も提出できます。 同じファイル名で複数のバージョンが存在できます。 締め切り内に採点可能なレポートがある場合は、最も締切に近いものだけを採 点します。 締め切り内に有効なレポートがない場合、最も新しいレポートを遅れレポー トとして減点の上採点します。


次の構造体の配列に関する関数を作成する。


typedef struct {
  char* id;
  int point;
} SEISEKI;

問題1-1

SEISEKI型の番地を受け取り、一行表示する関数 void seisekiprint(SEISEKI*) を作成せよ。 但し、ファイル名を seisekiprint.c とする。

これと、下記のテストプログラム, kadai1.h, seisekidata.c を結合して実行し、出 力結果を示せ。

ex11.c


#include <stdio.h>
#include <stdlib.h>
#include "kadai1.h"
int main(void){
  SEISEKI** p;
  SEISEKI* q;
  for(p=seisekiarray; *p!=NULL; p++){
    for(q=*p; q->id!=NULL; q++){
      seisekiprint(q);
      printf("\n");
    }
    printf("\n");
  }
  return 0;
}
出力例1-1
21nc990:90
21nc991:80
21nc993:70
21nc994:80

東京:13921
神奈川:9198
埼玉:7350
千葉:6259
茨城:2860
群馬:1942
栃木:1934

問題1-2

idがNULLとなっているデータで終端するSEISEKI型の配列の先頭番地を受け取り、 pointの平均値を返す double seisekiave(SEISEKI* p) を作成せよ。 この関数のファイル名を seisekiave.c とする。

これと、下記のテストプログラム, seisekidata.c, kadai1.h を結合して実行し、出 力結果を示せ。

ex12.c


#include <stdio.h>
#include <stdlib.h>
#include "kadai1.h"
int main(void){
  SEISEKI** p;
  for(p=seisekiarray; *p!=NULL; p++){
    printf("%f\n",seisekiave(*p));
  }
  return 0;
}
出力例1-2
80.000000
6209.142857

問題1-3

idがNULLとなっているデータで終端するSEISEKI型の配列の先頭番地を受け取り、 pointの標本分散を返す double seisekivar(SEISEKI* p) を作成せよ。 この関数のファイル名を seisekivar.c とする。

これと、下記のテストプログラム, seisekidata.c, kadai1.h を結合して実行し、出 力結果を示せ。

ex13.c


#include <stdio.h>
#include <stdlib.h>
#include "kadai1.h"
int main(void){
  SEISEKI** p;
  for(p=seisekiarray; *p!=NULL; p++){
    printf("%f\n",seisekivar(*p));
  }
  return 0;
}
出力例1-3
50.000000
16773165.836735

問題1-4

平均値、標本標準偏差、SEISEKI型の番地を受け取り、偏差値を返す関数 int seisekihensachi(double ave, double s, SEISEKI* p) を作成せよ。 この関数のファイル名を seisekihensachi.c とする。

これと、下記のテストプログラム, seisekidata.c, seisekiprint.c, seisekiave.c, seisekivar.c, kadai1.h を結合して実行し、出 力結果を示せ。 なお、 sqrt を使用するために libm.a を結合する必要がある場合がある (Makefile では、コンパイルスイッチ -lm で対応)。

ex14.c


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "kadai1.h"
int main(void){
  SEISEKI** p;
  SEISEKI* q;
  double ave;
  double s;
  for(p=seisekiarray; *p!=NULL; p++){
    ave=seisekiave(*p);
    s=sqrt(seisekivar(*p));
    for(q=*p; q->id!=NULL; q++){
      seisekiprint(q);
      printf(":%d\n",seisekihensachi(ave,s,q));
    }
    printf("\n");
  }
  return 0;
}
出力例1-4
21nc990:90:64
21nc991:80:50
21nc993:70:35
21nc994:80:50

東京:13921:68
神奈川:9198:57
埼玉:7350:52
千葉:6259:50
茨城:2860:41
群馬:1942:39
栃木:1934:39

kadai1.h


typedef struct {
  char* id;
  int point;
} SEISEKI;
extern SEISEKI seisekidata1[];
extern SEISEKI seisekidata2[];
extern SEISEKI* seisekiarray[];
void seisekiprint(SEISEKI* p);
double seisekiave(SEISEKI* p);
double seisekivar(SEISEKI* p);
int seisekihensachi(double ave, double s, SEISEKI* p);

seisekidata.c


#include <stdlib.h>
#include "kadai1.h"
SEISEKI seisekidata1[]={{"21nc990",90},{"21nc991",80},{"21nc993",70},{"21nc994",80},{NULL}};
SEISEKI seisekidata2[]={{"東京",13921},{"神奈川",9198},{"埼玉",7350},{"千葉",6259},
{"茨城",2860},{"群馬",1942},{"栃木",1934},
			{NULL}};
SEISEKI* seisekiarray[]={seisekidata1, seisekidata2, NULL};

Makefile

$(CC) -o $@ $^の $(CC) の前には TAB 記号が一つ だけ入っていることに注意する。これが空白記号に置き換わっていると動作しない。


ex11.exe: ex11.o seisekidata.o seisekiprint.o
	$(CC) -o $@ $^
ex12.exe: ex12.o seisekidata.o seisekiave.o
	$(CC) -o $@ $^
ex13.exe: ex13.o seisekidata.o seisekivar.o
	$(CC) -o $@ $^
ex14.exe: ex14.o seisekidata.o seisekiprint.o seisekiave.o seisekivar.o seisekihensachi.o
	$(CC) -o $@ $^ -lm

ex11.o: kadai1.h
ex12.o: kadai1.h
ex13.o: kadai1.h
ex14.o: kadai1.h
seisekidata.o: kadai1.h
seisekiprint.o: kadai1.h
seisekiave.o: kadai1.h
seisekivar.o: kadai1.h
seisekihensachi.o: kadai1.h

参考:統計値の定義

n個の点数データを p1 , ... , pn とする。

平均

ave = i=1 n pi n

標本分散

var = i=1 n ( pi - ave ) 2 n = i=1 n pi 2 n - ave 2

標本標準偏差

s = var

偏差値

hensachi i = 50 + 10 ( p i - ave s )

レポート作成上の注意

  1. レポートはグラフの表現など特殊な事情がない限り、必ず白黒で作成する こと。
  2. プログラミングのレポートでは必ずプログラムの説明をすること。その時 に、一行一行を日本語に直訳するのではなく、プログラムを機能毎に区分し、 機能の実現方法を説明すること。 プログラムに一行ずつコメントを入れてもプログラムの説明とは見なしません。
  3. 「問題を解きなさい」という問に対して「解きました。合ってました」は 正解ではありません。 「プログラムを作りなさい」という問題については、作成の手順の説明をする こと。 「テストしなさい」という問題についてはテストの手法、合否判定の基準、結 果の検討などを説明しなさい。
  4. プログラムが短くて説明がしづらい場合は、ポインタなどの関係を図示す るなどして工夫してください。
  5. 出力例は個々に異なりますので、手計算であらかじめ正しい値の求め方を 示し、値を提示してテストの合否を判定しなさい。
  6. レポートは手書きでも良いですが、プログラムの実行結果だけは必ずコン ピュータの出力の印刷にして下さい。
  7. 考察は必ず書いて下さい。ネタがない人は以下を参考にして下さい。
    • 途中失敗を経験した場合、その失敗した内容となぜ失敗したのか
    • ポインタ、関数、配列などの仕組みについて、新しく分かったこと

なお、写したと思われるほど酷似したレポートが複数提出された場合、原著が どれかの調査を行わず、抽選で一通のレポートのみを評価 の対象とし、他は提出済みの不合格レポートとして再提出は課しません。 自分で意図せずに他人にコピーされてしまった場合も同様ですので、レポート の取り扱いについては十分に注意して下さい。


坂本直志 <sakamoto@c.dendai.ac.jp>
東京電機大学工学部情報通信工学科