Report 1b

b1910586 1b:○ Mon May 18 23:54:15 2020


提出日時:2020/05/18

[課題]
演習1.b 整数nを入力し、2^n を出力する。n ≥ 0のときは整数、そうで
ないときは実数形式で出力すること。

演習2.a epsライブラリーを使って、4っの絵を作成する。

[演習1.bのソースコード]
#include <stdio.h>
#include <math.h>
#include <stdio.h>
#include <math.h>

double power2(int n){
  if(n == 0) return 1;
  else if(n < 0) 
    return power2(n+1)/2;
  else return 2*power2(n-1);
}

int main(void){
  int n;
  scanf("%d", &n);
  if(n>=0)
    printf("%i\n", (int)power2(n));
  else
    printf("%.2f\n", power2(n));
  return 0;
}

[演習1.bの説明および考察]
再帰的に2で割るか2をかけることで、2^nが求まった。出力の時はnの正
負の場合によって、タイプキャストし、整数形式で出力するか、そのま
ま実数形式で表した。
タイプキャストの他にfloorかceilも使えた。
nが大きい場いいはどうなるか気になった。例えば、2^1000を計算した
結果が1.07151e+301となったが、少し調べたところ、最下位数字を求め
るにはcmathのfmod関数を使わなければならない。

[演習2.aのソースコード]
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");   
  eps_drawline(-200, 0, 200, 0);  
  eps_drawline(0, 200, 0, -200);  
  for(int i = 0; i < 16; ++i) {
    eps_drawrect(-(i%4)*25-40, 100-(i/4)*25, 20, 20);
  }
  for(int i = 0; i < 25; ++i) {
      if(i%2 == 0) eps_fillrect((i%5)*20+20, 100-(i/5)*20, 20, 20);
  }
  for(int i = 0; i < 25; ++i) {
      if(i/5<1 || i/5>=4) eps_drawcircle(-(i%5)*20-30, -(i/5)*20-30,10);
      else if(i%5 == 0 || i%5 == 4) eps_drawcircle(-(i%5)*20-30, -(i/5)*20-30,10);
  }
  eps_cmd("70 -70 translate");  
  for(int i = 1 ; i <= 10; ++i) {
      eps_drawline(-50, 0, 50, 0);  
      eps_num(180/10); eps_cmd("rotate");   
  }
  eps_close();                          
  return 0;
}

[演習2.aの説明および考察]
できるだけ描く関数を一回書いて、ループで何回もコールできるように
考えた。そうするには、%と/の演算子を使った。x座標を増やす時に
i%nを使って計算した。
y座標はとびとびの値をとるため/演算子が向いていると判断した。

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
あまり大変ではなかったです
Q2. epsライブラリを使ってみてどのように感じましたか。
基礎プログラミングでは自分で線分とかを書きましたが、epsを使って
細かいアルゴリズムを考えなくてよいから、より楽です。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
特にないです。授業の高度な内容に取り組みたいです。

f1810567 1b:△ Mon May 18 23:28:42 2020


学籍番号:1810567
提出日付:5/18

・演習1b
[課題の再掲]
整数nを入力し、2のn乗を出力する

[プログラムのソースと説明]
#include <stdio.h>
#include <math.h>

float beki(int n){
  float result = pow(2, n);
  return result;
}

int main(void){
  int n;
  printf("n> "); scanf("%d", &n);
  if(n >= 0){
    printf("2**n=%d\n", (int)beki(n));
  }
  else{
    printf("2**n=%f\n", beki(n));
  }
}

2をn乗する関数beki(n)を定義する。計算結果はfloat型にして出力する。
main関数内ではnが非負整数か否かで分岐させて、nが非負整数の場合は
beki(n)の結果をfloat型からint型へキャストして出力、そうでないと
きはそのまま結果を出力する。

[考察]
数学ライブラリの関数powを使わずに書きたかったが、n<0の場合にどう
書けばいいのか思いつかなかったので諦めた。べき乗の計算には処理速
度の問題などでなるべくpowを使わない方がいいということが調べて分
かった。

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
簡単な問題でも全部自力でやるのは思った以上に時間がかかった。
Q2. epsライブラリを使ってみてどのように感じましたか。
Cでもこのように画像の描画を行えるのは面白いと思った。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
実際に自力でプログラムを書いてみないと、自分が本当に理解している
のか確認出来ないと思った。例題をしっかり手を動かして確認すること
の重要性を学んだ。

f1910560 1b:○ Mon May 18 23:34:41 2020


2020/05/18

演習1d
整数nを入力し、n以下の素数を出力する。

プログラム
#include<stdio.h>
#include<math.h>
#include<stdbool.h>

bool prime(int n){
  int limit = (int)sqrt(n);
  for(int i = 2; i <= limit; ++i){
    if(n % i == 0){
      return false;
    }
  }
  return true;
}

void nprime(int n){
  int i;
  for(i = 2; i <= n; ++i){
    if(prime(i) == true){
      printf(" %d", i);
    }
  }
  printf("\n");
}

int main(void){
  int n;
  printf("n>"); scanf("%d", &n);
  nprime(n);
  return 0;
}

結果
./a.out
n>50
 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47

説明
例題の素数判定のプログラムを用い、nprimeで2からnまでの数の素数判
定をし、素数であればprintfで書き出していく。基礎プログラミングの
C言語入門繰り返しの構文のtimes1を参考にした。

考察
最初、数列を使い、素数判定で素数であったものをその数列の中に入れ
ていき、最後にその数列を返すという形を考えた。しかし、数列を忘れ
ていたので見返そうとおもったらtimes1を見つけたのでそれを使うこと
にした。

また、最初はprimeで素数でなかったときは何も返さず、素数であった
ときだけnを返すという形に始用途したが、return ;という形には出来
無かったので、このように素数判定を行い、trueであった場合のみ
nprimeの中でそのnを記録していくという形にした。

演習2a(2)
次の図のような絵を作成する。色や線幅は自由にしてよい

プログラム
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("2a2.ps", 480, 480);
  for(int i = 15; i <= 375; i = i + 180){
    for(int j = 15; j <= 375; j = j + 180){
      eps_cmd("0.3 0.5 1.0 sethsbcolor");
      eps_fillrect(i, j, 90, 90);
    }
  }
  for(int i = 105; i <= 285; i = i + 180){
    for(int j = 105; j <= 285; j = j + 180){
      eps_cmd("0.3 0.5 1.0 sethsbcolor");
      eps_fillrect(i, j, 90, 90);
    }
  }  
  eps_close();                          
  return 0;
}

説明
eps_openで2a2.psという480×480の描画面を作る。一つめのfor内で縦
横3個ずつ合計9個の正方形を描き、二つめのfor内で縦横2個ずつ合計4
個の正方形を描いた。色はHSBで指定した。

考察
色をiやjに伴って変わるように指定しようとしたが、出来無かった。最
初、例題を参考にeps_numを使ったが、i*j/480/480のように0.0から1.0
の間に入り、なおかつ左下と右上である程度の変化がつく様に指定して
も全ての正方形が赤になるなど、上手くいかなかった。
最初、一つのfor内に9個の正方形とそれを右斜め上にずらした正方形を
描くことを考えたが、この描画面は必要な分よりも少し大きいので上と
右に余計な分が付くことに気付いたので止めた。

アンケート
Q1.
そこまで大変ではなかった。
Q2.
面白いけれどよく分からないところもある。
Q3.
特になし。

f1910563 1b:○ Mon May 18 22:49:06 2020


プログラミング通論レポートB課題 #1
提出日付:5月18日

<演習1.b>
[再掲]
整数nを入力し、2nを出力する。n ≥ 0のときは整数、そうでないときは 
実数形式で出力すること。

[プログラム]
#include <stdio.h>
#include <math.h>

double two(int n){
  return pow(2,n);
}

int main(void){
  int n;
  printf("n> "); scanf("%d", &n);
  printf("pow = %g\n",two(n));
  return 0;
}

[実行結果]
[@@@f1910563@sol 1]$ gcc8 1b.c -lm
[@@@f1910563@sol 1]$ ./a.out
n> 0
pow = 1
[@@@f1910563@sol 1]$ ./a.out
n> 3
pow = 8
[@@@f1910563@sol 1]$ ./a.out
n> -2
pow = 0.25

[説明]
冪乗pow関数を使うので、math.hをインクルードする。pow関数を使って、
2のn乗を出力する。実数で出力するので、関数の型はdoubleとした。
main関数で、入力する変数は整数のなので、int型で宣言をし、prinft
とscanfで入力を行った。printfで、出力する数字は実数なので書式設
定は%gで出力した。

[考察]
数学ライブラリを使うときに、math.hをインクルードすること、冪乗に
pow関数を使うことができることを復習することができた。今回は入力
するときに、printfでn>を出力しその後に数字を入力したが、数字の入
力する方法として、main関数を次のようにやる方法もある。

int main(int argc, char *argv[]){
  int n = argv[1];
  printf("pow = %g\n",two(n));
  return 0;
}

<演習1.d>
[再掲]
整数nを入力し、n以下の素数を出力する(出力の順番や形式は任意)。 

[プログラム]
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool isprime(int n) {
  int limit = (int)sqrt(n);
  for(int i = 2; i <= limit; ++i) {
    if(n % i == 0) { return false; }
  }
  return true;
}

void factorization(int n){
  for(int k=2; k <= n; k++){
    if(isprime(k)){printf("%d ",k);}
  }
}

int main(void){
  int n;
  printf("n> "); scanf("%d", &n);
  factorization(n);
  printf("\n");
  return 0;
}

[実行結果]
[@@@f1910563@sol 1]$ gcc8 1d.c -lm
[@@@f1910563@sol 1]$ ./a.out
n> 17
2 3 5 7 11 13 17 
[@@@f1910563@sol 1]$ ./a.out
n> 38
2 3 5 7 11 13 17 19 23 29 31 37

[説明]
素数判定のためにstdbool.hをインクルードした。素数を調べるために
平方根を出すsqrtを使うのでmath.hをインクルードした。まず、素数を
判定する関数の型をboolとして作った。その内容は 、nの平方根以下の
数字でnを1つずつ割っていき、割り切れた時はfalseを出力し、2以上
nの平方根以下の数字全てnで割り切れなかった場合、約数がないという
ことなので素数である。つまりtrueと出力する。次に、n以下の素数を
出力する関数を作った。その内容は、素数は2以上なのでパラメータk
の初期値を2に設定し、そこからnまで一つずつ増やすfor文で、素数判
定でtrueと判定された数字をprintfで出力した。最後に改行した。


[考察]
n以下の素数を出力するにあたって、素数判定と素数を出力する関数を
分けることでプログラムがわかりやすくなると考えた。素数の判定の仕
方として、nの平方根以上でnが割り切れた場合、その商はnの平方根よ
り小さい値となるため調べるのはnの平方根まででいいということにな
り、2からnの平方根のうちどれも割り切れなかったら約数がないとい
うことなので素数ということがわかるということがわかった。上の関数
では改行をmain関数のそとに追加するように入れたが、関数
factorizationの中の最後に入れるという方法もあると考えた。

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
今回は復習の内容だったので特に苦労することはなかったです。

Q2. epsライブラリを使ってみてどのように感じましたか。
gvをインストールしなければいけなくて大変でした。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。

今回は比較的易しい内容でしたので、次回以降ちゃんと身につくように
頑張っていきたいです。

f1910567 1b:○ Mon May 18 20:59:54 2020


プログラミング通論 レポート 1b
提出日付: 2020/05/18

[課題の再掲1]

課題1.e: 数列の最初のn項を出力する。

[プログラムのソース1]

#include <stdio.h>
void fibonacci(double *arr,int len){
  arr[0] = 0;
  if(len==1){return;}
  arr[1] = 1;
  if(len==2){return;}
  int i;
  for(i=2; i<len; ++i){
    arr[i] = arr[i-1] + arr[i-2];
  }
}
void nacci(double *arr, int len, int k, double fill, double fill2){
  int i;
 
  if(len==0){return;}
  if(len<k){
    for(i =0; i<len; ++i){
      arr[i] = fill;
    }
    return;
  }
  for(i =0; i<k-1; ++i){
    arr[i] = fill;
  }
  arr[i] = fill2; ++i;
  for( ; i<len; ++i){
    double sum = 0;
    for(int j=0; j<k; ++j){
      sum += arr[i-1-j];
    }
    arr[i] = sum;
  }
}
int main(void){
  int n,k;
  double fill1, fill2;
  printf("print F(n,k): F(n,k) = F(n-1,k) +...+ F(n-k,k) (n:0,1,...,N-1)\n");
  printf("N = "); scanf("%d",&n);
  printf("k = "); scanf("%d",&k);
  printf("F(0:k-1,k) = "); scanf("%lf",&fill1);
  printf("F(k-1,k) = "); scanf("%lf",&fill2);
  printf("\n");
  double arr[n];
  //fibonacci(arr, n);
  nacci(arr, n, k, fill1, fill2);
  for(int i=0; i<n;++i){
    printf("F(%d,%d) = %.0lf\n",i,k,arr[i]);
  }
  return 0;
}

[プログラムの説明1]

数列 F(n,k) を以下のように定義する。

F(n,k) = a  (n: 0,1,...,k-2)
F(n,k) = b  (n: k-1)
F(n,k) = F(n-1,k) +...+ F(n-k,k)  (n: k,k+1,...,N-1)

プログラムは上式の第0項から第n-1項までを順に出力する。
また、上式は、k=2, a=0, b=0 のときフィボナッチ数列となる。

プログラムの内容は以下の通りである。

N, k, a, b, の値をscanfで読み取る。
長さNの配列を作成する。
関数nacciを呼び出し、配列に数列 F(n,k)を代入する。
配列の値を順に出力する。

また関数nacciは、配列のインデックスiに対応した値 F(i,k) を配列に代入する。

実行例は以下の通りである。
---------------------

[〜]$ ./a.out
print F(n,k): F(n,k) = F(n-1,k) +...+ F(n-k,k) (n:0,1,...,N-1)
N = 10
k = 2
F(0:k-1,k) = 0
F(k-1,k) = 1
 
F(0,2) = 0
F(1,2) = 1
F(2,2) = 1
F(3,2) = 2
F(4,2) = 3
F(5,2) = 5
F(6,2) = 8
F(7,2) = 13
F(8,2) = 21
F(9,2) = 34
[〜]$ ./a.out
print F(n,k): F(n,k) = F(n-1,k) +...+ F(n-k,k) (n:0,1,...,N-1)
N = 10
k = 3
F(0:k-1,k) = 0
F(k-1,k) = 1
 
F(0,3) = 0
F(1,3) = 0
F(2,3) = 1
F(3,3) = 1
F(4,3) = 2
F(5,3) = 4
F(6,3) = 7
F(7,3) = 13
F(8,3) = 24
F(9,3) = 44
[〜]$ ./a.out
print F(n,k): F(n,k) = F(n-1,k) +...+ F(n-k,k) (n:0,1,...,N-1)
N = 10
k = 2
F(0:k-1,k) = 2
F(k-1,k) = 1
 
F(0,2) = 2
F(1,2) = 1
F(2,2) = 3
F(3,2) = 4
F(4,2) = 7
F(5,2) = 11
F(6,2) = 18
F(7,2) = 29
F(8,2) = 47
F(9,2) = 76

[考察1]

プログラムの作成に当たっては、はじめに配列にフィボナッチ数列を代
入する関数fibonacciを作成し、それを拡張した。また、各項の計算に
おいては、配列に記録した前項の計算結果を用いることで計算の時間を
短縮出来たと考えられる。

[課題の再掲2]

演習2.b: 多項式による関数をグラフ描写する。

[プログラムのソース2]

#include <stdio.h>
#include "eps.h"
#include <math.h>
#define PI (3.1415926535)
void func(double t, double *x, double *y, double scale);
 
int main(void) {
  eps_open("out.ps", 600, 850);  
  eps_cmd("300 425 translate");  
  eps_drawline(-250, 0, 250, 0); 
  eps_drawline(0, 375, 0, -250);
  double x, y, x0, y0;
  func(0, &x0, &y0, 10);
  int i;
  for(i = 1; i <=360 ; ++i) {
      func(i/180.0*PI, &x, &y, 10);
      //printf("%d %d\n",x,y);
      eps_num(i/360.0); eps_cmd("1.0 1.0 sethsbcolor");
      eps_drawline(x0, y0, x, y);
      x0 = x; y0 = y;
  }
  int f1 = eps_newfont("Courier", 30);     
  eps_puts(f1, -250, -290, "x = 16*(sin(t))^3");
  eps_puts(f1, -250, -330, "y = 13*cos(t) - 5*cos(2*t)");
  eps_puts(f1, -250, -370, "      - 2*cos(3*t) -cos(4*t)");
  eps_close();                         
  return 0;
}
 
void func(double t, double *x, double *y, double scale){
    *x = scale * (16*pow(sin(t),3));
    *y = scale * (13*cos(t) - 5*cos(2*t) - 2*cos(3*t) -cos(4*t));
}

[プログラムの説明2]

任意の実数 t に対して以下の式を満たすグラフを描写する。

x = 16*(sin(t))^3
y = 13*cos(t) - 5*cos(2*t) - 2*cos(3*t) -cos(4*t)

プログラムの主な内容は以下の通りである。

出力するpsファイルを開く。
基準となるt (プログラム中ではi)に上式を満たすx,y代入する関数func
を呼び出し、この値を基準x0, y0とする。

以下をtの値を微小に変化させながら行う。
上式を満たすx, y 代入する関数funcを呼び出し、x, yに代入する。
色を指定する値をtに応じて変更する。
点(x0,y0),(x,y)を結ぶ線分を描写する。
x0, y0にx, yの値を代入する。

図に関する説明を書き込む。
ファイルを閉じる。

[考察2]

上式でのtの範囲ついて考える。
便宜的に関数funcをtに対応するx,yを返す関数 func(t)とすると、x,
yはそれぞれtまたはtの整数倍を変数とした三角関数からなる多項式で
表されるので、周期2*piの周期関数である。
したがって、関数func(t)も周期2*piの周期関数である。
よって、グラフを描写するためには、0 <= t <= 2*pi の区間で描写すれば良いことがわかる。

[アンケート]

#Q1: プログラムを作るという課題はどれくらい大変でしたか?
A: 今のところあまり大変ではない。図や数式で理解出来ているものに
ついての簡単プログラムを書くことは容易に出来ると思う。

#Q2: epsライブラリを使ってみてどのように感じましたか。
A: 便利な道具だと思った。

#Q3: リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
A: C言語の基本的な扱い方について復習することが出来た。

f1910575 1b:○ Fri May 15 16:06:50 2020


プログラミング通論 第1回 レポート1B


【選択した課題1】演習1e
(課題の要約)
整数nを入力し、任意に選択した数列の最初のn項を出力するプログラムを作成する。

【方針1】
任意の等差数列を扱うことにした。出力する項数nに加えて、初項および公差を整数
で受け取る。初めに一般項を出力し、そのあと第n項まで出力する方針で作成した。

【コード1】
#include <stdio.h>

void sequence(int n, int a0, int d) {
  if (a0 == d) {
    printf("一般項 %dn\n", d);
  } else if(a0 > d) {
    printf("一般項 %dn+%d\n", d, a0 - d);
  } else {
    printf("一般項 %dn%d\n", d, a0 - d);
  }
  for (int i = 1; i <= n; i++) {
    if (i <= 9) {
      printf("第%d項 %5d\n", i, a0 + d*(i - 1));
    } else {
      printf("第%d項 %4d\n", i, a0 + d*(i - 1));
    }
  }
  printf("\n");
}

int main() {
  int n, a0, d;
  printf("初項を入力してください(整数に限る): "); scanf("%d", &a0);
  printf("公差を入力してください(整数に限る): "); scanf("%d", &d);
  printf("表示する項数を入力してください: "); scanf("%d", &n);
  sequence(n, a0, d);
  return 0;
}

【実行例1】
以下に実行例をいくつか示した。

[f1910575@sol l1]$ ./a.out
初項を入力してください(整数に限る): 3
公差を入力してください(整数に限る): 5
表示する項数を入力してください: 15
一般項 5n-2
第1項     3
第2項     8
第3項    13
第4項    18
第5項    23
第6項    28
第7項    33
第8項    38
第9項    43
第10項   48
第11項   53
第12項   58
第13項   63
第14項   68
第15項   73

[f1910575@sol l1]$ ./a.out
初項を入力してください(整数に限る): 5
公差を入力してください(整数に限る): 3
表示する項数を入力してください: 15
一般項 3n+2
第1項     5
第2項     8
第3項    11
第4項    14
第5項    17
第6項    20
第7項    23
第8項    26
第9項    29
第10項   32
第11項   35
第12項   38
第13項   41
第14項   44
第15項   47

[f1910575@sol l1]$ ./a.out
初項を入力してください(整数に限る): 5
公差を入力してください(整数に限る): 5
表示する項数を入力してください: 15
一般項 5n
第1項     5
第2項    10
第3項    15
第4項    20
第5項    25
第6項    30
第7項    35
第8項    40
第9項    45
第10項   50
第11項   55
第12項   60
第13項   65
第14項   70
第15項   75

【解説1】
まず引数として出力する項数n、初項a0、公差dを設定した。関数sequence
内初めのif文は、一般項を表示するための場合分けである。一般項まで整
理した際に+-の表示が正しくされるように初項と公差の大小関係に応じて
場合分けをした。if文の後は第n項まで表示する部分である。for文を使用
し、等差数列の公式に基づいて第n項まで計算をする。main関数部分では
初項、公差、項数を受け取り、関数sequenceを呼び出している。

【レビュー課題】
レビューをもらった人 : TA
(以下レビュー内容)
等差数列を自由に設定でき、さらに一般項を丁寧に場合分けされていると
いった、工夫が施されていて良いと思います。

【考察1】
公差と項数を整数に限定したが、実数の範囲まで拡張してもよかったと思った。
ただしその場合、整数型であるnをキャスト演算などで工夫する必要があると考
えられる。最後に各項を出力する部分でも、実数値として計算された値を見や
すく表示する必要があると考えた。今回もそうしたように、表示桁数の工夫が
有効ではないかと思われる。

【選択した課題2】演習2b
(課題の要約)
epsライブラリを利用し、数学で扱うような関数を描画する。

【方針2】
epsライブラリ中のeps_drawlineを使い、細かな直線をつなげて関数を描画する。

【コード2】
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out2b.ps", 480, 480);
  eps_cmd("240 80 translate");
  eps_drawline(-200, 0, 200, 0);
  eps_drawline(0, 360, 0, -40);
  eps_drawline(200, 0, 190, 10);
  eps_drawline(200, 0, 190, -10);
  eps_drawline(0, 360, -10, 350);
  eps_drawline(0, 360, 10, 350);
  int f1 = eps_newfont("Times-Roman", 20);
  eps_puts(f1, 210, -5, "x");
  eps_puts(f1, -5, 370, "y");
  eps_puts(f1, 5, -20, "O");
  for (double i = -190; i < 190; ++i) {
    eps_drawline(i, i*0.1*i*0.1, i+1, (i+1)*0.1*(i+1)*0.1);
  }
  eps_close();
  return 0;
}

【実行例2】
図を示すことができないが、確かに滑らかな曲線で関数を描画することができた。

【解説2】
ヘッダファイルeps.hおよびその実装ファイルは授業スライド通りである。
初めに480×480でファイルを開き、原点を画像内下よりに設定した。
設定した原点に合わせてx軸とy軸を描画した。続けて軸の名前も描画した。
今回選んだ関数は y=x^2 である。for文を用いて細かく直線を描画するこ
とにより、グラフを描画した。

【考察2】
初めはx^2を計算するためにiをそのまま2乗したが、画像サイズを480×480
にしていたため、i^2の値が190×190などのように過大になってしまった。
そのため y=x^2のグラフが縦につぶれてしまった。そこで、iはそのままx座
標として扱うが、1/10を乗じてから2乗するようにしたところ、画像の適度
な範囲内に収まった。数学ではx軸の値を左右にそこまで大きく扱わないた
め、プログラム上で単純に2乗するとこのようなことが起こると考えられる。
滑らかなグラフを描画するには工夫が必要だと分かった。

【アンケート】
(誤って1Aのレポートで1Bのアンケートに回答してしまったので、ここでは
1Aのアンケートに回答します。)
Q1. C言語のプログラミングは好き/嫌いどちら? 理由は?
A1. 型が明確に決まっているなど、良い意味で理屈っぽいので好きです。

Q2. epsライブラリについてどのように思いましたか。
A2. 1年次にやったときより理解できていると感じました。

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
A3. epsライブラリの部分でファイルを分けて1つのプログラムを完成させる
  作業が良い復習になりました。

f1910576 1b:○ Sat May 16 14:14:57 2020


学籍番号 1910576
氏名 藤松栞 (ペア:「個人作業」)
提出日時 5/16

1.1つ目の課題
1-1.課題の内容
(演習1c) 整数n(n>1)を入力し、nの素因数分解を表示する。

1-2.描いたプログラム
#include <stdio.h>

void soinsu(int a){
int i=2;
    while(i<=a){
if(a%i==0){
    a=a/i;
    printf("%d ",i);
    }else{
         
        ++i;
    }
    }
    printf("\n");
}
int main(void){
    int n;
   printf("n>");scanf("%d",&n);
   soinsu(n);
    return 0;
}

1-3.プログラムの説明
soinsuは、素因数分解を行う関数で与えられた数をiで割り切れなくな
るまで割り、aがiで割り切れなくなったらiに1ずつ足していくような関
数を作っていき、その動作をiがaより小さい間行うようにした。また割
り切れた時その時のiの値を表示するようにした。main内では値を読み
込んで関数に代入するだけにした。


1-4.考察
繰り返しは主にforを用いており、使い方が曖昧だったのもありwhileは
使って来なかったのでwhile(条件式)の条件式の部分は「(条件式)まで
繰り返す」だと思っていた。そのため最初whileの条件部分をa==1と書
いていたらエラーが発生した。whileの条件式は「(条件式)が成り立つ
間繰り返す」であることを再認識した。そのためwhile(a!=1)の間でも
成り立つのではないかと考えられる。関数をある程度使えるようになり
mainに描いていたときよりすっきりして見えるのでこのまま使いこなせ
るようにしていきたい。

2. 2つ目の課題について
2-1.課題の内容
(演習2a)4つの絵(a)~(d)を作成する。

2-2. 描いたプログラム
(a)// en1eps.c --- demonstration of eps library.
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");   
 
  for(int i = 0; i <= 3; ++i) {
      for(int j=0;j<=3;++j){
    eps_cmd("0.0 setgray");
    eps_cmd("1 setlinewidth");         
    eps_drawrect((-200)+i*105, 115-j*105, 85, 85);  
    
  }
  }
  eps_close();                          
  return 0;
}

(b)// en2eps.c --- demonstration of eps library.
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");   
 
  for(int i = 0; i <= 2; ++i) {
      for(int j=0;j<=2;++j){
    eps_cmd("0.4 setgray");
           
    eps_fillrect((-200)+i*160, 120-j*160, 80, 80);  
}
  }
  for(int i = 0; i <= 1; ++i) {
      for(int j=0;j<=1;++j){
    eps_cmd("0.4 setgray");
          
    eps_fillrect((-120)+i*160, 40-j*160, 80, 80);  
}
  }
  eps_close();                          
  return 0;
}

(c)// en3eps.c --- demonstration of eps library.
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");   
 
  for(int i = 0; i <= 1; ++i) {
      for(int j=0;j<=4;++j){
    eps_cmd("0.0 setgray");
    eps_cmd("1 setlinewidth");         
    eps_drawcircle((-160)+320*i,160-80*j , 40);   
  }
  }
   for(int i = 0; i <= 2; ++i) {
      for(int j=0;j<=1;++j){
    eps_cmd("0.0 setgray");
    eps_cmd("1 setlinewidth");         
    eps_drawcircle((-80)+80*i,160-320*j , 40);   
  }
  }
  eps_close();                          
  return 0;
}
(d)// en4eps.c --- demonstration of eps library.
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");   
 
  for(int i=1;i<=8;++i){
      eps_num(i*0.1);eps_cmd("setgray");
       eps_drawline(-200, 0, 200, 0); 
      eps_num(22.5*i);eps_cmd("rotate");

  }
  
  eps_close();                          
  return 0;
}

2-3.プログラムの説明
この時、eps.hとeps.cはそのまま利用した。
(a)480*480の紙面を用意し、原点を紙面の真ん中においた。今回塗りつ
ぶしの四角形ではないためdrawrect関数を用いた。2次元配列を表示さ
せるときのプログラムを応用して1辺が85,正方形の隙間が20になるよ
うにした。1番左の列の1番上の正方形の左下の点の座標を基準として
(このとき全ての正方形の左下の直角部分をその正方形の座標の基準と
した)、jの繰り返しでy軸方向へ繰り返しを、iの部分の繰り返しでx軸
方向での繰り返しを行って4*4の正方形ほ並びを作成した。また線の太
さを1pointにし、線の濃さは黒にしたかったのでsetgrayの濃さを0.0に
した。

(b)480*480の紙面を用意し、原点を紙面の真ん中においた。今回は(a)
とは異なり塗りつぶしの四角形なので描くときにfillrectを用いた。四
角形の個数が上から3,2,3,2,3となっているので最初に3個のところのみ
作成した。四角形の大きさは全て一辺が80になるようにした。(a)と同
様に1番左上端の正方形の左下の直角部分の座標を基準として2次元配列
の繰り返しを応用して一辺80の正方形、隙間が80となるように3*3の正
方形の並びを作成した。そのあと隙間に2*2の正方形の並びが出来るよ
うにもう1つ複数の繰り返しの動作を行った。このとき塗りつぶしなの
で線の太さはなし、塗りつつぶしの濃さを0.4としグレーになるように
した。

(c)480*480の紙面を用意し、原点を紙面の真ん中においた。今回の円は
塗りつぶしではないため、drawcircle関数を用いて円を表示した。右端
と左端の円の列を複数の繰り返しを用いて表示した。その後真ん中の上
下部分に円を表示できるように繰り返しを用いた。

(d)480*480の紙面を用意し、原点を紙面の真ん中においた。x軸のみ用
意してPostScriptコマンドである"D rotate"を用いてX軸を22.5°ずつ
回転を繰り返すようにした。この時各線の濃さが異なるようにした。

2-4.考察

例題の図形を動かして気づいたのはsethsbcolorのグラデーションの使
い方である。例題は色相の部分を繰り返しを用いて変化させていたため、
eps_cmdの中が"1.0 1.0 sethsbcolor"であることが分かった。彩度や明
度を変化させる時は繰り返しを行う前の部分にeps_cmdを行えば良いと
いうことが分かった。例えば彩度を変化させたいときは、

eps_cmd("1.0");eps_num(i*0.1);eps_cmd("1.0 sethsbcolor");

とやれば彩度が変化する事が分かった。

色変化に関して(d)では回転する線の色を全て変わるようなプログラム
にしたので分かったことだが、私は反時計回りに22.5°ずつ回ると思っ
ていたが、実際はそうはならなかった。線と線の間の角度は22.5°には
なっているが本来y軸の部分はi=4の時に表示されるはずなのにy軸の部
分はi=8の時に表示されていることが分かった。このことから私たちが
普段から用いている反時計回りにD°回っているとはまた違うようにで
きていることが分かった。

また(c)では本来ならば円の中心が正方形を描くように動くプログラム
を作ろうとしたが2次元配列を用いると中まで円で埋まってしまったた
め、実装できなかった。drawrectを応用して行うのだろうと考えたが中
心を正方形のように動かすと余分な部分まで表示されるあるいは必要な
部分が消えてしまうため上手くいかなかった。

今回PostScriptで図を作成し、慣れないこともあったが図を描いてて動
かし方はC言語とはかけ離れていないことが分かった。今後は透明度な
ども考慮しながら図を作れるようにしたい。

3.アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
図は慣れないので(プロ通の時もかなり曖昧にやってたこともあり)どこ
がどこに対応しているのか考えるのが大変だった。

Q2.epsライブラリを使ってみてどのように感じましたか。
C言語に慣れていればそこまであくせくせずに図が書けるようになれると思った。
 
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
来週のテストケースは複雑そうなので効率よく頑張っていきたい。

f1910579 1b:○ Sat May 16 12:32:50 2020


レポート1b
学籍番号:1910579
氏名 @@@藤本健太朗
ペアの学籍番号(または「個人作業」:個人作業
提出日時:5月16日


<1つ目の課題>
[課題の再掲]
a. 直角三角形の直角をはさむ 2 辺の長さを入力し、斜辺の長さを出力する。

[ソース]
// 斜辺の長さ
#include<stdio.h>
#include<math.h>
double tyokkaku(double w,double h){
    double t = w*w+h*h;
    double s = sqrt(t);
    return s;
}

int main(void){
    double w,h;
    printf("w> ");scanf("%lf",&w);
    printf("h> ");scanf("%lf",&h);
    double s = tyokkaku(w,h);
    printf("直角の斜辺:%g",s);
    return 0;
}

[説明]
まづdouble tyokkaku(double w,double h)で直角三角形の直角を形成す
る二辺の変数w,hとした関数を作った。そして変数tと置き、そこにwとh
を二乗して足したものを代入した。次に変数sと置き、sqrt(t)でtの平
方根を代入して、sを出力させた。

int main(void)の関数で変数w,hをエディタで入力できるようにして、
その値を先ほど作った関数で計算させて、printf("直角の斜辺:%g",s)
で直角の斜辺:のあとに計算された値を含めたものを出力させた。

[考察]
今回は二乗であったのでべき乗の関数powを使わなかったが、これから
もっと多くのべき乗が出てきたら、プラグラムとして見やすくなり、処
理も簡単になるのではないかと考えた。また処理を簡単にすると言う点
では、今回は段階的に二乗して、平方根としたが、sqrt(w*w+h*h)とし
てもみてもいいのではないかと考えられる。

<2つ目の課題>
[課題の再掲]
c. 整数 n (n > 1) を入力し、n の素因数分解を表示する。たとえば
60 を入力したら「2 2 3 5」を出力する (出力の順番や形式は任意)。

[ソース]
// 素因数分解
#include <stdio.h>
#include <math.h>       

double bunnkai(int a) {
    int i;
    int x = a;
    for(i=2;i<=a;++i){
        while(x%i==0){
          printf("%3d", i);
          x=x/i;
        }
    }
    return 0;
}

int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  bunnkai(n);
  return 0;
}

[説明]
最初にdouble bunnkai(int a)で素因数分解したい整数nを変数aと置い
た関数を作って、次に反復させるための変数i、それぞれの段階で素因
数分解してでた商を保存しておくための変数xを指定してxにはaを代入
した。次にfor(i=2;i<=a;++i)でiを2からaまで反復させるコマンドを
作り、その中の式でxがiで割って余りが出ない限り、iを出力させ、xに
x/iの答えを代入させる式を作り、素因数分解できるプログラムを作成
した。

続いてint main(void)で関数を作りprintf("n> "); scanf("%d", &n)で
nをエディタでnの値を入力できるようにして、 bunnkai(n)で出力させ
た。

[考察]
今回一つ目の関数では出力の関数で素因数を出力させる時
printf("%3d", i)を用いたため、もし3桁以上の素因数が現れたときに
前の文字とくっついてしまったり、完全に出力できないことに気づいた。
したがって%3dではなくて%d\nとした方がどの桁でも対応でき、改行で
見やすくなるのではないかと考えられた。

<アンケート>
Q1. プログラムを作るという課題はどれくらい大変でしたか?
久々にプログラムを作ったために結構エラーが出てしまい大変だと感じ
た。しかし演習を重ねて慣れてきたと思う。
 
Q2. epsライブラリを使ってみてどのように感じましたか。
まだ触れられてないので、開いた時間を見て勉強をしたいと思う。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
基礎プロで勉強したことが結構抜けていたのでできるだけ早く思い出せ
るように復讐したい。

f1910583 1b:○ Mon May 18 14:32:46 2020


レポート1B


・課題1c「整数 n (n > 1) を入力し、n の素因数分解を表示する。」
-ソースコード-
#include <stdio.h>
//素因数分解の結果を出力する関数
void printprime(int n){
    int i;
    int flag = 0;
    for(i = 2; i <= n; i++){
        while (n % i == 0){
            if (flag == 0) {
            //初めてwhileループに入ったときだけ実行される
                printf("%d", i);
                flag = 1;
            } else {
                printf(" * %d", i);
            }
            n /= i;
        }
    }
}

int main(void){
    int n;
    printf("n > "); scanf("%d", &n);
    printprime(n);
    return 0;
}

-実行例-
n > 20
2 * 2 * 5
n > 17
17

-解説-
printprime(n)はnを素因数分解した結果を出力する関数である。
この関数のwhile文内では、nがiで割り切れた回数だけ「i * i * i ...」と出力する。
nがiで割り切れなくなったら、iに1を足し、同様の出力判定をiがnになるまで続ける。
また、20 = * 2 * 2 * 5のように最初に*が付いてしまうのを防止する
ために、flagという変数を用いた。
flagが0のとき(最初にiを出力するとき)だけ、*をつけずに数字のみ出力している。

-考察-
ここではint flag = 0と整数でフラグを立てたり下ろしたりしているが、
stdbool.hをincludeすれば上記のプログラムを以下のように書き換えら
れる。

bool first_roop = true;
    for(int i = 2; i <= n; i++){
        while (n % i == 0){
            if (first_roop) {
            //初めてwhileループに入ったときだけ実行される
                printf("%d", i);
                first_roop = false;
            }
            (以下省略)

このようにすれば、0と1のどっちがtrueかfalseか分からなくなるといっ
た混乱がなくなり、見やすいコードになる。

・課題2a.(2)「チェック柄の図形を作る。」
-ソースコード-
eps.hとeps.cはテキストと同様のものを用いた。

//チェック柄の図形を作る。
#include <stdio.h>
#include "eps.h"
int main(void) {
    int dy, i, j;
    eps_open("out.ps", 480, 480);
    eps_cmd("240 240 translate");
    eps_cmd("0 setgray");
    dy = 192;
    //黒色正方形を3つ書く1行目, 3行目, 5行目の部分
    for(i = 0; i <= 2; i++){
        eps_fillrect(-240, 144-dy*i, 96, 96);
        eps_fillrect(-48, 144-dy*i, 96, 96);
        eps_fillrect(144, 144-dy*i, 96, 96);
    }
    //正方形を2つ書く2行目, 4行目の部分
    for(j = 0; j <= 1; j++){
        eps_fillrect(-144, 48-dy*j, 96, 96);
        eps_fillrect(48, 48-dy*j, 96, 96);
    }
    eps_close();
    return 0;
}

-解説- 
480×480サイズの画面上にチェック柄を描くプログラムである。
まず原点を中央(240, 240)にし、0 setgrayで色を黒色を指定する。
次に、黒色の正方形を3つ書く1行目, 3行目, 5行目の部分と、正方形を
2つ書く2行目, 4行目の部分の2種類に大まかに分けた。
for文内では、eps_fillrectで塗りつぶされる正方形の幅と高さは96と
し、カウンタ変数i,jが1増えると正方形を描く位置がy軸下向きに
192(正方形2つ分)だけ移動するようにした。

-考察-
チェック柄のように、同じ図形の繰り返しや幾何学模様の図形はプログ
ラミングで特に書きやすいことがわかった。
ところで、今回作ったプログラムは正方形を3つ描くならeps_fillrect
を3回使う、2つ描くならeps_fillrectを2回使うようにした。
さらに工夫すると、下記のプログラムのようにeps_fillrectを使う回数
を最小限にでき、冗長な部分を排除できる。

for(i=0;i<5;i++){
    for(j=0;j<5;j++){
     //黒色の96×96正方形を塗る塗らないを交互に繰り返す
      if((i+j)%2==0){eps_fillrect(-240+96*i, -240+96*j, 96, 96);}
    }
}

-アンケート-
Q1. プログラムを作るという課題はどれくらい大変でしたか?
思い通りの結果が出力されず手こずっていたら、プログラム作成に3時
間ほどかかってしまった。
Q2. eps ライブラリを使ってみてどのように感じましたか。
    最初は難しく感じたが、使ってみると便利で楽に作業ができるようになった。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
    久しぶりにプログラムを書いたので大変だった。

h1810533 1b:○ Tue May 19 00:28:00 2020


「個人作業」
5月17日

1、整数nを入力し、素因数分解を出力するプログラムの作成

ソースコード
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
int x = 0, j = 0;
char a[128];
bool isprime(int n)
{
  int limit = (int)sqrt(n);
  for (int i = 2; i <= limit; ++i)
  {
   if (n % i == 0)
    {
      x = i;
       return false;
    }
     }
	return true;
     }

void prime2(int n)
{
    if (isprime(n))
        {
	  a[j] = n;
          return;
        }
      else  {
      a[j] = x; int m = n / x;  j++;  prime2(m);
	    }
	     }
	      int main(void)
	     {
	       int n;
	        printf("n> ");
	        scanf("%d", &n);
	        prime2(n);
	        for (int i = 0; a[i] != 0; i++)
		  {printf(" %d", a[i]); }
		  printf("\n");
	 return 0;
	 }

説明

例題のprime.cから素数判定の関数isprimeを引用し、判定対象から見つ
かった約数を変数xに入れる変更をした。このプログラムでは、まず変
数x=j=0と空の数列aを置く。次にnが入力されたとき、関数prime2でnを
処理する。このとき、nが素数か1であればa[j]にnを入れて処理を終了
し、nが素数でなければnを割り切れた数xをa[j]に入れjに1加算し、関
数prime2でn/xを処理する。prime2の処理が終了した後数列aの要素を一
つずつ出力する。

考察
プログラムを実行した結果、以下のように出力は昇順になっている。
n> 1
 1
 n> 12
 2 2 3
 n> 123
 3 41
これはisprimeによる素数判定が2から順に昇順で行われており、判定対
象の約数も昇順で発見されるためと考えられる。

 2、APIを用いてPostScriptにより関数のグラフを描画するプログラムの作成

ソースコード
 関数 200*cos(x/100) のグラフを-200<=x<=200の範囲で作成するプログラム
  #include <stdio.h>
  #include <math.h>
  #include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  eps_drawline(-200, 0, 200, 0);
  eps_drawline(0, 200, 0, -200);
  for(int n = 0; n < 100; n++) {eps_drawline(-200.0 + 4.0*n, 200.0*cos(4.0*n/100.0 - 2.0),-200.0 + 4.0*(n+1), 200.0*cos(4.0*n/100.0 - 2.0) - 8.0 * sin(4.0*n/100.0 - 2.0));}
  eps_close();
  return 0;
	       }

関数 (x^3)/64000 - (x^2)/1600 + x/40 - 1 のグラフを-200<=x<=200の範囲で作成するプログラム
#include <stdio.h>
#include <math.h>
#include "eps.h"

int main(void)
{
    eps_open("out.ps", 480, 480);
    eps_cmd("240 240 translate");
    eps_drawline(-200, 0, 200, 0);
    eps_drawline(0, 200, 0, -200);
    for (int n = 0; n < 100; n++){
    eps_drawline( -200.0+4.0*n, pow(-200+4*n,3)/64000.0-pow(-200+4*n,2)/1600.0+(double)(-200+4*n)/40.0-1.0, -200.0+4.0*(n+1), pow(-200+4*n,3)/64000.0-pow(-200+4*n,2)/1600.0+(double)(-200+4*n)/40.0-1.0+3.0*pow(-200+4*n,2)/16000.0-2.0*(double)(-200+4*n)/400.0+1.0/10.0);}
    eps_close();
 return 0;
 }

関数 2^(x/40) + (x/40)^2 のグラフを-200<=x<=200の範囲で作成するプログラム
#include <stdio.h>
#include <math.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  eps_drawline(-200, 0, 200, 0);
  eps_drawline(0, 200, 0, -200);
  for(int n = 0; n < 100; n++) {
  eps_drawline( -200.0 + 4.0*n, pow(2,((-200 + 4*n)/40.0)) + pow((-200 + 4*n)/40.0,2), -200.0 + 4.0*(n+1), pow(2,((-200 + 4*n)/40.0)) + pow((-200 + 4*n)/40.0,2) + log(2)*pow(2,(-200 + 4*n)/40.0)/10.0 + (-200 + 4*n)/200.0);
		               }
  eps_close();
 return 0;
  }

説明
まず座標軸を書き、
x = -200 + 4*n と置き、n = 0 から n = 99 まで
関数のxでの接線をxからx+4まで引く。
を実行する。
このようにして描画した直線の集合を関数のグラフに近似させる。

考察

これらのプログラムから数字の小数点以下を取り除くと近似の精度が落
ち描画されたグラフはなめらかな曲線ではなくなる。これは数字から小
数点を取り除くとその数字がint型になり、分母がint型の分数やint型
の引数が渡された関数もint型として計算され、計算結果の小数点以下
が切り捨てになったためと考えられる。

アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
一つの課題を終わらせるのに1日かかるぐらい大変。
Q2.epsライブラリを使ってみてどのように感じましたか。
直線でなめらかな曲線を近似できるのは気持ちが良かった。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
久しぶりにここまで多くキーを打ったので疲れた。

h1810542 1b:○ Mon May 18 22:18:29 2020


レポート3B
選択した課題1:  
演習T3a: 正の整数 n を入力し、0 から n-1 までの整数を出力。 
方針1:for文を用いて考えていく
T3a
#include<stdio.h>
int main(void){
    int n,i;
    scanf("%d",&n);
    for(i = 0;i <= n-1; ++i){
        printf("%d\n",i);
    }
    return 0;
}
実行例1:
./T3a.exe
5(入力)
0
1
2
3
4
解説1:for文よりn回繰り返し表示するようにした
考察1:for文を用いることで簡単に表示できることが分かった

選択した課題2:
演習T3b:正の整数 n を入力し、1, 3, 5, ... とn 未満の整数を出力。 
方針2:for文とif文を組み合わせる
T3b
#include<stdio.h>
int main(void){
    int n,i;
    scanf("%d",&n);
    for(i = 1;i < n; ++i){
        if(i % 2 == 1){
            printf("%d\n",i);
        }    
            }
            return 0;
    }
実行例2:
./T3b.exe
9(入力)
1
3
5
7
解説2:for文によりn回繰り返しif文により偶数をはじいた
考察2:for文if文を組み合わせることで簡単に表示された     

選択した課題3:
演習T3c: 正の整数 n を入力し、0, 2, 4, ... とn 未満の偶数を出力。
方針3:for文とif文を組み合わせる
T3c    
#include<stdio.h>
int main(void){
    int n,i;
    scanf("%d",&n);
    for(i = 0;i < n; ++i){
        if(i % 2 == 0){
            printf("%d\n",i);
        }    
            }
            return 0;
    }
実行例3:
./T3c.exe
7(入力)
0
2
4
6
解説3:T2bと同様に考えて奇数をはじいた
考察3:こちらもfor文とif文を組み合わせることで簡単に表示された    

選択した課題4:
演習T3d: 正の整数 n を入力し、0, 3, 6, ... とn 以下の 3 の倍数を出力。 
方針4:for文とif文を組み合わせる
T3d 
#include<stdio.h>
int main(void){
    int n,i;
    scanf("%d",&n);
    for(i = 0;i <= n; ++i){
        if(i % 3 == 0){
            printf("%d\n",i);
        }    
            }
            return 0;
    }
実行例4:
./T3d.exe
13(入力)
0
3
6
9
12
解説4:T3bと同様に考えて3の倍数をはじいた
考察4:これもfor文とif文を組み合わせることで簡単に表示された    

選択した課題5:
演習T3e:正の整数 n を入力し、n 〜 0 の範囲の整数を大きい順に出力。 
方針5:for文の繰り返し--に変更した
T3e
#include<stdio.h>
  int main(void){
  int n,i;
  scanf("%d",&n);
  for(i = n; i >= 0; --i)
    printf("%d\n",i);
    return 0;    
  }
実行例5:
./T3e.exe
7(入力)
7
6
5
4
3
2
1
0
解説5:T3aの表示の方法とは逆にした
考察5:T3aの++を--に変更するだけで表示できる   

選択した課題6:
演習T3f:正の整数 n を入力し、-n 〜 n の範囲の整数を小さい順に出力。 
方針6:for文を用いて出力
T3f
#include<stdio.h>
  int main(void){
  int n,i;
  scanf("%d",&n);
  for(i = -n; i <= n; ++i)
    printf("%d\n",i);
    return 0;    
  } 
実行例6:
./T3f.exe
5(入力)
-5
-4
-3
-2
-1
0
1
2
3
4
5
解説6:T3aの範囲を-nからに拡大した
考察6:iの最初の指定を変更するだけで表示できた

選択した課題7:
演習T3g: 正の整数 n を入力し、n 〜 -n の範囲の整数を大きい順に出力。
方針7:for文を用いて出力
 T3g
 #include<stdio.h>
  int main(void){
  int n,i;
  scanf("%d",&n);
  for(i = n; i >= -n; --i)
    printf("%d\n",i);
    return 0;    
  } 
実行例7:
./T3g.exe
5(入力)
5
4
3
2
1
0
-1
-2
-3
-4
-5
解説7:nの表示範囲-nを拡大するように比較演算子を設定した
考察7:整数の長い範囲を指定しても容易に表示できることがわかった。

選択した課題8:
演習T4a: 正の実数 x を入力し、x^1〜x^10 を出力。
方針8:double型yとfor文を用いる
T4a
#include<stdio.h>

int main(void){
    double x,y=1;
    int i;
    scanf("%lf",&x);
    for(i =1;i<=10;++i){
        printf("%f\n",y=y*x);
        }
       
    return 0;
}
実行例8:
./T4a.exe
2(入力)
2.000000
4.000000
8.000000
16.000000
32.000000
64.000000
128.000000
256.000000
512.000000
1024.000000
解説8:yにxの値をかけていくことで階乗を表現した
考察8:yを用いることで表現できることが分かった

選択した課題9:
演習T4b: 正の実数 x を入力し、x^0〜x^9 を出力。 
方針9:while文とdouble型yとif文を用いた
T4b
#include<stdio.h>

int main(void){
    double x,y=1;
    int i;
    scanf("%lf",&x);
    while(i <= 9){
        if (i == 0){
            printf("%d\n",1);
        }
        else {
            printf("%f\n",y=y*x);
        }
        ++i;
    }
       
    return 0;
}
実行例9:
./T4b.exe
3(入力)
1
3.000000
9.000000
27.000000
81.000000
243.000000
729.000000
2187.000000
6561.000000
19683.000000
解説9:if文でi=0のときのみ1を代入してwhile文で繰り返しをするようにした
考察9:x^0のみ整数になってしまうのが分かった

選択した課題10:
演習T4c:正の実数 x と正の整数 n を入力し、x^0〜x^n を出力。 
方針10:while文とdouble型yとif文scanfのnを用いた
T4c
#include<stdio.h>

int main(void){
    double x,y=1;
    int i,n;
    scanf("%lf",&x);
    scanf("%d",&n);
    while(i <= n){
        if (i == 0){
            printf("%d\n",1);
        }
        else {
            printf("%f\n",y=y*x);
        }
        ++i;
    }
       
    return 0;
}
実行例10:
./T4c.exe
3(入力)
5(入力)
1
3.000000
9.000000
27.000000
81.000000
243.000000
解説10:xとnを入力してi=0のときのみ1を代入するwhile文で繰り返す
考察10:こちらもx^0のみ整数となってしまう

選択した課題11:
演習T4d: 正の実数 x と正の整数 n を入力し、x^?1〜x^?n を出力。 
方針11:while文とdouble型yとscanfのnを用いた
T4d
#include<stdio.h>

int main(void){
    double x,y=1;
    int i,n;
    scanf("%lf",&x);
    scanf("%d",&n);
    while(i <= n){
            printf("%1.9f\n",y=y/x);
        ++i;
    }
       
    return 0;
}
実行例11:
./T4d.exe
2(入力)
4(入力)
0.500000000
0.250000000
0.125000000
0.062500000
0.031250000
解説11::xとnを入力してwhile文で繰り返す
考察11:while文のほうがべき乗にむいていることが分かった

選択した課題12:
演習T4e: 正の実数 x と正の整数 n を入力し、x/1〜x/n を出力。 
方針12:for文とscanfのnを用いた
T4e
#include<stdio.h>

int main(void){
    double x;
    int i,n;
    scanf("%lf",&x);
    scanf("%d",&n);
    for(i=1;i<=n;++i){
        printf("%1.8f\n",x/i);
    }
       
    return 0;
}
実行例12:
./T4e.exe
2(入力)
5(入力)
2.00000000
1.00000000
0.66666667
0.50000000
0.40000000
解説12:forでの繰り返しによるx/iを表現
考察12:for文でシンプルに表現できる

選択した課題13:
演習T4f:正の実数 x と正の整数 n を入力し、x, 2x, ..., nx を出力。 
方針13:for文とscanfのnを用いた
T4f
#include<stdio.h>

int main(void){
    double x;
    int i,n;
    scanf("%lf",&x);
    scanf("%d",&n);
    for(i=1;i<=n;++i){
        printf("%8.2f\n",x*i);
    }
       
    return 0;
}
実行例13:
./T4f.exe
6(入力)
4(入力)
     6.00
    12.00
    18.00
    24.00
解説13:for文での繰り返しによる表現、実数の指定範囲も小数点以上におおくした
考察13:こちらもfor文でシンプルに表現できる

選択した課題14:
演習T4g:正の実数 x と正の整数 n を入力し、x, x + 1, ..., x + n を出力。 
方針14:if文とscanfのnを用いた
T4g
#include<stdio.h>

int main(void){
    double x;
    int i,n;
    scanf("%lf",&x);
    scanf("%d",&n);
    for(i=0;i<=n;++i){
        printf("%8.2f\n",x+i);
    }
       
    return 0;
}
実行例14:
./T4g.exe
5(入力)
4(入力)
    5.00
    6.00
    7.00
    8.00
    9.00
解説14:こちらもfor文での繰り返しによる表現、実数の指定範囲も小数点以上におおくした
考察14:これもfor文でシンプルに表現できる

選択した課題15:
演習T4h: 正の実数 x と正の整数 n を入力し、x, 1?x, ..., n?x を出力。 
方針15:for文とif文scanfでnを表現
T4h
#include<stdio.h>

int main(void){
    double x;
    int i,n;
    scanf("%lf",&x);
    scanf("%d",&n);
    for(i=0;i <= n;++i){
        if (i == 0){
            printf("%f\n",x);
        }
        else {
            printf("%f\n",i-x);
        }
    }
       
    return 0;
}
実行例15:
./T4h.exe
5(入力)
4(入力)
5.000000
-4.000000
-3.000000
-2.000000
-1.000000
解説15:for文での繰り返しによる表現、if文でi==0のときのみxそのまま代入
考察15:for文if文による組み合わせで複雑そうなものも表現できることが分かった

選択した課題16:
演習T5a:正の整数 n を入力し、1〜n を出力するが 5 の倍数のときは代わりに buzz と出力。 
方針16:for文if文を用いた
T5a
#include <stdio.h> 

    int main(void) { 
        int n;
        scanf("%d", &n); 
            for(int i = 1; i <= n; ++i) { 
                if(i % 5 == 0) { 
                    printf(" buzz\n"); 
                } else { 
                    printf(" %d\n", i);
                     } 
                }return 0; 
            }
実行例16:
./T5a.exe
8(入力)
 1
 2
 3
 4
 buzz
 6
 7
 8
解説16:for文での繰り返しによる表現、if文で5で割り切れる場合のみとbuzz出力するようにした
考察16:for文if文による組み合わせで特定の倍数をはじけることが分かった

選択した課題17:
演習T5b: 正の整数 n を入力し、1〜n を出力するが 3 の倍数のときは ?zz、5 の倍数のときは buzz、 3 と5 の公倍数では ?zzbuzz と代わりに出力。 
方針17:for文if文elseif文で表現
T5b
#include <stdio.h> 

    int main(void) { 
        int n;
        scanf("%d", &n); 
            for(int i = 1; i <= n; ++i) { 
                if(i % 3 == 0 && i%5 !=0) { 
                    printf(" fizz\n"); 
                } else if (i%5 ==0 && i%3 !=0){ 
                    printf(" buzz\n");
                     } else if(i%3==0 && i%5 ==0){
                         printf("fizzbuzz\n");
                     } else if(i%3!=0 ||i%5!=0){
                        printf("%d\n",i);
                     }
                }return 0; 
            }
実行例17:
./T5b.exe
15(入力)
1
2
 fizz
4
 buzz
 fizz
7
8
 fizz
 buzz
11
 fizz
13
14
 fizzbuzz
解説17:for文による繰り返しif文else if文で3の倍数5の倍数15の倍数をfizz,buzz,fizzbuzzと表現できるようにした
考察17:for文if文else if文による組み合わせで複雑そうなものも表現できたがかなり不格好になってしまった

選択した課題18:
演習T5c:正の整数 n を入力し、1〜n のうち 2 の倍数でも 3 の倍数でもないものだけを出力。 
方針18:for文if文による表現
T5c
#include <stdio.h> 

    int main(void) { 
        int n;
        scanf("%d", &n); 
            for(int i = 1; i <= n; ++i) { 
                if(i % 2 != 0 && i%3 !=0) {
                    printf("%d\n",i);
                } 
                    
                }return 0; 
            }
実行例18:
./T5c.exe
19(入力)
1
5
7
11
13
17
19
解説18:for文による繰り返しif文での2,3の倍数をはじく
考察18:こちらもfor文if文による組み合わせで複雑そうなものも表現できることが分かった

選択した課題19:
演習T5d: 正の整数 n を入力し、1〜n を出力するが偶数はマイナス符号をつけて出力。 
方針19:for文if文による表現
T5d
#include <stdio.h> 

    int main(void) { 
        int n;
        scanf("%d", &n); 
            for(int i = 1; i <= n; ++i) { 
                if(i % 2 ==0) {
                    printf("%d\n",-i);
                } else {
                    printf("%d\n",i);
                }
                    
                }return 0; 
            }
実行例19:
,/T5d.exe
7(入力)
1
-2
3
-4
5
-6
7
解説19:for文による繰り返しif文による組み合わせで偶数のみ-で表現した
考察19:これもfor文if文による組み合わせで複雑そうなものも表現できることが分かった

選択した課題20:
演習T5e: 正の整数 n を入力し、1〜n を出力するが奇数は同じものを 2 個出力。 
方針20:for文とif文による表現
T5e
#include <stdio.h> 

    int main(void) { 
        int n;
        scanf("%d", &n); 
            for(int i = 1; i <= n; ++i) { 
                if(i % 2 !=0) {
                    printf("%d  %d\n",i,i);
                } else {
                    printf("%d\n",i);
                }
                    
                }return 0; 
            }
実行例20:
./T5d.exe
7(入力)
1  1
2
3  3
4
5  5
6
7  7
解説20:for文による繰り返しif文による組み合わせprintf内で奇数のみ2つ表現した
考察20:for文if文による組み合せで大体の表現ができることが分かった

アンケート:
Q1. プログラムを作るという課題はどれくらい大変でしたか? 
かなり時間がかかりmした
Q2. eps ライブラリを使ってみてどのように感じましたか。
自分でも扱えるようになります 
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
多く作るのも考え物ということが分かった

h1910525 1b:○ Sat May 16 22:30:39 2020


学籍番号 1910525
ペア 1910616
提出日 5/16(土)

1つ目の課題再掲:演習2a、epsライブラリを用いてテキスト掲載の4つの図を作成せよ。

(1)

ソースコード


#include <stdio.h>
#include "eps.h"

int main(void) {
    eps_open("out.ps", 480, 480);
    //eps_cmd("240 240 translate");
    const int w = 90;
    const int h = 90;
    const double dist = 30;

    int i;

    eps_cmd("255 0 0 setrgbcolor");
    eps_cmd("4 setlinewidth");

    for(i = 0; i < 16; i++){
      eps_num(i);
      int x = (i % 4) * (w + dist) + 15 ; int y = (i / 4) * (h + dist) + 15;
      eps_drawrect(x, y, w, h);
    }

    eps_close();
    return 0;
}


説明:正方形が16個、等間隔に敷き詰められたスクエアの絵。epsライブ
ラリを用いて描画した。今回は原点を左下のままに設定し、左下から敷
き詰めていく方法を取った。画面いっぱいに敷き詰められるようにあら
かじめ正方形の大きさ及び正方形同士の間隔を決めておき、for文内で
位置x,yを変数iを用いて等間隔に配置した。x座標には剰余を、y座標に
はint型の除算を用いた。


考察:今回の絵は画像の中心点に対して点対称であるから、原点を中心
に移動しても同様の絵が描けると考えられる。具体的な方法を考えると、
中心から右上の4つの正方形を先に描き、それを他3象限に転写するとい
う方法が一つ考えられる。この時、転写はx座標及びy座標の符号をそれ
ぞれ変更することで可能となると考えられる。しかし、その条件を書く
よりは今回の敷き詰め方式のほうが行数が少なくて済むと考えられる。

(2)

ソースコード


#include <stdio.h>
#include "eps.h"

int main(void) {
    eps_open("out.ps", 480, 480);
    //eps_cmd("240 240 translate");
    const int w = 90;
    const int h = 90;
    const double dist = 0;

    int i;

    eps_cmd("255 0 0 setrgbcolor");
    eps_cmd("4 setlinewidth");

    for(i = 0; i < 25; i++){
      if(i%2==0){
        int x = (i % 5) * (w + dist) + 15 ; int y = (i / 5) * (h + dist) + 15;
      eps_fillrect(x, y, w, h);
      }
    }

    eps_close();
    return 0;
}

説明:正方形のタイルが25個、1つおきに敷き詰められた絵。今回は原点
を左下のままにして、左下から敷き詰める方法を取った。画面いっぱい
に敷き詰められるようにあらかじめ正方形の大きさ及び正方形同士の間
隔を決めておき、for文内で変数iが偶数のときのみ正方形が描画される
ようにif条件文を使用した。

考察:今回の絵は画像の中心点に対して点対称であるから、原点を中心
に移動しても同様の絵が描けると考えられる。具体的な方法を考えると、
中心から右上までの5つの正方形タイル群を先に描き、それを他3象限に
転写するという方法が一つ考えられる。この時、転写はx座標及びy座標
の符号をそれぞれ変更することで可能となると考えられる。この際、座
標軸を通るタイルは多重描画になるが、結果としては正しい絵が得られ
ると考えられる。しかし、その条件を書くよりは今回の敷き詰め方式の
ほうが行数が少なくて済むと考えられる。


(3)

ソースコード


#include <stdio.h>
#include "eps.h"

int main(void) {
    eps_open("out.ps", 480, 480);
    //eps_cmd("240 240 translate");
    const int r = 48;
    const double dist = 0;

    int i;

    eps_cmd("255 0 0 setrgbcolor");
    eps_cmd("4 setlinewidth");

    for(i = 0; i < 25; i++){
      if((i%5==0)||(i%5==4)||(i/5==0)||(i/5==4)){
        int x = (i % 5) * (r*2 + dist) + r; int y = (i / 5) * (r*2 + dist) + r;
      eps_fillcircle(x, y, r);
      }
    }

    eps_close();
    return 0;
}


説明:画像の4辺に円を互いに接した状態で計16つ並べるプログラム。今
回は2a(2)のプログラムの仕様を流用し、原点を左下のままにして左下
から敷き詰める方法を取った。これは5×5の図形の敷き詰めであったた
めである。for文内で敷き詰めの条件として、剰余、int型の除法をもち
いて4辺のみに図形が描画されるif条件文を用いた。


考察:今回の絵は画像の中心点に対して点対称であるから、原点を中心
に移動しても同様の絵が描けると考えられる。具体的な方法を考えると、
右上方向の計5つの円群を先に描き、それを他3象限に転写するという方
法が一つ考えられる。この時、転写はx座標及びy座標の符号をそれぞれ
変更することで可能となると考えられる。この際、座標軸を通る円は多
重描画になるが、結果としては正しい絵が得られると考えられる。しか
し、その条件を書くよりは今回の敷き詰め方式のほうが行数が少なくて
済むと考えられる。


(4)

ソースコード


#include <stdio.h>
#include "eps.h"
#include <math.h>

int main(void) {
    eps_open("out.ps", 480, 480);
    eps_cmd("240 240 translate");

    int x, y;
    const int a = 240;

    for(int i = 0; i < 16; ++i){
      x = a * cos(2*M_PI * i / 16);
      y = a * sin(2*M_PI * i / 16);

      eps_drawline(0,0,x,y);
    }

    eps_close();
    return 0;
}


説明:画像の中心点から等間隔の放射状に16本伸びる線を描くプログラ
ム。今回は原点を画像の中心に移動し、線の終点を極座標を用いて回転
させる方法を取った。eps_drawlineの始点を原点とし、終点の座標を極
座標のものとした。for文内で変数iを用いて1/16回転ずつ角度を変えて
いった。

考察:この問題は前問のように敷き詰め方式では難しい絵となっている。
よって、原点を画像の中心に持ってくるという方法は必然的であると考
えられる。他の方法があるとすれば、eps_drawlineの始点を原点ではな
く、終点の点対称、つまり逆側の線の先端にとり、計8回のforループで
描くという方法である。こちらのほうが、ループの回数的には半分で済
むので処理は軽くなると考えられる。


2つ目の課題再掲:演習2b、数学関数のグラフを描画せよ。

ソースコード


// testeps.c --- demonstration of eps library.
#include <stdio.h>
#include "eps.h"
#include <math.h>

int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  const int rmd = 60;
  const int a = 100;

  for(double i = -16; i <= 16; i+=0.1) {
    eps_num(fabs(i*0.1)); eps_cmd("1.0 1.0 sethsbcolor");
    int x1 = 20 * i;
    int x2 = 20 * (i+0.1);
    int y1 = a * sin(M_PI * x1 / rmd);
    int y2 = a * sin(M_PI * x2 / rmd);
    eps_drawline(x1, y1, x2, y2);
  }

  eps_close();
  return 0;
}


説明:sin関数をグラデーション付きで描画するプログラム。原点を画像
の中心に持ってきている。波長の変数rmdと、振幅の変数aを用いて関数
を操作可能としている。for文では数学関数にそって近似直線を繰り返
し引いていくが、その際、変数iの変量がそのままグラフの細かさにな
る。今回はiの増分を0.1とし、そのままでは定義域が狭すぎてしまうの
でxの値に重み付けをしてある。yの値にa、x、rmdを用いたsin関数の式
を定義している。グラデーションはhsbの色相の値を変更しているが、
i<0の時もグラデーションを表示するために絶対値を用いている。その
ためグラデーションの模様は線対象となっている。


考察:今回sin関数を作成する際、初めはint y1 = a * sin(x1)のように
してみた。しかしながら思うようなきれいな描画には至らなかった。こ
れはx1の値が、sin関数の係数としては不適切な間隔で増加していたこ
とが原因として考えられた。そこで定数rmdを導入し、係数の表現を
M_PI * x / rmdとして調整をかけた。すると関数がきれいに描画された。
この表現ではM_PIがfloat型であるため、係数内ではある程度正確な評
価がされ、sin関数出力後の結果がint型に直される構造となっているた
め、綺麗に描画されたと考えられる。

アンケート

Q1.プログラムを作るという課題はどれくらい大変でしたか?
1年次の科目である程度の基礎は身につけたので、春休みのリハビリ程
度といった感覚だった。

Q2.epsライブラリを使ってみてどのように感じましたか?
やはりプログラム群を予めパッケージ化して用いるのはとても使い勝手
がよかった。ただし、地震で作ったものではないため用法を覚えるまで
に少し時間がかかることは欠点かなと思った。

Q3.感想をどうぞ。
ペアプログラミングのおかげで互いに補完しあうことがかなり上手くいっ
たと今回感じた。自分1人ではもっと思いつくまで時間がかかるだろう
なという点が多々ノータイムで進んだことは感動を覚えた。今後も続け
ていきたい。

h1910527 1b:○ Mon May 18 22:32:43 2020



演習1e
#include <stdio.h>

int fibonacci2(int n){
    if(n==1||n==2){
        return 1;
    }else{
        return fibonacci2(n-1)+fibonacci2(n-2);
    }
}

int fibonacci1(int n){
    int a,b,c,i;
    a = 1; b = 1;
    for(i=0;i<n-2;i++){
        c = a + b; a = b; b = c;
    }
    return c;
}

int main(){
    int n,mode;
    printf("Fibonacci Sequence\n");
    printf(">");scanf("%d",&n);
    printf("For loop or Recursion(1/2)>");scanf("%d",&mode);
    if(mode==1){
        if(n>=1){
            printf("%d\n",fibonacci1(n));
        }else{
            printf("Incorrect input!\n");
        }
    }else if(mode==2){
        if(n>=1){
            printf("%d\n",fibonacci2(n));
        }else{
            printf("Incorrect input!\n");
        }
    }
    return 0;
}

解説
入力された整数nについて、フィボナッチ数列の第n項を出力するプログ
ラムである。n入力後に演算をforループで行うか再帰で行うか選択でき
る。


考察
どちらの方法で計算してもnが小さいうちは正しい結果を導出できた。
しかし、nに大きい数を入力すると結果が負となってしまった。

また、forループを使用した場合と比べ、再帰を使用した場合は演算が著しく遅くなった。
前者の現象はint型変数の範囲に起因すると考えられる。この現象は
unsigned intやlong intといったより範囲の大きい型を利用することで
ある程度解決できると考えた。
後者の現象は計算量に起因すると考えられる。forループを使用した場
合計算量はO(n)であるが、再帰を使用した場合計算量がO(2^n)となり計
算が著しく遅くなる。
そのため、フィボナッチ数列に限らず、コードを書く際は演算速度にも
気を配るべきだと感じた。

演習2d
// eps.c --- eps library implementation
#include <stdio.h>
#include "eps.h"
static FILE *fd = NULL;
static int fontid = 0;

void eps_open(char *fname, int w, int h){
    fd = fopen(fname, "wb");
    fprintf(fd, "%%!PS-Adobe-2.0\n%%%%BoundingBox: 0 0 %d %d\n", w, h);
}
void eps_close(void) {
    fprintf(fd, "showpage\n"); fclose(fd); fd = NULL;
}
void eps_cmd(char *cmd) {
    fprintf(fd, "%s\n", cmd);
}

int eps_newfont(char *font, double size) {
    fprintf(fd, "/%s findfont %.2f scalefont /font%d exch def\n",font, size, ++fontid);
    return fontid;
}
void eps_puts(int id, double x, double y, char *s) {
    fprintf(fd, "font%d setfont %.2f %.2f moveto (%s) show\n",id, x, y, s);
}

void eps_seidoume(double scale,double rad, int times){
    int i;
    for(i=0;i<times;i++){
        fprintf(fd,"0 0 moveto %.2f 0 %.2f %.2f %.2f %.2f curveto stroke\n",
                80*scale,80*scale,300*scale,-400*scale,150*scale);
        fprintf(fd,"0 0 moveto %.2f 0 %.2f %.2f %.2f %.2f curveto stroke\n",
                -80*scale,-80*scale,300*scale,400*scale,150*scale);
        fprintf(fd,"%.2f rotate\n",rad);//cmdだとradを受け取れない,forループの代わりにpsのrepeatでも
    }
}

//enshu2d.c --- bullet
#include <stdio.h>
#include "eps.h"

int main(){
    eps_open("out.ps", 480, 480);
    eps_cmd("240 240 translate"); //translate叩いて原点移動
    eps_seidoume(1,72,5);
    eps_newfont("Helvetica",10);
    eps_puts(1,60,200,"Bonus 100000     History 00/99+");
    eps_close();
    return 0;
}

解説

PostScript内でスプライン曲線を活用し、
https://www.google.com/search?q=%E5%A4%AA%E9%99%BD%E7%A5%9E%E3%81%AE%E8%B4%84&cl
ient=firefox-b-d&sxsrf=ALeKk034PeHNc5Zu3_Fd5y1mGRBMEyiodQ:1589807445608&source=l
nms&tbm=isch&sa=X&ved=2ahUKEwiRz7mgvr3pAhVB-WEKHQO_BhEQ_AUoAnoECAsQBA&biw=1282&b
ih=951#imgrc=9Urq_K0DLeGeVM
に示される弾幕を描画している。ライブラリ内のeps_seidoumeは無理関
数のような曲線を線対称に描画した後、座標軸をrotateで回転させ円形
に描画している。

考察

この画像はある1つの曲線を回転しながら描画されているため、本質的
な部分では演習2a4とあまり違いがない。両課題とも「回転動作をどの
ように実現するか」が肝となるが、今回はfor文でrotate命令を次々置
いていくことで実現した。他に回転動作を実現する方法として、repeat
を記述する、回転行列を用いる、座標を極座標で扱うなどが挙げられる。
これらはAPIとして組み込みにくい、Cファイルが冗長になるなどの弱点
があるが、描画図形によっては有用であると考える。また、一年次のコ
ンピューターリテラシで触れた時は内部ファイルを直接記述していたが、
今回はライブラリによるファイルへの書き出しを活用したため図形の作
成がしやすくなっていると感じた。

アンケート
Q1.一年次の授業でそれなりに意欲をもって取り組めたため、今のとこ
ろ気苦労はない。

Q2.どこにどのようなデザインで描画したいかだけを考えればよく内部
構造を無視できるので、手続き型プログラミングの良さが出ていると感
じた。

Q3.昨年度に引き続きプログラミング頑張りたいです。

h1910528 1b:○ Sun May 17 20:29:19 2020

提出日:5/17
■作成したプログラム1
[課題1b]
#include <stdio.h>
#include <math.h>
double nj(double n){
double a = pow(2.0, n);
return a;
}
int main(void) {
 double n;
 printf("n> "); scanf("%lf", &n);
 if (n >=0){
  int xa = (int)nj(n);
  printf("2**n 6= %d\n", xa);
 }else{
  double xb = nj(n);
  printf("2**n 6= %g\n", xb);
 }
 return 0;
}
[説明]

整数nを与えられて、2のn乗を出力するプログラム。nが0以上の場合は
整数の値を返し、0未満の時には実数で値を返す。入力されるnは前提で
整数なのでdoubleで実数扱いしてもよいと考えた。与えられたnの対し、
0以上かそれ以外かをif文で判定し、0以上なら関数njを整数化して出力、
それ以外の時はそのまま実数として関数njを出力する。関数njは、pow
を用いて2のべき乗を計算する関数となっている。

[考察]

今回は関数njをつかって作ってみたが、これくらいの短いものならmain
の中にいれて簡潔に記すのもアリだなと思った。また今回入力の条件を
nが整数であるときと決めつけて実数型のdoubleを使っているが、nが整
数ではない数だった場合にエラーを出すなどのためにintで定義して
doubleに変換して関数に代入しても良かったかもしれない。

■作成したプログラム2
[課題2a1]
#include <stdio.h>
#include "eps.h"
int main(void) {
 eps_open("out.ps", 480, 480);
 for(int i = 1; i <= 4; ++i) {
   for(int j = 1; j <= 4; ++j) {
  eps_drawrect(120 * i - 60, 120 * j - 60, 90, 90);
   }
 }
 eps_close();
 return 0;
}
[説明]

課題2aの(1)の図をつくるプログラム。includeでeps.hを読み込み、
eps.cと一緒にコンパイルする。例と同じeps_openで480×480の用紙を
つくり、for文を二つ重ねてeps_drawrectで縦横60間隔、一辺90の正方
形を縦横4個ずつの計16個生成し、eps_closeで編集を終了する。

[考察]
for文を使うことで同じ工程を省略して非常に簡潔な文になっている。
また、epsライブラリを使用したおかげで、書きやすく感じた。

■アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
 近くに人がいない状況で分からないことがあるとテキストを何度も確
認したり検索するなどで大変時間がかかると感じた。

Q2. epsライブラリを使ってみてどのように感じましたか。
 別のファイルに基本的な関数をまとめており、ひとつのものにまとめ
なくていいのでとてもコンパクトかつスムーズにまとめられるのが良い
と感じた。
また図形を簡単に値を与えることで出力できるのが管理がしやすくて面白く思った。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
 一度やったことでも話据えていることが多く、演習を重ねてリハビリ
をしていきたい。

h1910529 1b:○ Mon May 18 22:59:39 2020


 プログラミング通論#01
  提出日: 5/18
  
   課題1
  この課題は、C言語で2の累乗を計算したものである。
   プログラム
   //kaijo.c
#include <stdio.h>
#include <math.h>      
#include <stdbool.h>   
float kaijo(int n) {
  double w = 1;
  int i = 1;
  if(n>0){
  while(i <= n) {
    w = w*2;
    i = i+1;
  }
  return w;
  }else{
  n = -n;
  while(i<= n) {
    w = w/2;
    i = i+1;
  }
  return w;
  }
}
float main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  double w = kaijo(n);
  printf("%g\n", w);
  return 0;
}

 説明
  累乗とは2を何回かけるか、および割るかによって得られる値であ
る。すなわち指定された回数分繰り返してかけたり割ったりすればよい。
  まず、指定された値が0以上か否かでif文を用いて場合分けを行った。
そして、0以上の場合は2を回数分かけた。この繰り返しを行うために、
今回はfor文ではなくwhile文を用いた。whileで繰り返しの上限をきめ、
最初に用意した値に2をかけるという動作を行った後、用意したiという
整数を1増やすという操作を行った。
0以下の場合はまず指定された値を符号反転させて正の数に直した。そ
して、先ほどと同じ原理を用いて2を回数分割った。このために最初に
用意した値を実数で指定した。
最後の読み込みにおいては実数で読み込んだ。

 考察
 今回はpow関数を使わずに累乗を実装した。この際重要なのは整数と
実数の使い分けであった。ほとんどの言語において2と2.0は違った形に
なっている。これは数の表し方からしてどうしようもないことである。
今回もこの仕様が作成に手間取る要因となった。そのことより、int型
をdouble型に、double型をint型に変更できる仕様というものは中で高
度な処理が行われているのではないかと考えることができる。

 課題2
 Epsライブラリを用いて所定の図形を作成した。

 プログラム
 // testeps.c --- demonstration of eps library.
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");    
  for(int i = 1; i <= 4; ++i) {
    eps_num(i*0.1); 
    eps_cmd("2 setlinewidth");         
    eps_drawrect(0, i*30, 20, 20);  
    eps_num(i*0.1); 
    eps_cmd("2 setlinewidth");
    eps_drawrect(30, i*30, 20, 20);
    eps_num(i*0.1); 
    eps_cmd("2 setlinewidth");         
    eps_drawrect(60, i*30, 20, 20);
    eps_num(i*0.1); 
    eps_cmd("2 setlinewidth");         
    eps_drawrect(90, i*30, 20, 20);
  }   
  eps_close();                          
  return 0;
}

 説明
 今回作成した図形は正方形を少し並べて4×4に均等に並べるというも
のである。縦と横にシフトさせるコマンドでは斜めにのみ並んでしまう。
そこで今回は同じ正方形をつくるコマンドを4つ並べ、そのコマンドに
横に4つ並べるコマンドを含めた。具体的には、まずiという整数を1か
ら4まで繰り返させた。そこにiに依存した座標をもつ正方形を書くよう
指定した。こうすることで正方形を横に移動させることができた。そし
て、正方形のもう一方の座標を変更した同じ正方形をかくコマンドを用
意することで、正方形を4×4並べることに成功した。

考察
 今回は同じような形をもったプログラムを4つ並べることで画像を作
成したが、これが一つにまとめられれば非常に短くなるだろうしいくつ
でも連続させられるようになる。こうするためにはループのなかにルー
プを含めるという方法が考えられる。また、epsライブラリに配列を使
えるようにしたらループのなかのループというエラーを吐きやすい形を
避けることができ簡単であると考えられる。

Q1. プログラムを作るという課題はどれくらい大変でしたか?
A1,感覚が鈍っていて思ったより苦戦しました
Q2. epsライブラリを使ってみてどのように感じましたか。
A2,円以外の曲線を引きづらいのが難点だと感じました
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
A3,考察を書く勘が鈍っていてつらかったです

h1910531 1b:○ Tue May 19 00:26:42 2020


課題1b
    学籍番号 1910531
    個人作業 
  提出日時 5月19日
 
1 つ目の課題の再掲 課題1c 整数 n (n > 1) を入力し、n の素因数分解を表示する
プログラム
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
  bool isprime(int n) {
  int limit = (int)sqrt(n);
  int i;
    for( i = 2;      i <= limit; ++i) {
      if(n % i == 0) { return isprime(i);
      }

    }
    return true;
}

void prime(int n){

  int t;
  for( t=2;t <=n; ++t){
    while(isprime(t) && n%t==0){

      printf("%3d",t);
      n=n/t;
    }





  }

}

int main(void){
  int n;
  printf("n>");scanf("%d",&n);
  prime(n);
  return 0;
}      
例
n>43
 43
n>12
  2  2  3

説明 
素数判定を用いる。
2からnまでの数の内、素数においてのみ働く。

一ループごとに、2から順に素数tで与えられた整数nを割る。
tで割り切れたならばそのiはnの素因子である。
n=n/tでnから素因子tを除く。
ここで、一ループが終了する。

tは一つずつ増えている
nをnの素因子で割っているため、nは小さくなる。
tがnより大きくなるとループが終了する。

考察
このプログラムを作り始めた時、12を入力しても、2.3しか出力しなかった。

whileの条件にn%t==0がなかったからである。
これにより、12のように等しい素因子を複数個持つものにも対応できた。

このプログラムは、2からnまでの一つずつについて実行している。し
かし、2以外の素数は奇数であるため、3以降は3、5、7のように二
つ飛ばしで実行すればよい。


2 つ目の課題の再掲 課題1d整数 n を入力し、n 以下の素数をすべて出力する 
プログラム
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
  bool isprime(int n) {
  int limit = (int)sqrt(n);
  int i;
    for( i = 2;      i <= limit; ++i) {
      if(n % i == 0) { return false;
      }


    }
    return true;
}

void prime(int n){

  int t;
  for( t=2;t <=n; ++t){
    if(isprime(t)){

      printf("%3d",t);

    }





  }

}

int main(void){
  int n;
  printf("n>");scanf("%d",&n);
  prime(n);
 return 0;
}

例
n>47

 2  3  5  7 11 13 17 19 23 29 31 37 41 43 47
説明
2からnまでの数についてそれぞれ、素数判定を行った。
考察
素数以外で、割り切れるか判定しているため効率が悪い。例えば、7の
素数判定において、2や4で7を割り切れるか確認する。しかし、すで
に4は2の倍数であって、4で割り切れるか確認する必要はない。2の
倍数か確認するだけで十分である、素数判定された数を記憶し、次のルー
プに持ち越す必要がある、


 以下のアンケートの回答。
Q1. プログラムを作るという課題はどれくらい大変でしたか?
 一年前や、春休みの序盤の復習で作成したプログラムを見直し、思い
 出しながらの作業でした。大変です。
Q2. eps ライブラリを使ってみてどのように感じましたか。
一年生の時の勉強不足がたたり、難しいです。 
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ
トレーニング課題を中心にやっていきます。
初回から提出が遅れ、すいませんでした。

h1910532 1b:○ Mon May 18 23:40:33 2020


プログラミング通論 #1 1b

1910532
個人作業
5/18

1c
再掲
整数n(n>1)を入力し、nの素因数分解を表示する。

ソース
#include <stdio.h>
void prime(int n) {
  int i = 2;
  while(i <= n){
    while(n%i == 0){
      printf(" %d", i);
      n = n/i;
    }
    i = i + 1;
  }
  printf("\n");
}
int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  prime(n);
  return 0;
}

実行例
n> 2
 2

n> 120
 2 2 2 3 5

n=2、120の素因数分解が表示された。

説明

iを2から大きくしていき、nをiで割った時に余りが出ない場合のiを、
つまり素因数を表示するようにした。その都度nはiで割り、nの素因数
であるiを除外していった。

考察

始めはn=n/iをn%i==0のwhileループの外で行っていたので、おかしくなっ
てしまった。しっかり順序を考える必要があると分かった。forループ
を使おうとするとエラーが出てしまったのが疑問だった。

1d
再掲
整数nを入力し、n以下の素数を出力するプログラム。

ソース
#include <stdio.h>
void prime(int n) {
  int f = 0;
  int i = 2;
  int j = 2;
  if(n >= 2){
    while(i <= n){
      while(j < i){
	if(i%j == 0){
	  f = f + 1;
	}
	j = j + 1;
      }
      if(f == 0){
	printf(" %d", i);
      } 
      f = 0;
      j = 2;
      i = i + 1;
    }
  }else{
    printf("error");
  }
  printf("\n");
}

int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  prime(n);
  return 0;
}

実行例
n> 100
 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

n> 0
error

n> -3
error

n=100の時は100以下の素数が羅列され、n=0、3の時はerrorと出力された。

説明
まず、一番小さい素数は2なので、nが2以上かどうかで場合分けした。
2未満の場合はerrorと出力されるようにした。
次に、2からnまでiを大きくしていった。
iが素数であればf(フラッグ)は0のままで、素数でなければfは1以上になるようにした。
iより小さいjを大きくしながらiを割って余りがでるかどうかで素数か判定した。
素数であればprintfで順に出力していった。

考察
フラッグを使うことで楽に判定できた。
boolを使おうとしたが、できなかった。boolを使ってもできるのか気になった。

アンケート
A1
ここ最近のなかでは一番大変でした。

A2
あまりしっくりこなかった。

A3
c言語を春休み中に忘れてしまっていたが、だんだん思い出してきたので嬉しかった。

h1910534 1b:○ Thu May 14 19:11:43 2020


提出: 2020年5月12日
ペア: 個人作業

・1つ目の課題
演習1c 整数 n (n > 1)を入力し、nの素因数分解を表示する。

・プログラム
#include <stdio.h>
int check(int n){
  int a;
  for (int i = 2; i <= n; i++) {
    if (n%i==0) {
      a = i;
      break;
    }
  }
  return a;
}

int main() {
  int n, chn;
  printf("n> "); scanf("%d", &n);
  while(n != 1){
    chn = check(n);
    printf("%d ", chn);
    n = n / chn;
  }
  printf("\n");
  return 0;
}


・プログラムの説明
まず引数の最小素因数を返す関数checkを作った。
引数に対しfor文で最小の素数2から順に商のあまりが0になるかを確認
し、あまりが0になった時にbreakでループを抜け、その時の値を返すよ
うにした。forループで引数を割る数には素数でない数も含まれるが、1
より大きい任意の素数でない自然数NはN未満に素数を持つため、素数以
外が返ることはない。そのため、問題はない。

次に、main関数内でint型の変数nにscanfで入力をとる。
そしてwhileループ内でint型変数chnにnの最小素因数を代入し、printf
で出力し、nをchnで割りその商に関して1になるまでwhileループを繰り
返している。これにより素因数分解が小さい因数から順に並ぶように出
力される。最後に改行を出力している。

・考察
最小素因数を返す関数checkを作る際にforループ内で素数でない数が出
力されるかもしれないと考えたが、説明にも記述した通り、素因数の特
徴から問題ないことがわかった。プログラムで動かす処理の周辺知識か
ら、厳密で複雑な記述ではなく単純な処理で十分であることが判断でき
る場合があるので、論理的な思考と同じくらい処理の周辺知識は重要だ
とわかった。

・2つ目の課題
演習1d
整数nを入力し、n以下の素数を出力する

・プログラム
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool pricheck(int n) {
  int lim = (int)sqrt(n);
  for(int i = 2; i <= lim; ++i) {
    if(n % i == 0){
      return false;
    }
  }
  return true;
}

int main() {
  int n;
  printf("n> "); scanf("%d", &n);
  for (int i = 2; i <= n; i++) {
    if (pricheck(i)) {
      printf("%d ", i);
    }
  }
  printf("\n");
  return 0;
}

・プログラムの説明
まず引数が素数か判定する関数pricheckを作った。
int型変数limに引数の平方根を代入し、for文で最小の素数2から平方根
までで割り切れる数があるかどうかで素数かどうか判定している。引数
が素数の時、pricheckはtrueを返し、素数でないときはfalseを返す。

次にmain関数内で、int型変数nにscanfで入力をとり、for文で2からnま
で順にpricheckを使って素数かどうかを判定している。素数だった場合
はprintfでその整数を出力している。
main関数の最後に改行を出力している。

・考察
上記のプログラムでは素数を判定する関数を作った一方で、main関数内
のfor文内にに素数を判定するブロックを入れる記述も思いついた。し
かし、上記の素数判定の関数を作る記述の方が、役割ごとにデバッグが
しやすく、行数の少ないブロックに分かれるので可読性が高いとわかっ
た。また、機能ごとに実装するとこまめに思考が整理でき、プログラム
を記述する中で混乱し、文法など記述ののイージーミスが少なくなると
考えられた。また、プログラムの機能を拡張するときに素数判定の関数
を書いておくと拡張する機能の中で同じ処理を新しく書く必要がないの
で便利だとわかった。

・3つ目の課題
演習2a(2)テキストの図のような絵を作成する

・プログラム
#include <stdio.h>
#include "eps.h"
int main() {
  eps_open("out.ps", 420, 420);
  for(int i = 1; i <= 5; ++i) {
    for (int j = 1; j <= 5; ++j) {
      if ((i + j) % 2 == 0) {
        eps_fillrect(30 + i * 60, 30 + j * 60, 60, 60);
      }
    }
  }
  eps_close();
  return 0;
}

・プログラムの説明
epsを使うためにヘッダファイルeps.hをincludeしている。
まず、main関数内でeps_openより、サイズを縦横420pt、出力のファイ
ル名をout.psと指定した。
描く絵は5列5行のチェック柄なので、1〜5のfor文を入れ子にし、列と
行の数の和が偶数になる場合だけ正方形を描画することで、絵がチェッ
ク柄になるようにしている。正方形の角が重なるように正方形同士の縦
横の感覚は1辺の長さの60と同じにしている。

・考察
主にeps_fillrectを座標を変えて繰り返すプログラムになるので、for
文の入れ子構造とif文による条件を工夫してでの記述をeps_fillrect1
つに済ませた。繰り返す処理は繰り返しや条件分岐で記述を少なくする
ことで機能ごとにまとまり、条件の値を変えることで一括にデバッグし
やすくなるという利点があることがわかった。

アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
今回は去年の内容の復習が主だったのでプログラムを作ること自体はあ
まり大変ではなかった。忘れてしまっていたことを思い出すことの方が
大変だった。
Q2. epsライブラリを使ってみてどのように感じましたか。
ライブラリを使って絵を描いたり様々に機能が拡張できるのでできるこ
とが果てしなく多いと感じた
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
プログラミングには論理的な思考だけでなく、動かす処理の周辺知識も
重要であることがわかった。

h1910541 1b:○ Mon May 18 21:51:30 2020


学籍番号:1910541 氏名:@@@光 真司 提出日時:5月18日

1、「課題の再掲」整数nを入力して、nの素因数分解を表示する。
  「ソースコード」
// soinsu --- soinsubunkai
#include <stdio.h>   
int main(void) {
  int n, x;
  printf("n> "); scanf("%d", &n);
  x = 2;
  while( n != 1 ) { 
    if(n % x == 0) { n = n / x; printf( "%d\n", x);
    } else {
      x = x + 1;
  }
  }
  printf("\n");
  return 0;
}

 「説明」 最初にnに数値を入力する。xを初期値2に設定しておく
(1だと意味がない)。whileの条件をnが1でないときに設定し今度は、
ifの中でnをxで割った剰余が0ならば、nをxで割りそのときのxを出力。
そうでないとき、xに1足しておく。nが1になるまでこれは続き、nが1に
なったら終了。

 「考察」 最初にforを使おうと思ったのだが、++iの処理がわから
ずwhileに切り替えた。もしこの演習をforでやった場合どのようなコー
ドになるのか、あと見栄えの問題で出力の際に\nをつけたが空白を入れ
るにはどうしたらよいのか。

2、「課題の再掲」 直角三角形の直角を挟む2辺の長さを入力し、斜
辺の長さを出力する

 「ソースコード」
// syahen --- syahen of a triangle
#include <stdio.h>
#include <math.h>
double syahen(double a, double b) {
  double c = sqrt(a*a + b*b);
  return c;
}
int main(void) {
  double a, b;
  printf("a> "); scanf("%lf", &a);
  printf("b> "); scanf("%lf", &b);
  double x = syahen(a, b);
  printf("syahen of triangle = %g\n", x);
  return 0;
}

 「説明」 今回は実数のdoubleを使い、aとbに値を代入する。斜辺は
2辺の二乗の和の平方根であるためsqrtを使う。二乗はa*a,b*bで表し式
を作る。最後にcを返し終了。

 「考察」 例題をもとに作っているので関数として分かれているが、
やろうと思えばmainからでもできるのではないかと思った。

3、アンケート

A1、一文字のミスで小一時間消し飛ぶ大変さ
A2、やりたかったが、時間足らず。課題外でやってみたい
A3、トレーニング課題10個やったはいいものの再掲と考察が多すぎるの
とそこまで代わり映えしないので
書けなかった。

h1910542 1b:○ Tue May 12 19:58:19 2020



1.プログラム
演習1のd
演習1のdは整数nを入力したときにn以下の素数を出力するというプ
ログラム。作成したプログラムは以下のようになった。

// prime3.c --- n ikano sosu
#include<stdio.h>
int main(void){
    int i , j, n;
    printf("n> "); scanf("%d", &n);
    int hantei;
    for(i=2;i<=n;++i){
        hantei = 0;
        for(j=2;j<i;++j){
            if(i%j==0){
                hantei = 1;
                break;
            }
        }
        if(hantei == 0)
        printf("%d ", i);
    }
    printf("\n");
    return 0;
}

For構文の中でfor構文を用いることで素数を求めた。具体的には、素数
は1と自分以外の約数をもたない数であるため、i, j, hanteiの3つの
変数を用い、まずhantei = 0とし2からnまで1ずつ数を増やしていく
中で1つの数字iについて約数があるかをfor構文の中のfor構文で探して
いる。もし数字iのなかでjで割り切れる数の場合、素数ではないため
hantei = 1としfor構文の中のfor構文からははずれる。割り切れなかっ
た場合、素数であるためhantei = 0であるため、if文で出力させる。こ
れを2からnまで繰り返すことでn以下の素数を求めることができた。

・考察
 初めはn以下の素数をすべて求め、出力させるプログラムは長く難し
そうだと思ったが、素数の本質を理解することで、2からnまでの数を
一つずつ取り出し、n未満の数で割ればいいと気づいた。また、素数か
どうかを調べるために変数を用いたのも出力する際に便利だったのでこ
れからも有効に使っていきたい。また、true,falseでも同様なプログラ
ムができるのではないかと思った。

演習2のa(1)
演習2のa(1)は正方形の図形が16個あり、それが縦、横4×4の正方形
に並ぶ図形を作るプログラムを作る。作成したプログラムは以下のよう
になった。

#include<stdio.h>
#include"eps.h"

int main(void) {
  eps_open("square.ps", 480, 480);   
  for(int i = 1; i<=4; ++i){
      for(int j = 1; j<=4; ++j){
        eps_drawrect((i-1)*120, (j-1)*120, 60, 60);
        }     
    }
  eps_close();                          
  return 0;
}

4×4の正方形が16個並んだ図形なので、for構文の中にfor構文を用い
た。eps_drawrectは左隅のXY座標と幅と高さを指定するものであり、
一つ一つの正方形は同じ大きさであるため、最初の正方形を(0,0)に指
定し、変数iとjを用いて4×4になるようにすることで求める図形になっ
た。

・考察
 最初は中心に原点を持ってきてやろうとしたが、eps_drawrectが左隅
に座標を決めることで正方形が描けることにきづいたため、2次元配列
に近いことからfor構文の中にfor構文を用いたことで指定の図形が描け
ることがわかった。また、epsを用いると色を変えたり、彩度、明度も
簡単に変えることができることに驚いた。今回自分が作成した図形は原
点側に偏りがあった。それは正方形の1辺と隙間の幅を一緒にして考え
てたためであり、まだ改善する方法があると思うので探していきたい。

2.アンケート
Q1。プログラムを作るのはひらめきやこれを使えばできるというとこ
ろに達するまでが大変だった。
Q2.epsライブラリは理解するまで時間がかかったが全部ではないが
少しは理解することができた。正方形などを簡単に作ったり色付けでき
たりするので面白いと感じた。
Q3.C言語で忘れていた部分や理解出来てなかった部分も理解できた
ので良かった。

h1910546 1b:○ Sun May 17 16:58:28 2020



1つ目の課題の再掲
整数nを入力し、2n を出力する。n ≥ 0のときは整数、そうでないとき
は実数形式で出力するプログラムをつくる。

プログラムのソース
#include<stdio.h>

int main(void) {
  int n, i, x = 1;
  double y = 1.0;
  printf("n>");scanf("%d", &n);
  if(n >= 0) {
  for(i = 0; n > i; ++i) {
    x = 2*x;
  } printf("%d\n", x);
  } else {
  for(i = 0; n < i; --i){
    y = y/2;
  }
  printf("%f\n", y);
  }
  return 0;
}
プログラムの説明

Ifの条件部分がnが0以上のときとそうで無いときとで場合分けをし、0
以上の場合はint型の変数xを使い、それ以外の場合はdouble型の変数
yを使った。Forのループ文を使い、条件を満たさなくなるまで掛け算、
割り算することで累乗を表している。
 

考察
printfで出力するときに、フォーマット指定子には対応する変数の型を
正しくあてはめないと間違った出力が行われると思われる。実際、上記
のプログラムのprintf("%f\n", y)のフォーマット指定子のfをdに変
えてみたところ、出力がn=-3の時、844212752となった。本来なら、
0.125となるはずなので明らかに間違っている。これよりC言語は型に関
して、厳格であることが分かった。

2つ目の課題の再掲
整数n (n> 1)を入力し、nの素因数分解を表示するプログラムをつくる。

プログラムのソース
#include<stdio.h>

int main(void) {
  int n,i;
  printf("n>"); scanf("%d",&n);
  for(i = 2; n >= i; ++i) {
    while(n % i == 0) {
      printf("%d", i);
      n = n / i;
    }
  }
  printf("\n");
  return 0;
}

プログラムの説明
for文とwhile文を組み合わせて、n/iの余りが0になるまで割り算し続
け、その作業が終了したらiを1ずつ増やして、またwhile文に戻るとい
うプロセスで素因数分解を表現しているプログラムである。

考察
上記のプログラムはmain関数のみで構成されており、長くなっているよ
うに思えるが、プログラムが複雑になっている場合は、main関数外に関
数を別に宣言して、それをmain関数内で呼び出すといったことをすれば、
main関数内がシンプルになりわかりやすいプログラムになると考えられ
る。

アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか? 
初回なので、それほど大変ではなかった。

Q2.epsライブラリを使ってみてどのように感じましたか。
余り使ったことがなかったので、新鮮だった。 

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
プログラミングをこれからも頑張りたい。

h1910556 1b:○ Sun May 17 15:09:06 2020


レポート1b
2020/5/17 提出

1 課題1
1.1 課題の再掲

 演習1_c
 1より大きい整数nを入力し、その素因数分解を出力する。


1.2 ソースコード

 #include <stdio.h>
 #include <math.h>
 
 int main(void) {
     int n;
     printf("n>");
     scanf("%d", &n);
     while(n%2==0) {
         printf("2 ");
         n /= 2;
     }
     for (int i = 3; i <= (int)sqrt(n); i += 2) {
         while(n%i==0) {
             printf("%d ", i);
             n /= i;
         }
     }
     if(n==1) {
         printf("\b\n");
     }
     else {
         printf("%d\n", n);
     }
     return 0;
 }


1.3 入出力例

 in  n>144
 out 2 2 2 2 3 3

 in  n>999
 out 3 3 3 37


1.4 説明

 main内2~4行目は整数nの入力。
 素因数分解は整数nを素数の積で表すものであり、素数のうち偶数である物は2だけである。
 ので、5~8行目ではnを2で素因数分解して、9~14行目では奇数で素因数分解している。
 このときの素因数分解はwhileループにより整数を割れるだけ割っている。
 この際、除数の素数判定をしていないがforループにより絶対値の小さ
 い順に除数を見ているためにnを合成数で割ることはなくなっている。
  また、forループの上限である"(int)sqrt(n)"の値は素因数分解の過
 程で小さくなっていくため計算量を抑えられる。
 15~20行目は素因数分解の最終項に関するもので、9~14行目のforルー
 プ中に整数が割り切れ1になる場合と"(int)sqrt(n)"以下の数で割り切
 れないという2通りの場合分けが発生する。
  1になる場合は1が素数ではないので出力したくない。
 それらをif/elseで解消し、割り切れた場合はスペースが残るので\bで削除している。
 除数を小さい順にみているために結果も素因数の絶対値が小さい順に
 並ぶようになっている。
 

1.5 考察

説明にも書いたが人が素因数分解するときは「因数を見つける」→「素
数か判定」の2つの手順が必要だが、
コンピュータでは「因数を見つける」の1つの手順だけですむということがわかった。


2 課題2
2.1 課題の再掲

 演習2_a_1
 PostScriptを用いて指定された図を書く。
 今回の場合は中空の正方形が等間隔で4*4個に並んだもの。
 色や線幅は自由。

2.2 ソースコード

 #include <stdio.h>
 #include "eps.h"

 int main(void) {
     eps_open("2_a_1.ps", 480, 480);
     eps_cmd("240 240 translate");
     for (int i = 0; i < 4;++i) {
         for (int j = 0; j < 4;++j) {
             eps_drawrect(-225 + i * 120, -225 + j * 120, 90, 90);
         }
     }
     eps_close();
     return 0;
 }


2.3 説明

main内1行目と8行目では図を書くためのファイルの用意に関する命令を
書いている。画像の大きさは図に合わせて適当な大きさの正方形にした。
2行目では図を書く際に用いる座標の原点をファイルの中心にもってき
ている。3~7行目では正方形が等間隔に並んでいるという特徴を生かし、
2重ループによって図を描いている。この際、正方形の左下の点を基準
に取っている。


2.4 考察

コンピュータで曲線を描くときには細かい線分をつなげて曲線に近似させる。
この分割を細かくしていくほど、より曲線に近づくが線分の数も増えてしまう。
この2つのバランスが取れた分割はどのくらいなのか,またPostScript
のarcという指定はどのように弧を描いているのか疑問に思った。

3 アンケート
Q1.プログラムを作るという課題はどれくらい大変でしたか?

 今回のものは大変ではなかった。


Q2.eps ライブラリを使ってみてどのように感じましたか。 

 いちいち実行しないと図を見ることができず、座標を意識しながら図
 を書くのが面倒だった。

Q3.リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。

 次回からの新しい内容も頑張りたい。

h1910589 1b:○ Mon May 18 23:05:49 2020



[プログラムのソース.1]
演習1c(1より大きい整数nを入力し、nの素因数分解を表示するプログラム)

#include <stdio.h>
#include <math.h>
#include <stdbool.h>

#define max 10000

int lemon[max];
int a=2;
int b=0;



bool check(int c){
  int d=(int)sqrt(c);
    for (int k=1; k<a; k++){
      if(d<lemon[k])break;
      if(!(c%lemon[k])){
        return false;
      }
    }
  return true;
}



int amazon(int b){
  lemon[0]=2;
  lemon[1]=3;

  for(int k=5; k<=b; k+=2){
    if(check(k)){
      lemon[a]=k;
      a++;
      if(a>=max){
        printf("許容数を超えた為ここまでの結果を表示する.\n");
        break;
      }
    }
  }
  return a;
}



int main(void){
  int input=0;
  printf("素因数分解を行う数>");
  scanf("%d",&input);
  printf("結果\n");
  for (int b=0; b<amazon((int)sqrt(input)+1); b++){
    while(input%lemon[b]==0){
      printf("%d",lemon[b]);
      input=input/lemon[b];
    }
  }
  printf("\n");
  return 0;
}


[プログラムの説明.1]
inputに10000未満の任意の整数を渡してamazonのメソッドでinputの平
方根以下の素数をlemonに書き出す.その後与えられた数をできるだけ割っ
ていき、最終的に素因数分解を行うことができるというプログラムになっ
ている。

[プログラムの考察.1]
授業当日に作成したプログラムより遥かにコードが長く複雑になり手間
取ってしまった.まず素因数分解をどうやって行っていくかの仕組みを
思いつくのにも時間を要した.プログラミング以前に自分の数学力が足
らないと感じた.今回のプログラム内では繰り返しにforを用いた
が,whileを使っても条件の指定がしやすいのではないかと考えた.


[プログラムのソース.2]
演習1d(整数nを入力するとn以下の素数を出力するプログラム)

#include <stdio.h>
#include <stdbool.h>

#define max 10000
int india[max];
int a=2;
int b=0;



bool check(int m){
  for(int k=1;k<a;k++){
    if(!(m%india[k])){
      return false;
    }
  }
  return true;
}

int main(void){
  printf("n>");
  scanf("%d",&b);
  india[0]=2;
  india[1]=3;



  for(int k=5; k<=b; k+=2){
    if(check(k)){
      india[a]=k; a++;
      if(a>=max){
        printf("許容数を超えた為ここまでの結果を表示する.");
        break;
      }
    }
  }




  for(int k=0; k<a; k++){
    printf("%8d個目: %d\n",k+1,india[k]);
  }


  printf(“終了\n”);
  return 0;
}


[プログラムの説明.2]
演習1cと同様にindiaの配列の中にはあらかじめ素数である2と3を設定
しておき,checkでkがlistに含まれる数で割り切れるか判別して,新たな
素数が含まれている場合indiaに追加,続いてkを2ずつ増やしながら繰り
返し、素数を並べるという仕組みをとった.

[プログラムの考察.2]
演習1cと同様にkの値は2ずつ増やしていった.これは偶数である素数は2
以外に存在しないということからなされた工夫である.2ずつ増やさなく
ても同じ結果を出すことはできるが,こうした方が作業の効率化に繋が
ると考えて実行した.今回作成したプログラムは規模が小さいので,この
ような工夫をしても実行される過程では誤差のようなものでしかないと
は思うが,いつかゲームやアプリといった比較的規模の大きいプログラ
ミングをする際に大いに役立つのではないだろうか. このような小さな
工夫を忘れずに演習を行いたい.

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
とても大変
Q2. epsライブラリを使ってみてどのように感じましたか。
難しい.学校のパソコンで使用したい

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。 
もっと勉強します.

h1910596 1b:○ Mon May 18 22:31:00 2020


学籍番号: 1910596
ペア 1910620
提出日時: 2020/5/18

<演習1b>
・ソース
#include <stdio.h>

double ruijou(int n) {
  int i;
  if (n >= 0){
    int a = 1;
    for (i = 0; i < n; ++i){
      a = a * 2;
    }
    return a;
  } else {
    double b = 1;
    n = -n;
    for (i = 0; i < n; ++i){
      b = b / 2.0;
    }
    return b;
  }
}

int main(void){
  int n;
  printf("n> "); scanf("%d", &n);
  printf("2^n = %g\n", ruijou(n));
  return 0;
}

・説明
このプログラムでは、nが0以上の場合と0より小さい場合に分けて考え
た。nが0以上の場合はiがn以上になるまで2をかけつづけた。0より小さ
い場合は、nを正の整数に変えiがn以上になるまで2で割り続けた。また、
0以上の場合ではintを使い、0より小さい場合はdoubleを使った。

・考察
このプログラムでは、for文を使ったがnが0以上の場合は比較的すぐに
できたがnが0より小さい場合にはnが負の数であるため、どのようにfor
文を使えば良いのかわからなかった。しかし、nを正の数に変えてやれ
ば0以上の場合と同じ要領で行うことができると気づいた。このような
気づきは、プログラムを作ることにおいて大切であると考える。また、
以前はあまり気にしていなかったintとdoubleの使い分けの仕方を理解
できたと考える。もっと処理を簡単にすることではpow関数を使えば良
いと考えられる。


<演習1c>
・ソース
#include <stdio.h>

void bunkai(int s){
int i=2;
    while(i<=s){
if(s%i==0){
    s=s/i;
    printf("%d ",i);
    }else{
   ++i;
    }
    }
}
int main(void){
    int n;
   printf("n>");scanf("%d",&n);
   bunkai(n);
    return 0;
}

・説明
素因数分解を表示するプログラム。while文を用い、iの初期値を2とし
与えられた数sをiで割り余りが0の場合はiを出力し、余りが0でない場
合にはiを1増やす。この操作をiがsと同じ数になるまで繰り返すことに
より、素因数分解を表示することができる。

・考察
今回のプログラムは、iを1ずつ増やしていき割っていった。しかし、こ
れでは結果には関係しないが前に割った数の倍数で割ることもあり無駄
が生じると考えられる。そこで、素数かどうかを判定するプログラムな
どを用いて割る数を素数のみに限定することができれば、無駄を減らす
ことができると考えられる。また、今回のような短いプログラムでは、
main関数のみで構成しても良かったと考える。

<アンケート>
Q1. プログラムを作るという課題はどれくらい大変でしたか?
すごく大変であり、一つの課題をやるのにも結構時間がかかってしまった。

Q2. epsライブラリを使ってみてどのように感じましたか。
まだ使っていないのでわからないが、近いうちに使ってみたいと思う。

Q3. リフレクション・感想・要望をどうぞ。
以前よりプログラミングがあまり得意ではないが、忘れていたことも多
かったので早く思い出したい。

i1910089 1b:○ Mon May 18 23:26:27 2020



3. 1c
コード
#include <stdio.h>
void primeFactrization(int n,int a){
    if(n==1)return;
    if(n%a==0){
        printf("%d\n",a);
        primeFactrization(n/a,a);
    }else{
        primeFactrization(n,a+1);
    }
}
int main(void){
    int n;
    printf("n> ");scanf("%d",&n);
    primeFactrization(n,2);
    return 0;
}

実行例
n> 60
2
2
3
5

説明
与えられた数を割り切れないところまで割るという原始的な方法を取った。
関数primeFactrizationは第1引数nを第2引数aで割り切れるか確認する。
aの初期値は最小の素数である2とした。
割り切れた場合aを出力、さらにaはそのままでnに商を渡す。同じ素因
数が複数回出る可能性があるからだ。
割り切れなかった場合はaにa+1を渡す。
nが1、つまりこれ以上割り切れなくなった時に抜ける。

考察
今回は見た目のコード量を減らすことを優先したので実装しなかったが、
割り切れなかった時にaに渡す数字を次の素数までスキップした方がよさそうだ。
また、aで割り切れるごとに出力するようにしたが
配列か何かに溜めておいて関数を抜けた時にまとめて出力した方が良いだろうか。
しかし入力条件は無いのでどのくらいのサイズ確保しておけばよいか分
からないのでためらってしまった。

4. 1e
コード
#include <stdio.h>
int arithmeticSequence(int a,int b,int x){return a*(x+1)+b;}
int differenceSequence(int a,int b,int f1,int n){
    int y=f1;
    for(int i=0;i<n;i++)y+=arithmeticSequence(a,b,i);
    return y;
}
int main(void){
    int a,b,fn,n;
    printf("y = a*x + b\n");
    printf("a> ");scanf("%d",&a);
    printf("b> ");scanf("%d",&b);
    printf("first number?> ");scanf("%d",&fn);
    printf("n> ");scanf("%d",&n);
    for(int i=0;i<n;i++)printf("%d ",differenceSequence(a,b,fn,i));
    printf("\n");
    return 0;
}

実行例
y = a*x + b
a> 2
b> 3
first number?> 4
n> 5
4 9 16 25 36

説明
階差関数を返すプログラムを書いた。
関数arithmeticSequenceを等差数列を返し、
関数differenceSequenceその和を初期値に足して返す。

考察
関数differenceSequenceの引数が4つと多くなってしまった。
変数a,bをグローバル変数にするのが一番早いだろうか。

5.
Q1. 動けば構わない、というコードをよく書いているので他人に見せる
前提で見やすいコードを書くのは慣れなかった。
Q2. コードから出力結果が想像しやすかった。
Q3. 次回は配列のサイズを動的に確保する方法が出てくるらしいので楽しみだ。

k1710179 1b:△ Tue May 19 13:36:48 2020 (Late)


1710179 河村 優周

プログラミング通論#1

1.課題
演習1ーb
#include <stdio.h>
#include <math.h>

int main(void){
    double n;
    printf("n = ");
    scanf("%lf", &n);
    if(n >= 0)
        if(n >= 31)
            printf("2^%d is overflow.", (int)n);
        else
            printf("%d\n", (int)pow(2, n));
    else
        printf("%lf\n", pow(2, n));
    return 0;    
}

2.簡単な説明
倍精度実数nを受け取りnが0以上30以下のとき2のn乗を計算して
切り捨てで整数として出力する。31以上のときoverflowと出力する。
またnが0未満のとき2のn乗を計算し、実数形式で出力する。

3.出力結果
PS C:\pro2> ./a.exe        
n = 15
32768
PS C:\pro2> ./a.exe
n = 31
2^31 is overflow.
PS C:\pro2> ./a.exe
n = -0.5
0.707107

4.考察
int型の値の範囲が2^31未満のためそれ以上はoverflowとして処理した。
また実数形式の出力はdouble型で2^(-1022)より小さい数はunderflowであるが
今回の場合有効桁数が6桁なので問題ないと判断している。

1.課題
演習1ーc
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool isprime(int n) {
  int limit = (int)sqrt(n);
  for(int i = 2; i <= limit; ++i) {
    if(n % i == 0) { return false; }
  }
  return true;
}

int main(void){
    int n, i;
    printf("n = ");
    scanf("%d", &n);
    if(n > 1){
        if(isprime(n)){
            printf("%d ", n);
            return 0;
        }
        
        while(n % 2 == 0){
                printf("%d ", 2);
                n = n / 2;
        }
        
        for(i = 1; 2*i+1 <= n; i++){
            
            while(n % (2*i+1) == 0){
                printf("%d ", 2*i+1);
                n = n / (2*i+1);
            }
        }
        printf("\n");
    }
    return 0;    
}

2.簡単な説明
整数nを受け取り、nに含まれる素因数を出力するたびにnを素因数で割っていく。
nが1になると改行して終了する。

3.出力結果
PS C:\pro2> gcc pro2-1-1c.c
PS C:\pro2> ./a.exe        
n = 60
2 2 3 5
PS C:\pro2> ./a.exe
n = 300
2 2 3 5 5

4.考察
予め既に判明している素因数でnを割っておくことで奇数で素因数を調
べることができるようにした。またnが既に素数の場合for文が無駄にな
るのでisprimeを最初に設けた。

4.アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか ?
とても大変でした。
Q2. eps ライブラリを使ってみてどのように感じましたか。
面白いと思いました。
Q3. リフレクション ( 今回の課題で分かったこと ) ・感想・要望をどうぞ。
課題がんばります。

m1810587 1b:○ Mon May 18 15:18:06 2020


1810587
個人作業
2020年5月13日9時提出

課題1
演習1b 整数nを入力すると、2のn乗を出力するプログラム

#include<stdio.h>
#include<math.h>

int main(void){
        int n;
        double r;
        printf("n> ");scanf("%d",&n);
        r = pow(2.0,n);
        if(n >= 0){
                int a = (int)r;
                printf("%d\n",a);
        }else{
                printf("%lf\n",r);
        }
        return 0;
}

プログラムの説明
まず入力としてnを受け取り、pow関数を利用して2のn乗の結果をrに格納する。
次にnが0以上なら整数で、そうでないなら少数点も込みで出力したいた
め、条件分岐を行う。
0以上の場合はキャスト演算を用いてrを整数に変換し、出力する。
そうでなければそのまま出力する。

課題をやってみて分かったこと
最初にdouble型のrを出力するための型指定子を%ldだと勘違いしていた
ため、奇妙な値が出力されて戸惑った。
データ型を間違えると値がおかしくなってしまうようだ。
おそらくdoubleが浮動小数点表記でintが2の補数表現を使っていると思
うのでそれで値が変わってしまうのではないかと思うが、型指定子が間
違っている際に出力させるたびに数値が変わることがあり、それは何故
なのか疑問に思った。

課題2
演習1c 整数nを入力するとその素因数分解を出力するプログラム

#include<stdio.h>
#include<math.h>

int main(void){
        int n;
        int i = 2;
        printf("n> ");scanf("%d",&n);
        while(n != 1){
                if(n % i == 0){
                        n = n / i;
                        printf("%d ",i);
                }else{
                        i++;
                }
        }
        printf("\n");

        return 0;
}

プログラムの説明
入力された数値を2から始めて順繰りに割っていく。
割り切れた際には割った数を出力する。
その数で割れなくなったら割っていた数に1を足してまたループする。
これを入力された数値が1になるまで繰り返す。

考察
2から1ずつ足していって、その数で割れなくなるまで割っていく。
このループだと、すでに割った数の倍数が絶対に割れないことが分かっ
ているのに割れるかどうかを確かめるので、結果に支障は出ないが、無
駄がある。
テキスト内に出てきた素数判定のプログラムを使って割る数を素数かど
うか判定してからやった方が無駄が少ないかもしれない。

Q1. プログラムを作るという課題はどれくらい大変でしたか?
少し大変でした。

Q2. eps ライブラリを使ってみてどのように感じましたか。
難しいです。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
今回はまだ割と簡単だったので次回に備えたいです。

m1810639 1b:○ Tue May 12 17:29:18 2020


学籍番号:1810639
氏名:森口 壮太郎
個人作業
提出日時:5/12

課題の再掲1
整数nの約数を表示するコードをつくる

コード1
#include <stdio.h>
void wari(int n) {
  int i = 2;
  while(i <= n || n > 1) {
    if(n % i == 0) {
      n = n / i;
      printf("%d ", i);
    } else {
      i++;
    }
  }
  printf("\n");
}

int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  wari(n);
  return 0;
}

説明
関数wariで始めにi=2と定義してiの値を1ずつ上げてiがnの約数かどう
かを判定している。約数であれば、それを表示して、nをiで割った値に
置き換える。そうして、nに約数がなくなるまでwhile文で繰り返す。
while文の条件は、iがn以下かつ2以上にしている。
今回の課題では、約数を収納する配列を関数外で定義して、nの値を変
更することなくコードをつくることができるのではないかと考えた。

課題の再掲2
等間隔に正方形を並べた絵をかく。

コード2
#include<stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  int j = -240;
  while(j < 480) {
    int i = -240;
    while(i < 480) {
      eps_drawrect(i + 30, j + 30, 60, 60);
      i = i + 120;
    }
    j = j + 120;
  }
  eps_close();
  return 0;
}

説明
while文の中にwhile文を定義し、その中で変数iとjを変化させて等間隔
で正方形をかいている。まず、iとjを左下の座標に設定してから四つの
正方形が入るようにwhile文を繰り返した。

アンケート
Q1
頭の中で考えたものをそのままコードにするのは大変だった。紙にかい
て考えると簡単になった。
Q2
それぞれの関数の意味が分かってからは、使い易かった。
Q3
楽しくできたのでよかった。久し振りにC言語をつかったけどけっこう
覚えていた。

m1910598 1b:○ Mon May 18 23:44:06 2020


1. 課題の再掲
1-d 整数nを入力し、n以下の素数を出力する。
2-a(1) espライブラリを使用して絵を作成する。

2. プログラムのソースとその説明及び考察
1-d
#include <stdio.h>
#include <math.h>      
#include <stdbool.h>   
bool sosu(int n) {
    int max=(int)sqrt(n);
    for(int i=2;i<=max;++i){
        if(n%i==0){
            return false;
        }
        return true;
    }
}

int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  for(int i=2;i<=n;++i){
      if(sosu(i)){
          printf("%d",i);
      }
  }
  printf("\n");
  return 0;
}
(実行例)
n>4
2 3
n>5
2 3 5
(説明)

まずint型の変数nを入力し、ループ文を使って2から√nまでの数で順に
割っていってnが素数かどうか判定するプログラムを作成した。main内
については、まずnを入力し、ループ文を使って2からnまでの数につい
て素数かどうかの判定をしていき、もし素数であれば、その数を出力す
るという内容のプログラムである。
  
(考察)

素数判定の際に二乗根を整数値に変換するプログラムを利用することで
簡単にすることができることを知れたので値を変換することを今後も利
用したい。

2-a(1)
#include <stdio.h>
#include "eps.h"
int main(void){
    eps_open("out.ps",400,400);
    for(int i=1;i<=4;++i){
        for(int j=1;j<=4;++j){
            eps_drawrect(100*i-50,100*j-50,50,50);
        }
    }
    eps_close();
    return 0;
}
(説明)
includeでeps.hを読み込み、main内では、eps_openで400×400の用紙を
生成し、for文を重ねてeps_drawrectで等間隔に60×60の正方形を16個
描き、eps_closeでファイルを完成させるという内容のプロスラムを書
いた。

(考察)
for文を重ねたことで簡潔にプログラムが書けたと思う。

3. アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
久しぶりにプログラムを書いたのでいろいろと思い出すのが大変だった。
また復習するようにしたい。
Q2. epsライブラリを使ってみてどのように感じましたか。
図形が書きやすく感じた
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
今回の内容は基礎的なものであったが、抜けている部分も多くあったた
め、しっかりと復習するようにしたい。

m1910601 1b:○ Mon May 18 22:09:30 2020


1910601
「個人作業」
5月18日

「1つ目の課題」
(演習1-d)整数nを入力し、n以下の素数を出力するプログラムをつくること。

[プログラムのソース]
 実際に書いたプログラムは次の通りである。

// sosuu (1-d)
#include <stdio.h>
#include <stdbool.h>
double hantei(int n) {
  int i;
  bool jug = true;
    for(i = 2; i < n; ++i){
      if(n%i == 0){jug = false;
      }
    }
    if(jug == true){
    printf("%d ", n);
    }
    return 0;
  }

  double sosu(int n){
  int i;
  printf("1 ");
    if(n != 1){
      for(i = 2; i < n; ++i){
        hantei(i);
      }
    } 
  return 0;
  }

int main(void) {
  double num;
  printf("number> "); scanf("%lf", &num);
  sosu(num);
  printf("\n");
  return 0;
}

[プログラムの説明]
まず、関数hanteiはある数nが素数かどうかを判定するために作成した。
nが素数であればその約数は1とnのみ、すなわち 2からn-1の間には約
数がないことに注目し、ループでnを2からn-1で割ってあまりが0(すな
わち約数)であればbool型としたjugにfalseを、層でなければtrueが入っ
ているようにし、のちにfalseなら何もせずに0を返す、trueなら素数n
をprintfで出力してから0を返すようにした。

次に関数sosuではまず1を出力するようにした。1以下の素数は1だけ
なので、1以外のときに2からnまでの数を順に関数hanteiにいれていき、
素数であれば出力されていく機構とした。

[考察]
このプログラムを書いている時、はじめは関数hanteiとsosuを1つのも
のとして書こうとしていたが、ループの中にループを書く構造になり仮
引数がやや複雑になってしまい時間がかかってしまったため、分割して
書いたところ、あっさりと書けてしまった。また、多少のエラーであれ
ば分割されている方が直しやすくもあった。関数の数を増やすと記述量
は増えるが1つ1つの構造自体は簡単にでき、時間が短縮できるため、
結果としては効率が上がるケースも考えられる。関数を1つにすること
にこだわらず、分割して書くことも考慮する必要があると認識させられ
た。

ーーーーー

「2つ目の課題」

(演習1-e)整数nを入力し、何かしらの関数(今回はトリボナッチ数の数
列を採用)の最初のn項を出力するプログラムをつくること。

なお、トリボナッチ数は次の式で定義される数列である。
 T(n) = T(n-1) + T(n-2) + T(n-3)  かつ T(0) = 0, T(1) = 0, T(2) = 0

[プログラムのソース]
 実際に書いたプログラムは次の通りである。

// toribonachi (1-e)
#include <stdio.h>
double tori(int n) {
  if(n == 0){return 0;}
  else if(n == 1){return 1;}
  else if(n == 2){return 1;}
  int i;
  i = tori(n-1) + tori(n-2) + tori(n-3);
  return i;
}

int main(void) {
  int num;
  int i;
  int p;
  printf("number> "); scanf("%d", &num);
  for(i = 1; i <= num; ++i){
  p = tori(i - 1);
  printf("%d ", p);
  }
  printf("\n");
  return 0;
}

[プログラムの説明]
まず、上記のトリボナッチ数の式より再帰関数として関数toriを考えた。
初項から第3項まではそれぞれ0,1,1であるため、ifとelse ifでnが0か
ら2の時にそれぞれの値を返すようにした。nが3以上のときは再帰関数
として tri(n-1) + tri(n-2) + tri(n-3) の値を返すようにした。

次にmainの方では1から実引数numまでループさせてtori(i-1)の値をそ
れぞれ出力することで数列を出力した。

[考察]
この課題では、printfの出力の位置を考える必要があると気づかされた。
最初は関数toriの方にprintfを組み込むつもりであったが、再帰関数で
あるため、その関数の中にprintfを組み込むと出力がめちゃくちゃにな
り、思うようにはいかなかった。そのため、mainの方にループを用いて
第i項目(0 <= i <= n-1)の値をそれぞれ出力することで数列の表記を
することができた。上記の1つ目の課題で実感したことである、1つに
詰め込みすぎずに分割することの重要さをこちらの課題でも考えさせら
れた。すべての場面において、必ずしも分割することが良いことである
とは言えないが、分割を念頭におきながらプログラムを書いていくこと
が大切であると考えられる。

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
構造自体を考えるのにはあまり時間はかかりませんでしたが、不具合の
調整には時間がかかりました。

Q2. eps ライブラリを使ってみてどのように感じましたか。
何か規則性をのあるものを描くときには向いているとは思いますが、書
きたい内容が少ない場合はやはりペイントなどのツールの方が優れてい
るように感じてしまいます。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
今回の課題では最初に思いついた構造ではうまく行かず、視点を変えた
らうまくいったというような調子だったので、以前までの経験則も生か
しつつ、柔軟に考えるようにしたいと思いました。

m1910603 1b:○ Mon May 18 19:52:56 2020


課題1b



1.c

整数nを入力し、その素因数を出力する。

#include <stdbool.h>
#include <math.h>
#include <stdio.h>

int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  int i,j;
  while(n > 1){
    i = 1; j =1;
    while(j != 0){
      i = i + 1;
      j = n % i;
    }
    printf("%d ",i);
    n = n / i;
  }
  printf("\n");
  return 0;
}

メイン関数に直接書き込む形で書いた。
内側のループでnを割り切れる最小の素数を探し、それを返す。
外側のループではnを内側のループで探した最小の素数で割り、もう一
度内側のループへ送る。
これをnが1になるまで繰り返すと素因数が小さい順に返される。
また、素数はそのまま返される。

ループ一度ごとにi,jの値を初期化しないと無限ループになってしまう
ことが途中で分かった。
原因は一度iが1になると同じnが続いてしまうからである。
また、iの値を増やしてから素因数判定しないと実際に判定された値よ
り1大きな値を返してしまう。

1.d

整数nを入力し、n以下の素数を出力する。

#include <stdbool.h>
#include <math.h>
#include <stdio.h>


int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  int i = 2;
  while(i <= n){
    int k = 1,j = 2;
    while(j <= sqrt(i)){
      k = k * (i % j); j = j + 1;
    }
    if(k != 0){
    printf("%d ",i);
      }
    i = i + 1;
    }
  printf("\n");
  return 0;
}

メイン関数に直接書き込む形で書いた。
内側のループである数について1からnの平方根までの数で割り素数判定
をし、それを外側のループで1からnまで繰り返した。
素数判定法は、すべてのあまりの積が0でないと素数であるという事実を用いた。

この時、すべての整数は1で割ると必ず余りが0になるため、j=2とし、割る数から除外した。
また、1は1以外の数で割ると余りが1以上になるが、1は素数ではないためi=2とし除外した。
この時、kは0以外の整数なら何でもよい。

今回、for文を使おうとするとなぜかエラーが出たため(書き方に不備
があったのかもしれないが解決できなかった)、while文を用いて書い
た。

Q1.無限ループの解消に悩まされた。
Q2.規則的な図形が描きやすい
Q3.特になし

m1910606 1b:○ Mon May 18 22:07:56 2020


課題1b
学籍番号:1910606 
名前:@@@牧野 広隼
提出日時:5月18日



2-c 整数nを入力し、nの素因数分解を表示する。


#include <stdio.h>     
#include <stdbool.h>

bool num(int n) {
  int i;
  for(i=2;n!=1;++i){
    while(n%i == 0){
      n /= i;
      printf("%d\n",i);
    }
  }
  return n;
}

int main(void) {
  int n,i;
  printf("n> "); scanf("%d", &n);
  num(n);
  return 0;
}

説明
forを用いて余りが0のなるものを2から順に出力していく。繰り返すた
びにその数で割っていくので、イになったら終了する。

考察
当初、sqrtを用いて素数だけを用いようとしたが、そんな複雑なことを
せずともできると気づいて今の形になった。内容的には今までの復習で
あったが苦戦してしまったように思える。

2-d 整数nを入力し、n以下の素数を出力する。

#include <stdio.h>
#include <stdbool.h>

int num(int n) {
  int i,j;
  int fl;
  for(i=2;i<=n;++i){
    fl=0;
    for(j=2;j<i;++j) {
      if(i%j == 0){
	fl=1;
	break;
      }      
    }
    if(fl==0){
      printf("%d\n",i);
    }
  }
}

int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  num(n);
  return 0;
}

説明
整数n以下のそれぞれの数について素数判定を行い、素数であれば出力
する。それぞれの数について素数であればfl=0、素数でなければfl=1と
した。

考察
このプログラムでもsqrtを用いようとしたが、これも使わないほうが見
やすくてよいと思った。mainとvoidをつ投げ忘れるという初歩的なミス
をしていたことに気づかず、時間がかかってしまった。

5. 以下のアンケートの回答。
Q1. C言語のプログラミングは好き/嫌いどちら? 理由は?
うまくプログラミングをかけた時、うれしいので好き。

Q2. epsライブラリについてどのように思いましたか。
結構様々な模様が描けるのだと思った。

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
去年習ったものを忘れているところもあるので早めになれるようにしたい。

m1910615 1b:○ Mon May 18 22:11:13 2020


第01回bレポート

学籍番号:1910615
提出日時 2020/05/18
ペア:個人作業

[課題1]
・演習 1d
 整数 n を入力し、n 以下の素数を出力する (出力の順番や形式は任意)。

・コード
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool isprime(int n);
void gatherprime(int n);

void main(void){
    int n;
    printf("n > ");scanf("%d" ,&n);
    printf("The prime numbers under n are...\n");
    gatherprime(n);
}

void gatherprime(int n){
    int count = 0;
    int i;
    for(i = 2; i <= n; ++i ){
        if(isprime(i)){
            printf("%6.d",i);
            count += 1;
        }
        if(count == 10){
            printf("\n");
            count = 0;
        }
    }
}

bool isprime(int n) {
    int i;
    int limit = (int)sqrt(n);
    for(i = 2; i <= limit; ++i) {
        if(n % i == 0) { return false; }
        }   
    return true;
}   


・説明
今回は記述を簡潔にするため、素数判定、収集、main関数をそれぞれ設
けた。math.hとstdbool.hがインクルードされているが、これは例題の
素数判定のプログラムを使用したためである。収集のプログラムでは2
以上n以下について素数判定を繰り返し、素数と判定されればcountを一
増やし、printf関数で出力されるようになっている。countは改行の為
に定義してあり、10個の数字が出力されるとcountを0に戻し改行される
ようになっている。main関数では入力のみを行っている。 見やすさの
ために桁をあけて出力している。入力が100000未満であれば整列された
結果が出力される。

・考察
改行のカウントについて、iを利用しようとしたが、出力の数と一致し
ないことに気付き、新たにカウ
ントを用意した。改行の判定方法を2種類考えていて、

1,countが10までたまった時改行してcountを0に戻す
2,countを0に戻さず、ある数で割り切れるたびに改行

を考えていた。int型の最大値に達することがないように前者を採用し
たが、2147483647まで素数判定する間に出力する数が2147483647を超え
てしまうので意味がなかった。

[課題2]
・演習2b
  数学関数、多項式による関数、その他好きな関数を 1 つ選びグラフ
  を描画する (細かい折れ線で近似する)。
 今回は5sinx,(x^3 - x)/3, 半径50の半円(下部)について行った。

・コード
#include <stdio.h>
#include <math.h>
#include "eps.h"

int f1(int i){
    double w = pow((double)i, 3);
    return ((int)w - i)/3; 
}

int f2r(int i){
    int y = (int)sqrt( 2500 - (int)pow((double)i, 2));
    return y;
}

int f2(int i){
    eps_drawline(i,- f2r(i), i + 1, - f2r(i + 1));
}


int main(void){
    eps_open("r01_2b.ps" , 100, 100);
    eps_cmd("50 50 translate");
    eps_cmd(" 0.5 setgray");
    eps_drawline(-100, 0, 100, 0);
    eps_drawline(0, -100, 0, 100);
    int i;
    eps_cmd("0.0 setgray");
    for(i = -50; i <= 49; ++i){
        eps_drawline(i, 5*sin(i), i + 1, 5*sin(i + 1));
        eps_drawline(i, f1(i), i + 1, f1(i + 1));
        f2(i);
    }
    eps_close();
}

・説明
 100×100のサイズを用意し、座標軸を0.5のグレーで描写した。その
後、x軸について幅1で近似し続け、-50~50まで行った。
 描いた関数は以下
 1,5sinx
  for文内1行目のdrawlineによるもの。簡単な関数であるため、別にsinxを設けることはしなかっ
 た。

 2,(x^3 - x)/3
  for分内2行目のdrawlineによるもの。sqrt,pow関数の型指定が面倒なのでint f1に分離した。

 3,半径50の半円
  指数の記述が難しいので、数式に関してはf2r参照。これについても型指定が面倒なので複数のステ
 ップに分離した。

・考察 
 線にある程度の幅があるので、原点付近が曖昧になってしまった。
sin関数については縦幅増強のため5倍することとした。2つ目の関数に
ついては、x=+−1でx軸と交点を持つ関数であるが、1マスを1で描写し
ているため潰れてしまった。半円は比較的綺麗に描写されたが、
drawcircleと比較すると下部に少し揺れがあるように感じる。これは
int型とdouble型を行き来させて切り捨てが発生したためであると考え
る。
 いずれの場合でも明確に描写するためには特徴づける点の間隔が広い、
または傾きの変化が緩やかな関数を使用する必要がある。例えば、(x^3
- x)/3 の-xを-16xに変えるなどである。

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
 半月ほどプログラミングに触れなかっただけで、かなり記述の仕方を
忘れていた。継続して学習をする
べきだと感じた。
Q2. eps ライブラリを使ってみてどのように感じましたか。
 eps_cmdの記述でカンマが不要なところが少し気落ち悪く感じた。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
 次回も頑張ります。

m1910616 1b:○ Mon May 18 23:23:34 2020


プログラミング通論’20 #1 – C言語の基本機能
課題1b

学籍番号:1910616
ペアの学籍番号:1910525
提出日時:2020/5/20

・演習2a
【課題の再掲】
 演習2aの(1)~(4)にあるような絵を描写する。実行後の絵は掲載せず省略する。
(1)
【コード】

// testeps.c --- demonstration of eps library.
#include <stdio.h>
#include "eps.h"
#include <math.h>

#include <stdio.h>
#include "eps.h"

int main(void) {
    eps_open("out.ps", 480, 480);
    const int w = 90;
    const int h = 90;
    const int dist = 30;

    int i;

    eps_cmd("255 0 0 setrgbcolor");
    eps_cmd("4 setlinewidth");

    for(i = 0; i < 16; i++){
      eps_num(i);
      int x = (i % 4) * (w + dist) + 15 ; int y = (i / 4) * (h + dist) + 15;
      eps_drawrect(x, y, w, h);
    }

    eps_close();
    return 0;
}

【説明】
 まず定数として自然数w, j, distをそれぞれお四角形における幅、高
さ、互いの間隔を表すものとして設定する。今回は90, 90, 30とおいた。
eps_cmdにsetrgbcolorを用いて色を赤色に設定した。次にeps_cmdに
setrgbcolorを用いて色を赤色に設定した。今回、作るべき絵の四角の
個数が16個であったので、forループを16回行わせた。「このとき、剰
余と除算を算出し、左から右に4つ敷き詰めたら、ひとつ上に進んでも
う一度左から右に敷き詰めていく形をとった。その際に(w + dist)で単
なる位置の情報に距離を付与した。その後、eps_drawrectをもって四角
を描画した。」(「」内がループの内容である)

【考察】
 16つの位置情報の具体的なセットを先に作るのではなく、数式を用い
て簡潔に記述すると便利であると分かった。また、変数x, yに値を入れ
込んでいく際に、端からの距離として15の値を直接足したが、これは単
にdistを2で割った方が良いように感じた。というのも、後々distの値
を変更するとなった際に一々変更する手間が省けるためである。

(2)
【コード】

#include <stdio.h>
#include "eps.h"

int main(void) {
    eps_open("out.ps", 480, 480);
    const int w = 90;
    const int h = 90;
    const int dist = 0;

    int i;

    eps_cmd("255 0 0 setrgbcolor");
    eps_cmd("4 setlinewidth");

    for(i = 0; i < 25; i++){
      if(i%2==0){
        int x = (i % 5) * (w + dist) + 15 ; int y = (i / 5) * (h + dist) + 15;
      eps_fillrect(x, y, w, h);
      }
    }

    eps_close();
    return 0;
}

【説明】
 (1)のコードを一部用いた。まず定数として自然数w, j, distをそれ
ぞれお四角形における幅、高さ、互いの間隔を表すものとして設定する。
今回は90, 90, 0とおいた。eps_cmdにsetrgbcolorを用いて色を赤色に
設定した。次にeps_cmdにsetrgbcolorを用いて色を赤色に設定した。今
回、作るべき絵の四角の個数が25個とみなせたので、「forループを25
回行わせた。このとき、剰余と除算を算出し、左から右に5つ敷き詰め
たら、ひとつ上に進んでもう一度左から右に敷き詰めていく形をとった。
その際に(w + dist)で単なる位置の情報に距離を付与した。その後、i
が偶数のときのみeps_drawrectをもって四角を描画した。」(「」内が
ループの内容である)

【考察】
 (1)で気付いたことに加え、forループに入れ込む数字も定数として先
に宣言した方が良いことに気付いた。また、25個の四角を作って偶数の
未描写するのではなく、各々の四角の間隔を広げて12個の四角を作成す
ることでも目的が達成されると考えられる。


【コード】

#include <stdio.h>
#include "eps.h"

int main(void) {
    eps_open("out.ps", 480, 480);
    const int r = 48;
    const int dist = 0;

    int i;

    eps_cmd("255 0 0 setrgbcolor");
    eps_cmd("4 setlinewidth");

    for(i = 0; i < 25; i++){
      if((i%5==0)||(i%5==4)||(i/5==0)||(i/5==4)){
        int x = (i % 5) * (r*2 + dist) + r; int y = (i / 5) * (r*2 + dist) + r;
      eps_fillcircle(x, y, r);
      }
    }

    eps_close();
    return 0;
}

【説明】
 まず定数としてr, distをそれぞれ円の半径、互いの円の距離として
設定した。次にeps_cmdにsetrgbcolorを用いて色を赤色に設定した。今
回作るべき円の個数は16個であったが、中心の空白に透明な円があると
みなし、25個の円を作成するつもりでforループを25回行った。「この
とき、iを5で割ったときのあまりが0, 4、あるいはiを5で割ったときの
解が0, 4のときに円をeps_fillcircleで描写した。」(「」内がループ
の内容である)

【考察】
 今回は絵の端からの間隔を変数で記したため、円の半径の変更に伴う
修正が楽であった。また、今回のように25回ループをするのではなく、
「の形をした5つの円を90度ずつ回転して4回描写することも可能である
と考えた。しかし、この場合だと値を5つも設定する必要があり、より
簡潔には書くことができないと思われる。

【コード】

#include <stdio.h>
#include "eps.h"
#include <math.h>

int main(void) {
    eps_open("out.ps", 480, 480);
    eps_cmd("240 240 translate");
    
    int x, y;
    const int a = 240;

    for(int i = 0; i < 16; ++i){
      x = a * cos(2*M_PI * i / 16);
      y = a * sin(2*M_PI * i / 16);

      eps_drawline(0,0,x,y);
    }

    eps_close();
    return 0;
}

【説明】
  まず、円を仮定し、それを分割する16本の半径を考える。まず円の
半径を定数としてaに240を代入した。forループを16回行い、「円周上
の点(x, y)のxにcos関数、yにsin関数を用いて値を割り振った。そして、
円の中心と円周上の点を繋げた。」(「」内がループの内容である)

【考察】
 16回のループを行ったが、今回も後に修正する可能性を思ってその値
を定数として先に宣言した方が良いと考えられる。cosやsin関数の引数
に定数16が存在するため、その方がより便利になると思われる。

・演習2b
【課題の再掲】
 数学関数を用いてグラフを描写する。実行後の絵は掲載せず省略する。
【コード】

// testeps.c --- demonstration of eps library.
#include <stdio.h>
#include "eps.h"
#include <math.h>

int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  const int rmd = 60;
  const int a = 100;

  for(double i = -16; i <= 16; i+=0.1) {
    eps_num(fabs(i*0.1)); eps_cmd("1.0 1.0 sethsbcolor");
    int x1 = 20 * i;
    int x2 = 20 * (i+0.1);
    int y1 = a * sin(M_PI * x1 / rmd);
    int y2 = a * sin(M_PI * x2 / rmd);
    eps_drawline(x1, y1, x2, y2);
  }

  eps_close();
  return 0;
}

【説明】
 まず、正弦波を描写することを考え、定数として波長rmd、振幅aを置
いた。forループはiを-16から16までの間で一回ごとに0.1ずつ増やして
行う。「まず始点(x1, y1)、終点(x2, y2)のうち、x座標を指定し、そ
れに対応するy座標をsin関数で指定していった」(「」内がループの内
容である)

【考察】
 完成以前、M_PI(円周率を表す)やrmdの変数をおかずにy1, y2 の値を
置いていた。この際、波の形がいびつになり、iの値の変化の具合をい
くら変えてもきれいにならなかった。そこで円周率をかけ、波長でわっ
た。rmdもx1, x2もint型であったが、M_PIはdouble型である。この二つ
の型の演算結果がdouble型になることがガタガタでなくなった原因と考
えられるが、綺麗に連続した形になった原因はM_PIやrmdといった定数
のおかげと思われる。

【アンケート】
Q1. プログラムを作るという課題はどれくらい大変でしたか?
 初回だからか今回のプログラムにはそれほど大変さを感じませんでした。
Q2. eps ライブラリを使ってみてどのように感じましたか。
 指で描くのではなく数式を指定して描写できることに便利さを感じました。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
 今回はペアプログラミングを行いましたが、それゆえか普段より素早
く課題をこなすことができました。特に、単純な変数宣言の間違いやス
ペルミス等で苦戦せずに済みました。

m1910619 1b:○ Mon May 18 23:43:57 2020


プログラミング通論 #01 課題1b

[課題の再掲1]
整数 n を入力し、2のn乗 を出力する。n ≥ 0 のときは整数、そうでな
いときは実数形式で出力すること。

[実施したこととその結果1]
作成したプログラム
//2^n                                                                           
#include <stdio.h>

double jyo(int n){
  int k=1,i=1;
  for(i=1; i<=n; ++i){
    k = k*2;
  }
  return k;
}

int main(void){
  int n;
  printf("nの値を入力して下さい\n");scanf("%d",&n);
  if(n>=0){
    printf("2のn乗=%g\n",jyo(n));}
  else{printf("2のn乗=%g\n",1/jyo(-n));
   return 0; 
  }
}

その結果
[m1910619@sol ~/pro1]$ ./a.out
nの値を入力して下さい
4
2のn乗=16
[m1910619@sol ~/pro1]$ ./a.out
nの値を入力して下さい
0
2のn乗=1
[m1910619@sol ~/pro1]$ ./a.out
nの値を入力して下さい
-5
2のn乗=0.03125
[m1910619@sol ~/pro1]$ ./a.out
nの値を入力して下さい
-2
2のn乗=0.25

[プログラムの説明と考察1]
2のn乗を計算するために,forのループを用いた.また,printfで初めて
人でもきちんと意図が伝わるように文章でで対応した.nの整数のマイ
ナスが来てもいいようにifを使い,場合分けする事でマイナスの値にも
対応させた.%dを用いる事で整数で表現した.pow(x,y)の表現でもプロ
グラムを試してみたが,うまくいかなかったのでこのプログラムを作成
した.

[課題の再掲2]
整数 n (n > 1) を入力し、n の素因数分解を表示する。

[実施したこととその結果2]
作成したプログラム
//因数分解                                                                      
#include <stdio.h>

int innsuubunnkai(int n){
  int i;
  int k;
  for(i=n-1; i>0; --i){
    if(i==1){return n;}
    else if(n%i==0){k=n/i;printf("%d\n",k);innsuubunnkai(i);break;}}
}

int main(void){
  int n;
  printf("あなたの因数分解したい数は?\n");scanf("%d",&n);
  printf("%d\n",innsuubunnkai(n));
  return 0;
}


その結果
あなたの因数分解したい数は?
6
2
3
[m1910619@sol ~/pro1]$ ./a.out
あなたの因数分解したい数は?
37
37
[m1910619@sol ~/pro1]$ 
[m1910619@sol ~/pro1]$ ./a.out
あなたの因数分解したい数は?
120
2
2
2
3
5

[プログラムの説明と考察2]
求めた数を因数分解するときに,求めたい数より大きい素因数を持つこ
とがないのでそれよりも1づつ小さくしていき,その数で割り切れたら,
その数が素因数となる.これを1になるまでforで続けていき,割り切れ
るたびに表示することで素因数分解することができる.

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
久しぶり,プログラムしたので大変でした.

Q2. eps ライブラリを使ってみてどのように感じましたか。
幾何学模様を作るのに便利だと感じました.

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
プログラミングが苦手なのでもっと頑張りたいです.

m1910620 1b:○ Mon May 18 23:30:32 2020


プログラミング通論 #1
課題1b
 
1つ目の課題
演習1-b
整数nを入力して、2のn乗を出力する。
[プログラミング]
// ruijou            
include <stdio.h>

double ruijou(int n) {
  int i;
  if (n >= 0){
    int a = 1;
    for (i = 0; i < n; ++i){
      a = a * 2;
    }
    return a;
  } else {
    double b = 1;
    n = -n;
    for (i = 0; i < n; ++i){
      b = b / 2.0;
    }
    return b;
  }
}
int main(void){
  int n;
  printf("n> "); scanf("%d", &n);
  printf("2^n = %g\n", ruijou(n));
  return 0;
}

[説明]
まず、ruijouの関数で、nが0以上の整数の時とそれ以外の場合わけで
if文を使った。そしてnが0以上の時はaに2をかけ、それ以外の場合に
は2で割るるfor文を作った。その後main関数で呼び出しプログラミング
を完成させた。

[考察]
今回はfor文を用いてプログラムを行なったが、他にもwhileループや
pow関数を用いたやり方も考えられる。


2つ目の課題
//bunkai                                                                      
#include <stdio.h>

void bunkai(int s){
int i=2;
    while(i<=s){
if(s%i==0){
    s=s/i;
    printf("%d \n",i);
    }else{
          ++i;
    }
    }
}
int main(void){
    int n;
   printf("n>");scanf("%d",&n);
   bunkai(n);
    return 0;
}
[説明]
素因数分解するために、演算子の%を用い、入力した数に対し余りが0
になった値を素因数として出力される。

[考察]
素数関係の計算は%を使うと簡単にでき、%の使い道について学ぶことができた。

[アンケート]
Q1.プログラミングを作る課題はどれくらい大変だったか?
オンラインで自宅のマイパソコンで行うと言う慣れない環境でやったた
め、慣らすのに時間が掛かった。
また久しぶりプログラミングを行ったため、トライアンドエラーの回数が多く大変だった。
Q2.epsライブラリを使ってみてどのように感じましたか?
今回の演習では使うところまで行けなかったため次回頑張りたい。
Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
久しぶりにプログラミングを組み立て、頭を使った。いいリフレッシュになったと感じた。

m1910626 1b:○ Mon May 18 23:31:17 2020


1910626
個人作業
5/18(月)
<演習1c>
整数nを入力し、nの素因数分解を表示する。
(コード)
#include <stdio.h>
#include <math.h>

int main(void) {
    int n;
    printf("n>");scanf("%d",&n);
    int yakusuu = n;
    for(int i = 2; i <= yakusuu;){
        if(yakusuu % i == 0) {printf("%d ", i);
            yakusuu = yakusuu/i;
        }
        else {i = i+1;}
        
    }
    return 0;
}
(説明)

整数nを受け取り、まずyakusuuという整数変数に格納する。その後、初
期値2の変数iでyakusuuを割り、余りが0の時は、その時のiの値とス
ペースが表示されてyakusuuはiで割られた値となる。0でない時はiの
値を1増やす。この作業がiとyakusuuの大きさが等しくなるまで行われ
るため、小さい順にnの素因数が全て表示される。
    
(考察)

初め、for内のyakusuuをnと書いていたため、例えば5をnの値として打
ち込んだ際、延々と5が表示された。iの大小関係による条件と、きち
んと変数として指定したyakusuuの関係を強く意識していなかったから
間違えたのだと思う。また、書いているうちに、小さい順に約数を書き
出すプログラミングと混同してしまいそうになったため、細かいことだ
が、変数名はyakusuuではなく、soinsuuの方が良かったと後で思った。

<演習2a(1)>
指定された絵を作成する。
(コード)
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
    for(int i = 1; i <= 4; ++i) {
        for(int j = 1; j <= 4; ++j){
    
    eps_num(0.1); eps_cmd("setgray");
    eps_cmd("2 setlinewidth");
    eps_drawrect(-190+j*40, i*40, 30, 30);
      
  }
    }
  int f1 = eps_newfont("Courier", 20);
  int f2 = eps_newfont("Helvetica", 30);
  eps_close();
  return 0;
}

(説明)
eps_drawrectによって正方形を複数規則的に並べるプログラムを作成し
た。変数iとjにより、一辺の長さ30の正方形が横に均等に長さ40ず
つ離れて4つ並んだ後、縦に40ずつ4回動かされることで、縦横4×
4個並ぶ。色の濃さは0.1と黒に近づけ、線の細さは2ptとした。

(考察)
例題中の数字を色々と変えることで意味を理解していった。正方形を縦
横に並べる方法は、2次元配列の考え方をそのまま適用した。

<アンケート>
A1.例題を参照しながら時間をかけてなんとかできたという具合で、とても大変だった。
A2.ピクセルの2次元配列による画像の表現とほぼほぼ変わらないような
気がした。やはり規則的、再起的な絵以外を描くことは大変骨が折れる
と思う。文字をきれいに描けるのが魅力的と感じた。
A3.初歩的なことでつまずくことが多かったので、きちんと復習しようと思った。

m1910627 1b:○ Mon May 18 22:01:48 2020


学籍番号 1910627
個人作業
提出日時 5月18日 22時頃
課題
演習1-c 素因数分解

プログラム
#include <stdio.h>

void write(int n){
    int k = 2;
    for(k; n != 1 ; ++k){
        if(n%k != 0){

        }
        else{
            int i;
            for(i; (n % k == 0)  ; ++i){
            printf("%d ",k);
            n = n/k;
            }
        }
    }
    printf("\n");
}

int main(void){
    int n;
    printf("n> ");scanf("%d",&n);
    write(n);

    return 0;
}
実行例
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/01$ gcc soinsuu.c
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/01$ ./a.out
n> 15
3 5 
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/01$ ./a.out
n> 57
3 19 
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/01$ ./a.out
n> 180
2 2 3 3 5 

説明
write関数が与えられた整数nの素因数分解を表示する関数である。nを
k(kは最小の素数2から始める)でnが1になるまである条件の下で割り続
ける。その条件は、nをkで割った時余りが0のときである。kで割る際、
nをkで割った余りが0である限りkは変えず、0でなくなったらkを1増や
す。1回kで割る毎に1回kをprintfで打ち出すことで素因数を打ち出して
いる。nをkで割り続けることで最終的にkがnの素因数の中で最大の数に
なる。この時nをkで割ればnは1になり大本のループは終わる。

考察
素因数は2以外奇数となるのだから、最初に2の何乗かを調べた後にこの
プログラムにおいてkを3から2ずつ増やして割り続けるほうが処理の効
率が上がりそうだなと考える。このプログラムは紙にアルゴリズムを書
いてそれをプログラムにしたのだが、その際nを1度kで割ったらすぐkを
増やしてしまい累乗がうまくいかなくなった。自分ではうまくできたよ
うなアルゴリズムも間違っていることが大いにあることを想定しなけれ
ばと思った。


2つめの課題
演習1-D
整数n以下の素数を打ち出すプログラム

#include <stdio.h>
#include <math.h>
#include <stdbool.h>

void primewrite(int n){
    
    int i ,j;
    for(i = 2; i <= n; i++){
       
        int b = (int)sqrt(i);
        int flag = 1;

        for(j = 2; j <= b; j++){

            if( i % j == 0){flag = 0;}
        }

        if(flag == 1){printf("%d ",i);}
    }     
            
    
    printf("\n");
}

int main(void){
   int n;
   printf("n> ");
   scanf("%d",&n);

  primewrite(n);
   
   
   
   
    return 0;
}

実行例
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/01$ gcc sosuu2.c -lm
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/01$ ./a.out
n> 15
2 3 5 7 11 13 
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/01$ ./a.out
n> 19
2 3 5 7 11 13 17 19 
m1910627@DESKTOP-QOKSOM0:/mnt/c/Users/みずたかずし/Documents/prog/01$ ./a.out
n> 2
2 

説明
おおまかな方針としては、2からnまで一つずつ素数かどうかを調べ、素
数だったら出力するプログラムを組んだ。まずiが2から1ずつ増えてい
き、各々のiについて素数かどうかを調べている。あるiについて、j(2
からi^(1/2)まで)で割り続け余りが全て0じゃない場合iは素数なので、
打ち出す。

考察
これも素数が絡むので、iは2だけ独立で扱い、3以降はiを2づつ増やし
続けるほうが処理の効率はよさそうだなと考えた。ただその場合nが偶
数のときk+2がnを超えるのでその場合n-1までをループで調べ、nは単体
で調べることになりそうだなと考える。

アンケート

Q1. プログラムを作るという課題はどれくらい大変でしたか?

まだifやforしか使ってないのでそこまで大変ではない。

Q2. eps ライブラリを使ってみてどのように感じましたか。

forやwhile文が便利だなと感じた。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。

優秀なアルゴリズムどころか要求を満たす最低限のアルゴリズムでさえ
作るのは一苦労だなと感じた。手書きでアルゴリズムを作る練習を増や
そうと思っています。

m1910629 1b:○ Mon May 18 04:34:59 2020


学籍番号:1910629
個人作業,提出日:5/16

課題1:演習1d:整数nを入力し,n以下の素数を出力する
--以下コード
#include <stdio.h>
#include<math.h>

int isprime(int i){
    int j;
    for(j=2;j<i;j++){
        if(i%j == 0){
            return 0;
        }
    }
    return 1;
}

int main(void) {
  int n;
  int i;
  printf("n> "); scanf("%d", &n);
  for(i=2;i<=n;i++){
      if(isprime(i)){
          printf("%d ",i);
      }
  }
  return 0;
}

説明:main関数では入出力と,nまでの整数で素数を判定する関数を呼び出している.
1は素数ではないため,2からforループを始めている.
isprime関数では素数かどうかの判定をしている.i未満の整数jのいず
れかについてiがjで割り切れる,つまりi mod jが0になれば0を返して
いる.0を返すことがなければ1が返る.
C言語の条件式は0をfalse,0以外をtrueとするため,if文が成立している.

考察:素数判定法としてエラトステネスの篩を最初に考えたが,これは
入力する整数の最大値が決まっていないとC言語ではうまくいかない場
合があるため,試し割り法で素数を書き出した.

初めはmain関数に全て書こうとしていたが,素数判定の部分も含めると
forの階層が増えてコードが見にくくなるので,isprimeの部分を新たな
関数に移した.

また,for文を条件によって途中で切りたいときはbreak;でも良いが,
関数を別にして条件によって値を返すと見やすくて処理も少なくなる.

課題2:演習2b:epsライブラリを用いて好きな函数をグラフにする.
--以下コード
#include <stdio.h>
#include<math.h>
#include"eps.h"

int main(void) {
    int i;
    double x;
    eps_open("out.ps",480,480);
    eps_cmd("240 240 translate");
    for(i=-200;i<200;i+=10){
        eps_cmd("1.0 0.0 0.0 setrgbcolor");
        eps_drawline(i,200*sinh((double)i/100),i+10,200*sinh((double)(i+10)/100));
        eps_cmd("0.0 1.0 0.0 setrgbcolor");
        eps_drawline(i,200*cosh((double)i/100),i+10,200*cosh((double)(i+10)/100));
        eps_cmd("0.0 0.0 1.0 setrgbcolor");
        eps_drawline(i,200*tanh((double)i/100),i+10,200*tanh((double)(i+10)/100));
    }
    eps_close();
    return 0;
}

説明:まずeps_cmdで画像中心に原点を移動し,そこからfor文でiを10ず
つ-200から200まで変えながら40回ループさせて折れ線を描画した.for
文内はそれぞれ色を指定した後にsinh,cosh,tanhをmath.hの関数で描
画した.

考察:双曲線函数の関数の引数をキャストしていないと,かなり大雑把
な折れ線が描画されたので,doubleへキャストしたら滑らかになった.

アンケート:
Q1:プログラムを作ること自体は今のところ簡単だが,

m1910631 1b:○ Mon May 18 19:30:08 2020


プログラミング通論レポートb#1

<課題1>
演習1e ある数列の指定された項番目までを出力するプログラム

<コード>
#include <stdio.h>
int fibonacci_series[1000] = {1, 1};

int fibonacci(int n){
    if (fibonacci_series[n] != 0){
        return fibonacci_series[n];
    }
    int new_number = fibonacci(n-1) + fibonacci(n-2);
    fibonacci_series[n] = new_number;
    return new_number;
}

int main(){
    int n;
    printf("n: "); scanf("%d", &n);
    fibonacci(n - 1);
    for (int i = 0; i < n; ++i){
        printf("%d, ", fibonacci_series[i]);
    }
    printf("\n");
    return 0;
}

<説明>
数列を出力するのに前覚えたメモ化再帰と相性がいいなと思いフィボナッ
チ数列を出力するプログラムにした。数列格納用の配列は0番目と1番目
以外を0で初期化して、0を未計算の目印として扱うようにした。出力は
コンマ区切りにした。

<考察>
とりあえず配列の大きさは1000にしたが、実際には1000以上番目の項が
知りたいときもあると考えられる。配列の大きさを最初に決めなくては
いけない言語かつはっきりした制限もない場合こういうところで何を基
準に決めるのだろうかと思った。

<課題2>
演習2a(2) 市松模様の画像を生成するプログラム

<コード>

#include <stdio.h>
#include "eps.h"

int main(void) {
    eps_open("out.ps", 500, 500);
    for (int x = 0; x < 5; ++x){
        for (int y = 0; y < 5; ++y){
            if ((x + y) % 2 == 0){
                double color = (x+1)*0.1 + (y+1)*0.1;
                if (color == 1){color = 0.99;}
                eps_num(color); eps_cmd("setgray");
                eps_fillrect(x*100, y*100, 100, 100);
            }
        }
    }
    eps_close();
}

<説明>
模様を5*5の升目に見立て、その升目の模様の行と列に0-4の番号をつけ、
2重ループで行番号と列番号の和が偶数になったときに色づけるように
した。色は自由とのことだったので、行番号と列番号に関連付けてグレー
の濃度が変わるようにした。そのままだと端っこの色が白になってしま
うので端っこだけグレーの濃度を手動で設定した。

<考察>
グレーの濃度を替える過程で、最初は(x+1)*0.1と書いてあるところを
(x+1)/10としていたのだが、切り捨てで結果が0になってしまいグラデー
ションがかからなかった。変数を使わないときも型を意識する必要があっ
た。

<アンケート>
A1. 今回はそんなに大変ではなかった。
A2. 仕組みが分かりにくかった。
A3. postscriptの扱いが少しわかった。

m1910632 1b:○ Mon May 18 02:56:29 2020


レポート1B


選択した課題: 1d - n以下の素数を列挙する

方針:
次の3つの方法で素数を列挙した。
1. 2 <= i <= n の各iに対して2〜sqrt(i)まで試し割りする。
2. 1.と似た方法だが見つかった素数をメモしておき、それを使って試し割りする。
3. エラトステネスのふるいを使う。

コード:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>

void print_prime_01(int n) {
    for(int i = 2; i <= n; ++i) {
        bool isprime = true;
        for(int j = 2; j * j <= i; ++j) {
            if(i % j == 0){
                isprime = false;
                break;
            }
        }
        if(isprime) printf("%d ", i);
    }
    printf("\n");
}

void print_prime_02(int n) {
    if(n <= 1) return;
    int *p = malloc(sizeof(int)*n);
    int front = 1;
    p[0] = 2;
    printf("2 ");
    for(int i = 3; i <= n; ++i) {
        bool isprime = true;
        for(int j = 0; p[j] * p[j] <= i; ++j) {
            if(i % p[j] == 0){
                isprime = false;
                break;
            }
        }
        if(isprime){
            printf("%d ", i);
            p[front++] = i;
        }
    }
    printf("\n");
    free(p);
}

void print_prime_03(int n) {
    bool *isprime = malloc(sizeof(bool)*(n+1));
    for(int i = 0; i <= n; ++i) {
        isprime[i] = true;
    }
    
    isprime[0] = isprime[1] = false;
    for(int i = 2; i <= n; ++i) {
        if(!isprime[i]) continue;
        for(int j = i*2; j <= n; j += i) {
            isprime[j] = false;
        }
    }
    for(int i = 0; i <= n; ++i) {
       if(isprime[i]) printf("%d ", i);
    }
    printf("\n");
    free(isprime);
}

int main(void) {
    printf("n> ");
    int n; scanf("%d",&n);
    
    void (*f[])(int) = {
        print_prime_01,
        print_prime_02,
        print_prime_03
    };
    
    for(int i = 0; i < 3; ++i) {f[i](n);}

    return 0;
}

解説:
[main関数]
まず変数nを標準入力から受け取る。
関数ポインタの配列fを作り、3つの素数列挙関数を入れる。
これをfor文で回してそれぞれの関数を実行する。

[print_prime_01]
まずnを受け取り2からnまでループする。
2 <= i <= nのそれぞれで、2からsqrt(i)までiを試し割りし、一つも割
り切れなければそれは素数なので出力する。
これを繰り返す。

[print_prime_02]
まずnを受け取り要素数nの配列pを作る。
pには見つかった素数を入れていく。
p[0]には2を入れ、配列の末尾を示す変数としてfront = 1を用意しておく。

2 <= i <= nのそれぞれで、p[j] <= sqrt(i)となる全てのp[j](素数)で試し割りする。
どれか一つで割り切れれば素数ではないので次のループに移る。
一つも割り切れなければそれは素数なので出力する。
また、素数リストpの末尾(p[front])にiを代入する。frontに1を足して次のループに移る。

最後に配列のメモリを解放して関数を抜ける。

[print_prime_03]
まず変数nを標準入力から受け取り、関数print_primeに渡す。
print_primeでは次の操作を行う。
まず添字が0〜nのサイズn+1のbool配列isprimeを作る。
isprimeはisprime[0]とisprime[1]をfalse, 他はtrueで初期化しておく。

次にi=2,3,...,nにおいて以下の操作を行う。
まずisprime[i] == false、つまり素数でなければ次のループへ移る。
trueであれば
    isprime[i * k] = false (k = 2, 3, 4, ..., n/i)
とする。つまりiを除いたiの倍数を全て「素数ではない」とする。
これを終えたら次のループに移る。

ループを抜けたらisprime[i]がtrueのものを全て出力する。
最後に配列のメモリを解放して関数を抜ける。

実行例:

[入力例1]
100
[出力例1]
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

[入力例2]
13
[出力例2]
2 3 5 7 11 13
2 3 5 7 11 13
2 3 5 7 11 13

[入力例3]
2
[出力例3]
2
2
2

考察:
それぞれの計算量を考えたい。
ここでsqrt(x)はxの正の平方根を表す。

1番目の方法:
n回素数判定をするのでO(sqrt(1)+sqrt(2)+...+sqrt(n))時間かかる。sqrt(x)を0からnまで積分すると2/3n*sqrt(n)となり、だいたいO(n*sqrt(n))の計算量だと考えられる。

2番目の方法:
素数定理よりn以下の素数の数はだいたいn/lognであることが知られている。
sqrt(i)以下の数について試し割りするので、sqrt(i)/log(sqrt(i)) =
2*sqrt(i)/log(i) となり、全体では O(sqrt(2)/log(2) +
sqrt(3)/log(3) + ... + sqrt(n)/log(n)) になる。
sqrt(i)/log(i)については和を求めることも不定積分することもできな
いので、分かりやすくオーダーを求めることができなかった。
しかし大きなiでは明らかに、sqrt(i) > sqrt(i)/log(i) であるので、
1番目の方法よりは効率が良いことはわかった。

3番目の方法:
エラトステネスのふるいは各素数についてその倍数をO(n/2 + n/3 +
n/5 + n/7 + n/11 + ... + 1)となる。素数の逆数和のオーダーは
O(log(log(n)))であることが知られている(*)ので計算量は
O(nlog(log(n)))となり1番目の方法よりも高速に計算ができることがわ
かった。
(*参考: 素数の逆数和が発散することの証明 | 高校数学の美しい物語
https://mathtrain.jp/primeinverse 2020/5/17アクセス)

具体的にどれくらいの実行時間がかかるのかが気になったので、main関
数を次のように書き換えて様々なnで実行した。

int main(void) {
    printf("n> ");
    int n; scanf("%d",&n);
    
    void (*f[])(int) = {
        print_prime_01,
        print_prime_02,
        print_prime_03
    };
    
    for(int i = 0; i < 3; ++i) {
        clock_t start = clock();
        f[i](n);
        double time = (double)(clock() - start)/CLOCKS_PER_SEC*1000;
        printf("f%d: %.2f[ms]\n", i+1, time);
    }
    
    return 0;
}

実行結果は次のようになった。ただし素数の出力はコメントアウトで止
めたため、printfの分の実行時間はここに含まれていない。

n> 500
f1 time: 0.03[ms]
f2 time: 0.06[ms]
f3 time: 0.01[ms]

n> 1000
f1 time: 0.05[ms]
f2 time: 0.06[ms]
f3 time: 0.02[ms]

n> 10000
f1 time: 0.49[ms]
f2 time: 0.35[ms]
f3 time: 0.10[ms]

n> 100000
f1 time: 14.36[ms]
f2 time: 4.42[ms]
f3 time: 1.17[ms]

n> 1000000
f1 time: 217.58[ms]
f2 time: 61.11[ms]
f3 time: 12.71[ms]

n> 10000000
f1 time: 5288.19[ms]
f2 time: 1085.41[ms]
f3 time: 130.66[ms]

n> 20000000
f1: 14262.78[ms]
f2: 2724.97[ms]
f3: 317.11[ms]

小さいnでは素数をメモする試し割りより愚直に試し割りする方が速い
ことがわかった。これはメモリの確保や、配列のアクセスにかかる時間
が関係していると考えた。しかしnが大きくなるにつれその差も無視で
きるようになったからか、n=20000000では愚直な試し割りよりも5倍近
く高速に動作していた。

一方エラトステネスのふるいは前の2つよりもさらに高速に動作してい
た。素数が確定した瞬間から合成数とわかった候補を消していくことで
無駄な試し割りをしなくて済むことが影響していると考えた。

同じ動作をする関数でも動作は遅いが正しいことが明らかな関数と、動
作が高速だが正しいかどうかが分かりにくい関数の両方を作って同時に
実行することで、高速なアルゴリズムの確かさ確認することができるの
ではないかと考えた。実際に今回は愚直な試し割りと他2つの関数を同
時に動かして、どのパターンでも同じ答えを出していたことから、その
2つが正しいことを確認することができた。

-----

選択した課題: 2b - 好きな数学関数を描画する

方針:
まず関数f(x)を描画するために微小な値dxを定めて点(x, f(x))から
(x+dx, f(x+dx))に線を引くことを繰り返すことを考えた。
グラフだけだと分かりづらいのでx軸y軸も描画することにした。
またHSBカラーコードを利用してグラフの色を虹色に塗ることにした。
更に描画する関数は単射であるy=f(x)の形をしたグラフなら何でも描け
るように作ることにした。

コード:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include "eps.h"

typedef struct {
    double r, g, b;
} color;

const color BLACK = {.r = 0, .g = 0, .b = 0};

int paper_width = 600;
int paper_height = 800;

color rgb_to_color(int r, int g, int b) {
    color c;
    const double RGB_MAX = 255;
    c.r = r/RGB_MAX;
    c.g = g/RGB_MAX;
    c.b = b/RGB_MAX;
    return c;
}

void setcolor(color c) {
    char s[100];
    sprintf(s, "%f %f %f setrgbcolor", c.r, c.g, c.r);
    eps_cmd(s);
}

typedef struct {
    double x_from, x_to, y_from, y_to, w, h;
    double line_width;
    bool y_infinity;
    int x_step, y_step;
} plotconfig;

typedef struct {
    double dh, h;
} rainbow;

plotconfig set_range(double xf, double xt, double yf, double yt, double w, double h) {
    plotconfig s;
    s.x_from = xf;
    s.x_to = xt;
    s.y_from = yf;
    s.y_to = yt;
    s.w = w;
    s.h = h;
    return s;
}

void next_rainbow(rainbow *r) {
    r->h += r->dh;
    r->h -= floor(r->h);
}

void draw_axis(plotconfig pc) {
    setcolor(rgb_to_color(100, 100, 100));
    
    eps_drawline(-pc.w/2, 0, pc.w/2, 0);
    eps_drawline(pc.w/2, 0, pc.w/2 - 10, 5);
    eps_drawline(pc.w/2, 0, pc.w/2 - 10, -5);
    
    eps_drawline(0, -pc.h/2, 0, pc.h/2);
    eps_drawline(0, pc.h/2, 5, pc.h/2 - 10);
    eps_drawline(0, pc.h/2, -5, pc.h/2 - 10);
    
    double w = pc.w / (pc.x_to - pc.x_from);
    double h = pc.h / (pc.y_to - pc.y_from);
    int font = eps_newfont("Helvetica", 10);
    
    eps_puts(font, 5, 5, "O");
    eps_puts(font, pc.w/2+5, 0, "x");
    eps_puts(font, 0, pc.h/2+5, "y");
    
    for(int i = ceil(pc.x_from); i <= floor(pc.x_to); i++) {
        if(i == 0) continue;
        
        if(i % pc.x_step == 0) {
            char c[5];
            sprintf(c, "%d", i);
            eps_puts(font, w*i, 10, c);
            if(i == pc.x_to) continue;
            eps_drawline(w*i,5,w*i,-5);
        }
    }
    
    for(int i = ceil(pc.y_from); i <= floor(pc.y_to); i++) {
        if(i == 0) continue;
        
        if(i % pc.y_step == 0) {
            char c[5];
            sprintf(c, "%d", i);
            eps_puts(font, 10, h*i, c);
            if(i == pc.y_to) continue;
            eps_drawline(5,h*i,-5,h*i);
        }
    }
    
    setcolor(BLACK);
}

void plot_graph(double dx, double (*f)(double), plotconfig pc) {
    double w = pc.w / (pc.x_to - pc.x_from);
    double h = pc.h / (pc.y_to - pc.y_from);
    
    eps_num(pc.line_width); eps_cmd("setlinewidth");
    rainbow r = {.dh = 0.01, .h = 0};
    for(double x = pc.x_from; x <= pc.x_to; x += dx) {
        next_rainbow(&r);
        eps_num(r.h); eps_cmd("0.8 1 sethsbcolor");
        
        double fx = f(x), fxdx = f(x + dx);
        if(isnan(fx) || isnan(fxdx)) continue;
        if(!pc.y_infinity) {
            if(fx < pc.y_from || pc.y_to < fx) continue;
            if(fxdx < pc.y_from || pc.y_to < fxdx) continue;
        }
    
        eps_drawline(w*x, h*fx, w*(x+dx), h*fxdx);
    }
}

double f(double x) {
    return (x+4)*(x+3)*(x-1)*(x-2)*(x-3);
}

int main(void) {
    eps_open("out.ps", paper_width, paper_height);
    
    eps_num(paper_width/2.0);
    eps_num(paper_height/2.0);
    eps_cmd("translate");
    
    plotconfig pc;
    pc = set_range(-10, 10, -200, 200, 400, 400);
    pc.y_infinity = false;
    pc.line_width = 2;
    pc.x_step = 3;
    pc.y_step = 25;
    
    draw_axis(pc);
    plot_graph(0.01, f, pc);
    
    eps_close();
    printf("finished!\n");
    return 0;
}

解説:
作ったものを箇条書きでここに列挙する。

- 構造体
    - color:
        R, G, Bの値を0から1の範囲で保持する。
        
    - plotconfig:
        グラフを描画するときのオプションの値を保持する。
        x_from    : x軸の左端の値
        x_to      : x軸の右端の値
        y_from    : y軸の左端の値
        y_to      : y軸の右端の値
        w         : グラフ描画範囲の横の長さ
        h         : グラフ描画範囲の縦の長さ
        line_width: 線の太さ
        y_infinity: trueのとき、関数を描画でhの制限を守らない
        x_step    : x軸に数字(印)を振る間隔(整数値)
        y_step    : y軸に数字(印)を振る間隔(整数値)
        
    - rainbow:
        虹色を描画するためにHSBカラーコードのHの値と次のHとの差の値dhを保持する。
        
- 定数
    - BLACK: (r,g,b)=(0,0,0)の構造体color
    
- グローバル変数
    - paper_width: 紙の幅
    - paper_height: 紙の高さ
    
- 関数
    - color rgb_to_color(int r, g, b):
        0から255のRGBの値を構造体colorに変換する。
        
    - void setcolor(color c):
        受け取った色cをsetrgbcolorコマンドで適用する。
        
    - plotconfig set_range(double xf, xt, yf, yt, w, h):
        横軸を xf <= x <= xt, 縦軸を yf <= y <= yt, グラフの描画
        範囲を横w, 縦hにするよう設定した構造体plotconfigを返す。
        
    - void next_rainbow(rainbow *r):
        受け取った構造体rainbowのhにdhを加算する。1を超えたとき
        はfloor(h)をひき、必ず0から1の範囲に収まるようにする。
        
    - void draw_axis(plotconfig pc):
        受け取った描画設定に合わせて軸を描画する。
        
    - void plot_graph(double dx, double (*f)(double), plotconfig pc):
        受け取った描画設定に合わせて関数fを描画する。
        
    - double f(double x)
        描画するための数学関数を定義する。
        
【main関数】
まず、main関数で"out.ps"を開く。
次にtranslateコマンドで紙の中央を原点に設定する。
次に構造体plotconfigを作り、描画範囲や線の太さを設定する。

【draw_axis】
次に関数draw_axisで軸を描画する。
draw_axisではまず色を灰色に設定する。(R,G,B)=(100,100,100)
受け取った構造体plotconfigで定義される描画範囲を縦に半分、横に半
分に分割するように線を引き、矢印を描画する。
次にx軸の左端から右端まで、整数値をとるところに印をつける。このとき
    ceil(pc.x_from) <= x <= floor(pc.x_to)
の範囲で操作を行うことで印が軸をはみ出ないようにした。また、xが
plotconfigのx_stepで割り切れるとき、そこに数字と印を振るようにし
た。
この操作をy軸でも同様に行なった。

【plot_graph】
次に関数plot_graphでグラフを描画する。
線を引く間隔として微小な値dx, doubleを受け取りdoubleを返す関数オ
ブジェクトf, plotconfig pcを受け取る。
まずsetlinewidthコマンドでpc.line_widthを反映させる。
次に構造体rainbowをh=0, dh=0.01として設定する。
次にfor文でpc.x_fromからpc.x_toの範囲までdxの間隔で次の操作を実行する。

まず、rainbowの構造体rをnext_rainbow(&r)して、虹色の値を更新する。
h=0, dh=0.01のときはh=0.01に更新され、h=0.99, dh=0.01のときはh=0
に更新される。
そしてsethsbcolorコマンドで描画に使う色を(h,s,b)=(h,0.8,1)に更新する。
次にf(x)とf(x+dx)を計算し、これらの値が非数NaNあれば描画できないので次のループへ移る。
またpc.y_infinity(縦の描画範囲を無視するかどうかのオプション)が
falseのとき、f(x)とf(x+dx)の値のどちらかが縦の描画範囲を超えてい
れば、これも描画できないので次のループに移る。
最後に点(x, f(x))から(x+dx, f(x+dx))に線を引き、次のループに移る。

これらの操作を繰り返すことで指定した関数f(x)を描画することができる。
また、fは関数オブジェクトで渡せるようにすることでmath.hにあるよ
うなcosやlogもそのまま代入できるようにした。

考察:
虹色を描画するときにRGB値で何とか作れないか考えたが、赤・緑・青
が独立している以上、ここから綺麗な虹色を作り出すことは難しいと思
い、もう一つのHSB値について見てみた。するとHが色相を表しており、
これを0から1まで徐々に変えていくことで虹色を作り出せると分かり
HSB値を採用した。このように色空間には特徴があり、表現したい色の
性質によりこれを使い分けると良いとわかった。

関数を描画するときに点(x, f(x))から点(x+dx, f(x+dx))まで直線を引
く作業を繰り返すとうまく綺麗なグラフになってくれることがわかった
が、この方法には弱点があることにも気がついた。

例えば f(x) = sin(2^x) のとき x > 0 2^xが爆発的に増大しsinの周期
が短くなる。この周期がdxより短くなったときxとx+dxの間の変化を無
視して線を結んでしまうためグラフに間違えが発生してしまった。各x
において点をとる方法も考えられるが、やはりこれも周期がdxより小さ
くなるとグラフに間違えが起こると考えられる。
したがって、このxとx+dxの間を補完する方法には限界があることがわかった。

ここではグラフ描画の細かい設定を構造体で保持する方法をとった。こ
の方法を取ることで、毎回引数に大量のデータを書かなくても何度も同
じ設定を使い回すことができるようにすることができた。これにより同
じ関数を何度も使い回しできるようになった。
-----

アンケート:

Q1. プログラムを作るという課題はどれくらい大変でしたか?
どちらかというとプログラムを作ることは楽しいのであまり大変さは感
じなかった。

Q2. epsライブラリを使ってみてどのように感じましたか。
C言語の計算と組み合わせることでいろいろ面白いことができると感じられた。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
C言語の復習ができた。次回からも頑張っていきたい。

m1910636 1b:○ Mon May 18 22:14:59 2020



演習2-a (1)

#include<stdio.h>
#include"eps.h"//ヘッダ読み込み
#define NUMBER_OF_RECT 4//rectの数
#define WIDTH 480//画面縦横
#define HEIGHT 480
#define MARGIN 20//マージン
//せっかくなので
int main(){
    if(MARGIN*2>=WIDTH||MARGIN*2>=WIDTH){//マージンが大きすぎる
        fprintf(stderr,"マージンが大きすぎるか画像サイズが小さすぎます");
        return -1;//エラーで終了
    }
    int width=WIDTH-MARGIN*2;//左右のマージンを除いたキャンバスサイズ
    int height=HEIGHT-MARGIN*2;
    int rectWidthPlusMargin=(int)width/NUMBER_OF_RECT ;//どれだけおきに四角を描くかを計算
    int rectHeightPlusMargin=(int)height/NUMBER_OF_RECT ;
    int rectWidth=(int)rectWidthPlusMargin*2/3;//四角形の幅,高さ
    int rectHeight=(int)rectHeightPlusMargin*2/3;
    eps_open("test2.ps",WIDTH,HEIGHT);
    eps_num(MARGIN);
    eps_num(MARGIN);
    eps_cmd("translate");
    for(int r=0;r<NUMBER_OF_RECT;r++){
        for(int c=0;c<NUMBER_OF_RECT;c++){
            eps_drawrect(c*rectWidthPlusMargin+rectWidthPlusMargin/6,r*rectHeightPlusMargin+rectHeightPlusMargin/6
            ,rectWidth,rectHeight);
        }
    }
    eps_close();
}

説明:
 #defineを用いて画像の幅,高さ,4方向のマージン,四角形の数を定
義し,これを変えても画像内のバランスが崩れないように注意してコー
ドを書いた.また,マージンが大きすぎたときはエラーで終了するよう
にmainの最初に判定した.その後,マージンを除いた画像のサイズを計
算し,四角形同士の間の長さを含めて,何ピクセルごとに四角形を描け
ばいいかを縦横それぞれ計算し,二重forループで描いた.
考察:
 せっかくなので,画像の縦横のサイズをいろいろ変えたり,四角形の
数を変えたりして実験してみたが,正しく均等に四角形が描かれた.最
初に画像のサイズなどの数字を変数やマクロで定義しておくと後から変
更するのが楽だった.

演習2-a (2)

#include<stdio.h>
#include"eps.h"//ヘッダ読み込み
#define NUMBER_OF_RECT 5//rectの数
#define WIDTH 480//画面縦横
#define HEIGHT 480
int main(){
    int rectWidth=(int)WIDTH/NUMBER_OF_RECT ;//四角形の幅,高さを計算
    int rectHeight=(int)HEIGHT/NUMBER_OF_RECT ;
    eps_open("test2a2.ps",WIDTH,HEIGHT);
    
    for(int r=0;r<NUMBER_OF_RECT;r++){
        for(int c=0;c<NUMBER_OF_RECT;c++){
            if(!((r+c)%2)){//白黒模様
                eps_fillrect(c*rectWidth,r*rectHeight,rectWidth,rectHeight);
            }
        }
    }
    eps_close();
}

説明: 
 ほぼさっきのコードを流用して書いた.今回は白黒模様の端を画像の
端にくっつけるのでマージンは定義しなかった.まず画像のサイズと四
角形の数から四角形の幅と高さを計算し,後は二重forループを用い,
縦と横の繰り返しの回数が偶数のとき黒い四角を描くようにした.
考察:
 白黒模様を描くときに,if((r+c)%2==0){ と描くつもりが 
if((r+c)%2){ となっていたことに気づかず実行していたが,0が
false,それ以外がtrueという性質を偶然利用してうまく機能していた.
その後に白黒を入れ替えたくなったので条件式全体を括弧で囲んで!で
否定した.
 こちらも画像のサイズなどをいじってもいい感じに表示されるように
した.やはりWIDTH,HEIGHTを先に定義しておくとやりやすかった.

演習2-c 
 我が故郷,美しき富山県(富山市)の4/1~4/30の気温(最高,最低,
 平均)及び降水量のグラフを描いた.気温は推移がわかりやすいよう
 に折れ線グラフにし,降水量は棒グラフにした.なお,各データは気
 象庁のホームページからダウンロードし,toyama_data.csvというファ
 イルに","で区切って年,月,日,最高気温,最低気温,平均気温,降水量,
 降水の有無(0or1)のように入っている.
 
#include<stdio.h>
#include"eps.h"
#define WIDTH 1600
#define HEIGHT 900
#define MARGIN 50
#define MAX_STR 256//一行の最大の長さ
#define BOU_LENGTH 8 //棒グラフの高さの係数,テキトー
int getLines(FILE* fptr){//最初にデータの行数を取得
    char buf[MAX_STR];
    int lineCount=0;
    while(fgets(buf,MAX_STR,fptr)!=NULL){
        lineCount++;
    }
    //printf("%d\n",lineCount);//printfデバッグ
    return lineCount;
}
int drawLineGraph(int temp,int prevTemp,double x,double d,int r,int g,int b){//折れ線グラフを描く
    double y=540+(temp*9);
    double py=540+(prevTemp*9);
    eps_num(r);
    eps_num(g);
    eps_num(b);
    eps_cmd("setrgbcolor");
    eps_drawline(x,y,x-d,py);
    eps_fillcircle(x,y,10);
}
int main(){
    char* file_name="toyama_data.csv";//csvからデータを読み取る
    FILE* fptr=fopen(file_name,"r");//ファイルを読み取りモードで開く
    if(fptr==NULL){//失敗
        fprintf(stderr,"failed to open a file");
        return -1;
    }

    int lines=getLines(fptr);//行数を取得
    fclose(fptr);
    //一回閉じてまた開く,これ無駄な気もするけど
    fptr=fopen(file_name,"r");
    int year,month,date,prevYear,prevMonth,prevDate;
    float avr_temp,h_temp,l_temp,prevAvr,prevH,prevL;
    int precipitation,prevPrecipitation;//年月日気温(平均最高最低)降水量
    char buf[MAX_STR];
    
    double x=MARGIN;
    double deltaX=(WIDTH-MARGIN*2)/lines;//幅とか
    //x+=deltaX;

    eps_open("eps2c.ps",WIDTH,HEIGHT);
    fgets(buf,MAX_STR,fptr);//1回目はprevが無い関係で別処理
    sscanf(buf,"%d,%d,%d,%f,%f,%f,%d",&year,&month,&date,&avr_temp,&h_temp,&l_temp,&precipitation);
    drawLineGraph(h_temp,h_temp,x,deltaX,255,0,0);
    drawLineGraph(l_temp,l_temp,x,deltaX,0,0,255);
    drawLineGraph(avr_temp,avr_temp,x,deltaX,0,0,0);
    eps_cmd("0 0 0 setrgbcolor");
    eps_fillrect(x-deltaX/2,0,deltaX,precipitation*BOU_LENGTH);

    x+=deltaX;
    prevAvr=avr_temp;//prevの更新
    prevH=h_temp;
    prevL=l_temp;

    while(fgets(buf,MAX_STR,fptr)!=NULL){//29日分ループ
        sscanf(buf,"%d,%d,%d,%f,%f,%f,%d",&year,&month,&date,&avr_temp,&h_temp,&l_temp,&precipitation);//値の取得
        /*ここから折れ線グラフ*/        
        drawLineGraph(h_temp,prevH,x,deltaX,255,0,0);
        drawLineGraph(l_temp,prevL,x,deltaX,0,0,255);
        drawLineGraph(avr_temp,prevAvr,x,deltaX,0,0,0);
        prevAvr=avr_temp;//prevの更新
        prevH=h_temp;
        prevL=l_temp;
        
        /*ここから棒グラフ*/
        eps_cmd("0 0 0 setrgbcolor");
        eps_fillrect(x-deltaX/2,0,deltaX,precipitation*BOU_LENGTH);
        x+=deltaX;
    }
    eps_cmd("255 0 0 setrgbcolor");//あとは文字とか
    int f = eps_newfont("Helvetica", 30);    
    eps_puts(f, WIDTH/2, HEIGHT-100, "TOYAMA 4/1~4/30"); 
    eps_puts(f, WIDTH-300, 500, "Highest temperature"); 
    eps_cmd("0 0 255 setrgbcolor");
    eps_puts(f, WIDTH-300, 450, "Lowest temperature"); 
    eps_cmd("0 0 0 setrgbcolor");
    eps_puts(f, WIDTH-300, 400, "Average temperature"); 
    eps_puts(f, WIDTH-300, 100, "Precipitation"); 

    fclose(fptr);
    eps_close();   
}

説明:
 main関数内,まずデータファイルを読み込み,失敗したらエラーを返
すようにした.そして,getLines関数内でその総行数を求めた.一度ファ
イルを閉じてもう一度開き,今度はデータを読み取りながら点,折れ線,
棒グラフをwhile文で繰り返し描いていった.最後の行まで読み取って
これ以上読み取れなくなったら終了し,文字などを最後に書き込んで終
了した.

考察:
 外部ファイルの扱いで,読み込みなのでfopen("ファイル名","r");と
したが,これを'r'とするとエラーが出た.文字列と一文字の違いがエ
ラーを生んだのだと考えられる.また,epsライブラリには"wb"とあっ
たが,"w"との違いはバイナリがどうこうということを聞いたことがあ
るが十分に理解しているわけでは無いのでいずれ調べてみたい.また,
ファイルを開いて行数を調べて閉じてまた開いてデータを読むという作
業を行ったが,これももっと簡潔に,ファイルを一回開くだけで行う方
法はないかについても調べたい.

 char buf[MAX_STR];という記述があるが,配列の要素数を例えば 
int max; char str[max]; のように変数で定義するとエラーが出た.
#defineで定義した値だとエラーが出なかった.rubyではこのようなこ
とはなかったので不思議だった.

 これもWIDTH,HEIGHTを先に定義し,それに合わせてグラフの大きさが
決まるようにしたが,温度に関しては40℃を超えると画面外へはみ出し
てしまうし,降水量もある程度大きくなると上へはみ出してしまう.こ
の対策に最初にデータの最大値最小値を求め,それによってどれくらい
引きのばすかを変えるという方法を考えたが,結局時間的問題で実装で
きなかった.ただ,横幅はデータの数が変わっても程よい縮尺で表示さ
れるように実装できた.

Q1.プログラムを作るという課題はどれくらい大変でしたか?
 グラフを描く課題は途中で投げ出したくなる程度には甚だ大変だった.
それ以外はあまり大変という感覚はなかった.
 
Q2. epsライブラリを使ってみてどのように感じましたか.
 結構使いやすかった.ただ欲を言うなら3つの数値を引数に渡せばRGB
カラーを自動でセットしてくれる関数が欲しかった.作ろうと思ったが
面倒なのでやめた.C++にクラスとして移植してみたい.
 
Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ.
 C言語について基礎プロの復習や,eps関係の扱いを復習できた.まだ
今回は復習メインなのでやりやすかったが,これからはそうはいかない
ことが容易に想像できるのでしっかり心して臨みたい.

m1910638 1b:○ Mon May 18 17:46:54 2020



1.演習1.b 

[コード]

//2の累乗
#include <stdio.h>
#include <math.h>

int main(void){
  int n;
  printf("n >");
  scanf("%d",&n);
  if(n >= 0){
    printf("%d\n",(int)(pow(n,2)));
  } else {
    printf("%f\n",pow(n,2));
  }
  return 0;
}

[説明]
math.hをインクルードすると使えるpow(x,y)はyをx乗した数を表すのに
使える。まずprintfでnの数字を入力させるような表示をしてscanfでn
に入力された数字を入れる。そのあとでnの正負で分岐するようif文を
使い、正の場合はpowに(int)を付けて整数型にし、負の場合は実数型で
表示されるようにした。

[考察]
Int型で定義したnをpowで計算すると自動的に実数型になっていた。こ
れは指数が1未満の場合にも正しい結果を表示させるためにわざと実数
型への変換が施されていると考察できる。

2.演習1.c

[コード]

//素因数分解
#include <stdio.h>

int main(void){
  int n,i;
  printf("n >"); scanf("%d",&n);
    if(n == 1){
        printf("1\n");
        return 0;
    }
  for(i = 2;i <= n;++i){
    while(n % i == 0){
      printf("%d\n",i);
      n /= i ;
    }
  }
  return 0;
}


[説明]
まずprintfでnの数字を入力させるような表示をしてscanfでnに入力さ
れた数字を入れる。n == 1の場合のみ1を表示するようにし、それ以外
は、nをi(初期値2)で割り切れるか判定→iを表示→nをiで割る→iで割
り切れなくなったらiを1増やす
という作業をfor文とwhile文を組み合わせて実行できるようにした。

[考察]
繰り返す文の中で繰り返す文を実行すると中の繰り返しの実行が優先さ
れることが分かった。

3.アンケート
A1.慣れてない分try&errorでやっているので構造を思いついてから書き
終わるまで時間がかかった。
A2.思い描いたものと合わないことが多々あり大変だった。
A3.もう少し書く作業に慣れなければならないと思った。

m1910640 1b:○ Mon May 18 08:28:45 2020


プログラミング通論 #1B
提出日付:5月17日

[課題の再掲]
演習1d
整数nを入力し、n以下の素数を出力する。(出力の順番や形式は任意)

[実施したこととその結果]
演習1dで書いたプログラムは下となった。
#include <stdio.h>
#include <math.h>

//isprimeを使った方が良い

int main(void){
    int counter1, counter2, input; int judge = 0;
    printf("Let's get prime numbers less than an input. Enter the input.");scanf("%d", &input);
    if(input <= 1){
        printf("Inappropriate.\n");//1以下の数字を入力すると不適当とみなす
        return 0;
    }
    if(input == 2){
        printf("2\n");
        return 0;
    }
    for(counter1 = 2;counter1 <= input;counter1++){
        if(counter1 == 2){
            printf("%d ", counter1);
        }else{
            for(counter2 = 2; counter2 <= counter1-1; counter2++){
                if(counter1%counter2==0){
                    judge = 1;
                }
            }
            if(judge != 1){
                printf("%d ", counter2);
            }
        }
        judge = 0;
    }
    printf("\n");
    return 0;
}

実際に動かした結果は下である。
[]$ ./a.out
Let's get prime numbers less than an input. Enter the input.10
2 3 5 7
[]$ ./a.out
Let's get prime numbers less than an input. Enter the input.30
2 3 5 7 11 13 17 19 23 29
[]$ ./a.out
Let's get prime numbers less than an input. Enter the input.-5
Inappropriate.
[]$ ./a.out
Let's get prime numbers less than an input. Enter the input.2
2
[]$ ./a.out
Let's get prime numbers less than an input. Enter the input.3
2 3


このプログラムでは入力する整数は2以上とした。そのため、1以下の整
数を入れた場合不適当であることを示す文字出力をプログラム4行目の
if文に書いた。
次に8行目のif文では入力した値が2であるとき、素数として2を出力し
てプログラムが終了するようにした。
12行目のforループでは入力された3以上のn(input)の値が素数である
かを判定する。整数としてcounter1とcounter2を用意してcounter1を2
からn(input)まで増加するループを作った。このループではcounter1が
素数であるかを判定した。counter1が2であれば2を出力し、そうでなけ
ればcounter2を2からcounter1-1まで増加させてcounter1がcounter2で
割り切れるかどうかを判定し素数かどうかを判定した。なお、素数かど
うかの判定としてjudgeという変数を用意した。

[考察]
このプログラムでは素数であるかを判定するために二重のforループを
用いることによって判定した。このforループではcounter2が
counter1-1になるまで数を増加させた。プロ通のテキストのisprimeを
使えばmain関数の行数が小さくなり、私のプログラム内のforループ内
のループ回数も(counter2)^(1/2)まで減らすことができ、プログラムコー
ドを見やすくできる。また、nが2だったときの場合分けも必要なくなる。

#include <stdbool.h>
#include <math.h>
bool isprime(int n){
  int limit = (int)sqrt(n);
  for(int i = 2; i <= limit; ++i) {
    if(n % i == 0) { return false}
  }
return true
}


[課題の再掲]
演習2b 多項式関数y = (x^3-x)/3のグラフを描画するプログラムを作成する。

[実施したこととその結果]
書いたプログラムは下となった。
#include <stdio.h>
#include <math.h>
#include "eps.h"

int main(void){
    eps_open("E2-b.ps", 160, 400);
    eps_cmd("80 200 translate");
    eps_drawline(-75, 0, 75, 0);
    eps_drawline(0, 195, 0, -195);
    for(int i = -30; i <= 29; i++){
        eps_cmd("0.0 0.0 0.0 setrgbcolor");
        double x1 = i;double y1 = 160*(i*0.05*i*0.05*i*0.05-i*0.05)/3;
        double x2 = i+1; double y2 = 160*((i+1)*0.05*(i+1)*0.05*(i+1)*0.05-(i+1)*0.05)/3;
        eps_drawline((int)x1, (int)y1, (int)x2 , (int)y2);
    }
    int f1 = eps_newfont("Courier", 10);
    int f2 = eps_newfont("Courier", 8);
    eps_puts(f1, 70, 5, "x");
    eps_puts(f1, 5, 190, "y");
    eps_puts(f2, -75, 185, "y = (x^3-x)/3");
    eps_puts(f1, -25, -23, "-1/8");
    eps_puts(f1, 2, 20, "1/8");
    eps_puts(f1, 23, -10, "1");
    eps_puts(f1, -19, -10, "-1");
    eps_puts(f1, 2, 2, "O");
    eps_close();
    return 0;
}

このプログラムでは160*400のキャンバスを用意して、そのキャンバス
にx軸y軸を引いた。その後、整数iについて-30から29まで1ずつ増える
forループを作り、iと対応したx1, y1, x2, y2を用意してキャンバスに
その二点をつなぐ直線を引く。その後、曲線とx軸、y軸と交わった部分
の座標を分かりやすい位置に書き込んだ。このプログラムで作った放物
線はかなり縦に引き伸ばされてしまった。

[考察]
このプログラムを作るとき、最初に整数x座標を対応したy座標の点を書
いていくようにした。しかし、x=0のときy=0、x=1のときy=0、x=2のと
きy=2となり、x=0からx=1までに本来できるであろう下に凸の曲線を表
現できなかった。そこで、xの値を0.05倍した値を曲線式に代入するこ
とによってx座標の点の取り方を細かくした。このままだとyの値が小さ
くなりすぎて上手く曲線が描かれないのでy座標に160を掛けた。これら
の試行により、より滑らかな曲線が描かれるとわかる。また、x座標を
細かく取る際はy座標が小さくなり、キャンバス上の格子点に点を取る
ことができなくなってしまうのでy座標に160のような数字を掛けること
でバランスの良い曲線が描けると分かった。しかし、どれだけ縦に曲線
を引き延ばせば良いかはx座標の取る細かさによるので適宜変更しなけ
ればならない。

[アンケート]
Q1.プログラムを作るという課題はどれくらい大変でしたか?
6時間程使ったのでかなり大変でした。
Q2.epsライブラリを使ってみてどのように感じましたか。
図形は色々書けそうだなと思いました。
Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
次回以降ポインタ等を扱うのが不安ですが頑張っていきたいです。

m1910646 1b:○ Sun May 17 19:29:14 2020


1910646
「個人作業」
2020/5/17

[課題1]
演習1-b
[課題の再掲]
整数nを入力し、2^nを出力する。ただしn≥ 0のときは整数、n<0のとき
は実数形式で出力する。

[プログラムのソース]
#include <stdio.h>
#include <math.h>

double expo(int n){
    double x = pow(2, n);
    return x;
}

int main(void){
    int n;
    printf("n>"); scanf("%d", &n);
    if (n>=0){
        int x = (int)expo(n);
        printf("%d\n", x);
    }
    else{
        double x =expo(n);
        printf("%f\n", x);
    }    
    return 0;
}

[実行例]
n>5
32

n>1.5
2

n>-1
0.500000

n>-3
0.125000


[プログラムの説明]
このプログラムではべき乗を計算する関数powを用いたので、プログラ
ムの頭に#include <math.h>を追加した。関数expoは答えが整数になら
ない場合もあるので少数を格納する型であるdoubleを利用した。main関
数ではif文を使うことでnが0以上の時は整数で、nが0未満の時は少数で
表されるようにした。

[考察]
最初はexpoの中にif文を入れていたが、main関数で結局int型に直して
しまいn<0の時も整数で出力してしまった。powさえ使えればこのプログ
ラムを作るのは簡単だが今までmain関数内でif文を使う機会が少なかっ
たので、練習になった。

[課題2]
演習2-a
[課題の再掲]
正方形を4×4に並べた絵を作成する。ただしそれぞれの正方形の間は空いている。
[プログラムのソース]
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");   
  for(int i = -2; i <= 1; ++i) {
    for(int j = -2; j <= 1; ++j) {
      eps_cmd("0.1 setgray");
      eps_cmd("1 setlinewidth");         
      eps_drawrect(j*100, i*100, 80, 80);  
      eps_cmd("1.0 1.0 1.0 sethsbcolor");
    }
  } 
  eps_close();                          
  return 0;
}

[実行例]
画像の貼り方がわからなかったので言葉で説明する。
1辺の長さが80の正方形が間隔20で4×4で並んだ。

[プログラムの説明]
基本的な構造はtestpes.cと同じなのでtestpes.cとの変更点であるプロ
グラムのソースの4行目から始まるfor文の中身について説明する。

jはiのfor文の中にあるのでiとjを-1,0,1,2と4回繰り返すことで、長方
形を描くeps_drawrectの中身を16種類作った。またeps_drawrectの中身
を(j*100, i*100, 80, 80)とすることで正方形の左下の座標がそれぞれ
平行か垂直方向に100離れているのに対して、正方形の1辺の長さを80に
することで正方形を20の間隔で並べることができた。

[考察]
for文をうまく使うことで正方形の左下の座標を何回も書かずに済んだ。
しかしこのやり方では正方形の集合全体でみると、正方形の集合の中心
は描画面の中心から左下にずれてしまう。このようになる原因は
eps_drawrectの指定している座標が長方形の左下隅であるということだ
が、なぜ指定するのが長方形の中心ではなく左下隅なのだろうと思った。

[アンケート]
Q1.時間はかかりそうだがまだ自分の力でできる範囲だった。

Q2.eps_drawrectの指定する座標が長方形の左下隅であることに不便を感じた。

Q3.epsライブラリで作成した描画の提出方法が分かりませんでした。

n1910483 1b:○ Sun May 17 12:56:39 2020


学籍番号:1910483
個人作業

今回の課題:epsライブラリを用いた描画を行う。

課題を実行する前段階として、テキストに記載されているeps.h及び
eps.cを作成し、epsライブラリの実行を行った。テキストの再掲になる
が、使用したコードを以下に示す。

#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  eps_drawline(-200, 0, 200, 0);
  eps_drawline(0, 200, 0, -200);
  for(int i = 1; i <= 8; ++i) {
    eps_num(i*0.1);
    eps_cmd("setgray");
    eps_cmd("4 setlinewidth");
    eps_drawrect(i*20, i*20, 30, 30);
    eps_num(i*0.1); eps_cmd("1.0 1.0 sethsbcolor");
    eps_fillcircle(-i*20, -i*20, 15);
  }
  int f1 = eps_newfont("Courier", 20);
  eps_puts(f1, -180, 50, "This is a pen.");
  int f2 = eps_newfont("Helvetica", 30);
  eps_puts(f2, 20, -50, "How are you?");
  eps_close();
  return 0;
}

このコードを実行した結果、テキストに記載されていたような図が完成
した。以上のことを踏まえて、以下の課題に取り組んだ。

<課題2-a:目的の図を完成させる>
テキストに記載されている(1)~(4)の図の作成に取り組んだ。以
下に使用したプログラムコードを示す。

(1)
#include <stdio.h>
#include "eps.h"

int main(void) {
  int i = -210;
  int j = -210;
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  while(j <= 240){
    while(i <= 240 ) {
      eps_drawrect(i, j, 80, 80);
      i = i + 120;
    }
    i = -210;
    j = j + 120;
  }
  eps_close();
  return 0;
}

このプログラムでは、eps.c及びeps.hに含まれる、長方形を描くプログ
ラムを用いて、うまく数値調整をして作成したものとなる。eps_openで
キャンパスを開いた後、while文を用いて位置をずらしながら、正方形
を描画するといった仕組みとなっている。

このプログラムの作成はepsの数値調整をするチュートリアルのような
ものであったため、然程の苦労はなかった。一方、座標指定時の数値調
整に少し戸惑った。基礎プログラミング演習の際に用いた描画プログラ
ムは、左上(左下?)を原点とする第一象限のみのキャンパスであった
が、今回はキャンパスの中心点を原点とし、第一象限から第四象限まで
全てを含むものだった。そのため、負の座標を考慮した上で座標指定す
る必要がある。

ここで、このような仕様となっている理由を探るため、基礎プログラミ
ング演習で用いたキャンパス生成プログラムを見た。

#define WIDTH 300
#define HEIGHT 200

static unsigned char buf[HEIGHT][WIDTH][3];

基礎プログラミング演習でのコードは、予めhファイルでサイズを指定
しておき、その上でcファイルで配列を用いたキャンパス生成を行って
いる。そのため、負の値を参照する座標が存在しないと推測される。

一方で、今回使用したプログラムを見ると、eps.cに記載されている
BoundingBoxが使用されている。このBoundingBoxでは、画像の中心を
[0,0,0](すなわち原点)とした情報である。よって、このBoundingBoxに
よって、上記のような負の座標を含むようなキャンパスが作成されると
推測される。

以上の仕様を理解した上で、他の図についても描画していく。

(2)
int main(void) {
  int i = -230;
  int j = -230;
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  while(j <= 240){
    while(i <= 240 ) {
      eps_drawrect(i, j, 90, 90);
      eps_drawrect(i+90, j+90, 90, 90);
      eps_fillrect(i+90,j,90,90);
      eps_fillrect(i,j+90,90,90);
      i = i + 180;
    }
    i = -230;
    j = j + 180;
  }
  eps_close();
  return 0;
}

このプログラムでは、指定してあるy座標の行を作成し、その後次の行
を描画してから、while構文でループするという手法をとっている。
whileのループを減らして一度に複数の描画をすることができている。

このように、1回の操作で複数の描画をすることができれば、アルゴリ
ズムの簡略化にもなるしプログラムそのものが綺麗になると考え、以下
の2つのプログラムで意識してみることにした。

(3)
int main(void) {
  int i = -192;
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  while(i<=216){
  eps_drawcircle(i,-192,48);
  eps_drawcircle(-192,i,48);
  eps_drawcircle(192,96+i,48);
  eps_drawcircle(96+i,192,48);
  i = i + 96;
  }
  eps_close();
  return 0;
}

このプログラムでは、whileループ内の数値調整をして、キャンパスの
一辺に円を描画する際、他の三辺にも同時に描画するようにしている。
当初は一辺毎に描画、もしくは向かい合う一辺を同時に描画することを
想定していたが、このようにして全ての辺で一度に描画をすることで、
アルゴリズムの簡略化・プログラムの短縮に成功している。

(4)
#include <stdio.h>
#include <math.h>
#include "eps.h"

#define PI 3.14159265358979

int main(void) {
  double i = -67.5;
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  while(i<=90){
    eps_drawline(0,0,200*cos((i*PI)/180),200*sin((i*PI)/180));
    eps_drawline(0,0,-200*cos((i*PI)/180),200*(-sin((i*PI)/180)));
    i = i + 22.5;
    }
  eps_close();
  return 0;
}

このプログラムでは、iを角度として、角度毎に線を引くことを繰り返
すといった方法で描画している。ここでも一度にx座標の正と負の領域
に同時に線を引くようにしてあるが、例えば初期位置を、負の領域に線
を引くときに指定している位置とすれば、プログラムコードが1行で済
む。

以上のように、数学的性質を利用して、プログラムを綺麗に描くことが
できる。今後作成するプログラムにおいても、数学の性質を利用した工
夫を施し、アルゴリズムの簡略化/プログラムの簡潔化に努めたい。

<アンケート>
Q1.手探りで結果を調べる実験的要素があるため、大変と言うよりかは
楽しい印象を覚えた。
Q2.様々な描画の可能性を感じた。いろいろな図を作ってみたい。
Q3.今回はプログラムを工夫して描くことを念頭に置いた。今後も心掛
けたい。

n1910485 1b:○ Sun May 17 16:26:53 2020


提出日時:5/17

①課題1d
・課題の再掲
入力された整数nが素数かそうでないかを判定する。
・ソースコード
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool pr(int n){
  int i;
  int lim = (int)sqrt(n);
  if(n==1){return false;}
  for(i=2;i<=lim;++i){
    if(n%i==0){return false;}
  }
  return true;
}

int main(void){
  int n;
  printf("n> "); scanf("%d",&n);
  if(pr(n)){printf("%d is a prime number.\n",n);}
  else {printf("%d is not a prime number.\n",n);}
  return 0;
}

・プログラムの説明
入力されたnが素数かどうかを判定する。nが素数である場合には、√n
(ソースコードでいうsqrt(n))以下の整数で割れる。なぜなら、もし
√nより大きい整数で割れる場合、この整数をmとするとn/mもnの約数で
あり、この約数は√nより小さい。このため、2以上√n以下の整数でnを
割り切れる整数があればnは素数ではない。prはint型の整数nがが素数
であればtrue、そうでなければfalseを返す。また、この方法だとn=1の
ときについて調べられないので、別で先に調べる。

・考察
今回はsqrtを用いてコードを記述したが、調べる量が少なくなる反面、
デメリットもある。例えば、sqrtがない場合はmath.hをincludeしなく
ていい、int型のlimという変数が無くなるので、コードがよりシンプル
になるなどである。整数nが10や100など、そこまで大きい数でなければ
1からnまですべての整数についてnを割り切ることができるか調べるの
にコンピュータならばさほど時間はかからないが、桁数が大きくなって
くると調べる量が少ないことが非常に影響してくる。なので、自分の書
いた場合のソースコードは、より大きい数が素数であるかを判定するの
に適しているといえると考えた。

②課題2a
・課題の再掲
示された様々な模様をPSを用いて描く。
・ソースコード
(1)
#include <stdio.h>
#include "eps.h"

int main(void){
  eps_open("out.ps", 480, 480);
  eps_cmd("2 setlinewidth");
  int i,j;
  for(j=0;j<4;j++){
    for(i=0;i<4;i++){
      eps_drawrect(120*j+20,120*i+20,80,80);
    }
  }
  eps_close();
  return 0;
}
(2)
#include <stdio.h>
#include "eps.h"

int main(void){
  eps_open("out.ps", 480, 480);
  int i,j;
  for(j=0;j<5;j++){
    for(i=0;i<5;i++){
      if((i+j)%2==0){eps_fillrect(96*j,96*i,96,96);}
    }
  }
  eps_close();
  return 0;
}
(3)
#include <stdio.h>
#include "eps.h"

int main(void){
  eps_open("out.ps", 480, 480);
  int i,j;
  for(j=0;j<5;j++){
    for(i=0;i<5;i++){
      if((i*j)%4==0 && i*i+j*j!=8){eps_drawcircle(48+96*j,48+96*i,48);}
    }
  }
  eps_close();
  return 0;
}
(4)
#include <stdio.h>
#include "eps.h"

int main(void){
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  int j;
  for(j=0;j<16;j++){
    eps_drawline(0,0,200,0);
    eps_cmd("22.5 rotate");
  }
  eps_close();
  return 0;
}

・プログラムの説明
 まず、全てのコードについて共通する部分について説明する。まず、
一番上がstdio.hとeps.hのヘッダファイルをincludeしている。そして、
eps_open("out.ps", 480, 480);で大きさ480×480の大きさのpsファイ
ルout.psを用意する。次からがそれぞれ違う部分で、最後に
eps_close()で描画を終え、0(int mainなので)を返す。

 次に、それぞれコードの違う部分について説明する。
(1)
eps_cmd("2 setlinewidth");で線の太さを2ピクセルにしている。初期
状態だと一番左下が原点なので、まず20,20の点を左下の頂点として
80×80の正方形を描く。次に、左下の頂点を120だけ左にずらして同じ
図形を描くのを計4回繰り返す。この一連の操作を、左下の頂点を上に
120ずらして行う、という操作を計4回繰り返す。

(2)
480×480のファイルを96×96の正方形に25等分する。まず、一番左下の
正方形を黒で塗りつぶし、一つとばしで黒く塗りつぶしていく。この操
作を、

  for(j=0;j<5;j++){
    for(i=0;i<5;i++){
      if((i+j)%2==0){eps_fillrect(96*j,96*i,96,96);}
    }
  }
の部分で行っている。全てのマスについて、if((i+j)%2==0)で黒く塗り
つぶすかそうでないかを調べている。
   
(3)
480×480のファイルを96×96の正方形に25等分する。その正方形の中心
を中心とする半径48の円を縁のマスに描く。

  for(j=0;j<5;j++){
    for(i=0;i<5;i++){
      if((i*j)%4==0 && i*i+j*j!=8){eps_drawcircle(48+96*j,48+96*i,48);}
    }
  }
でこれを行っている。if((i*j)%4==0 && i*i+j*j!=8)で縁のマスかどうかを調べている。
(4)
まず、 eps_cmd("240 240 translate");で原点を初期状態において
240,240の点に移している。そこの点から長さ200の直線を
eps_drawline(0,0,200,0);引く。次に、eps_cmd("22.5 rotate");で
22.5°だけ図形を回転させる。これを計16回繰り返す。
    
・考察

(1)~(3)のものに関しては、画像全体を何等分かにして、そこに図形を
描くかどうかを調べるという手法でファイルを記述することができた。
(1)は特に調べる必要が無かった。(3)ではiとjを0から4まで一つずつず
らして調べるので、数値的に縁の部分を表すことができる条件を探した
結果、iとjの積は0か4がかけられるのでその積は必ず4の倍数になるこ
とに気づいた。しかし、i*j%4==0の条件のみだとi=2,j=2のときも当て
はまってしまうので、i*j%4==0&&i*i+j*j!=8としてその場合を省いた。
しかし、これは縁を調べる方法としてはi,jが増えた場合について対応
できなくなることがあるので、あまり一般的な方法ではない。一般的に
は、条件は多くなってしまうが、i,jのどちらか一方でも0か最大値なら
trueを返す条件にすればよい。

(4)では、完成した図形を見ると、回転は半周でもいいように見えるが、
ここでは、原点を中心の点としているので、線を16本書いた方がコード
が簡単になる。

③アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
今回はそこまで大変ではなかった。
Q2. epsライブラリを使ってみてどのように感じましたか。
図形を描くにも数学の知識を用いることに少し驚いた。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
チャット形式だと質問が全体で共有されていて良かった。

n1910493 1b:○ Mon May 18 00:35:18 2020


プログラミング通論レポート 1b

学籍番号:1910493
ペアの学籍番号:個人作業
提出日時:2020/5/18

------
・演習1-b:整数nを入力し、2^nを出力。0以上の時は整数、負の時は実数形式で出力。

// 1b.c
#include <stdio.h>
#include <math.h>
double expo2(int n) {
  double s = 1;
  if(n >= 0) {
    for(int i = 0; i < n; ++i) { s *= 2; }
    return s;
  } else {
    n = -n;
    for(int i = 0; i < n; ++i) { s *= 2; }
    return 1/s;
  }
}
int main(void) {
  double n;
  printf("n> "); scanf("%lf", &n);
  double result = expo2(n);
  printf("%g\n", result);
  return 0;
}

---実行例---
% ./a.out
n> 11
2048
% ./a.out
n> -4
0.0625


---説明---
 expo2関数の中では、sという変数を用意して、与えられたn回分だけ2
をかけることで2のn乗となる数を用意した。そして、nが0以上の場合は
そのままその数を、負の場合はその逆数を返すことで、題意を満たす数
がmain関数内で書かれるようにした。

---考察---
 工夫したところとして、nが負の場合は絶対値をとってからforループ
に入るようにした。もともとはforループを先に実行し、そのあとnが0
以上か否かで分けていたが、課題をやってみて、それだとnが負の時に
forループの条件にあわずどんな数でも1と返されてしまったので、この
ようにした。絶対値を返すという簡単なプログラムはRubyのときに学ん
だが、それをこういった形で利用するとは思わなかったので、大変勉強
になった。
------
・演習1-c:正整数を入力し、その素因数分解を表示。

// 1c.c --- 自然数nの素因数分解を表示する
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
bool isprime(int n) {
  int limit = (int)sqrt(n);
  for(int i = 2; i <= n; ++i) {
    if(n % i == 0) { return false; }
  }
  return true;
}
void primesmash(int n) {
  for(int i = 2; i <= n; ++i) {
    if(n % i == 0) { printf("%d ", i); n = n/i; --i; }
  }
  printf("\n");
}
int main(void) {
  int n;
  printf("n > "); scanf("%d", &n);
  if(isprime(n)) { printf("%d", n); }
  else           { primesmash(n); }
  return 0;
}

---実行例---
% ./a.out
n > 73
73 
% ./a.out
n > 20790
2 3 3 3 5 7 11 


---説明---
 まず、例題でも扱ったisprime関数によって、与えたnが素数かそうで
ないかを判別し、素数ならmain関数内でそのままその数を返すようにし
た。もし素数でないなら、primesmash関数内でforループをかけ、2から
nまでで割りきることができたらその数を出力し、nをその数で割る。そ
して素数まで割り切れたら、ループを抜けた最後に改行することできれ
いに見えるようにした。

---考察---
 工夫としては、forループ内で割り切れた時、最後にiを1減らしても
う一度その数でforループをかけるようにしたことだ。例えば上の例の
20790では、3で3回割れるため、一度割って次の数に行ってしまうとそ
の後の分解がおかしくなってしまう。この場合なら、3で割る回数が2回
分だけ残っているため、2 3 5 7 9 11となってしまう。それを解消する
ために、--iとした。

------
演習2-a(3):縦横ともに5個の円の囲いの図を描写する。

// 2a3.c --- 5*5の円
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");
  for(int i = 1; i <= 5; ++i) {
    eps_drawcircle(60*i, 180, 30);
    eps_drawcircle(60*i, 420, 30);
    eps_drawcircle(60, 60*i+120, 30);
    eps_drawcircle(300, 60*i+120, 30);
  }
  eps_close();                          
  return 0;
}


---実行---
% gcc 2a3.c eps.c
% ./a.out

---説明---
 例題と同じような用紙のサイズを作成し、そこに塗りつぶさない円を
描くeps_drawcircleを用いて円を描いた。半径は用紙のサイズに収まる
よう30ptにした。

---考察---
 課題としては16個の円を描けば良いが、16回drawcircleを記述するの
は大変なので、for文で括って変数iを5回回して、縦横それぞれ2ライン
ずつ5つの円を描くように工夫した。これにより4回記述するだけで題意
を満たす図形を描けた。そのため、見た目上は円が16個描かれているが、
角の4つの円については2つ重なっている。完成したps形式の画像をプレ
ビューで開き、ドラッグした結果色が異なることでそれを確認した。こ
のように記述するプログラムを減らして完結にすることも、for文の役
割だと考える。

------
・演習2-b:任意の関数のグラフを描写する。

// 2b.c --- ハート型関数の描写
#include <stdio.h>
#include <math.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");   
  eps_drawline(-200, 0, 200, 0);  
  eps_drawline(0, 200, 0, -200);  
  for(int t = -500; t <= 500; ++t) {
    eps_drawrect(160*sin(t)*sin(t)*sin(t), 130*cos(t)-50*cos(2*t)-20*cos(3*t)-10*cos(4*t), 1, 1); 
  }
  int coordinate = eps_newfont("Times-Italic", 30);      
  eps_puts(coordinate, -30, -30, "0");
  eps_puts(coordinate, 200, -20, "x");
  eps_puts(coordinate, -20, 200, "y"); 
  eps_close();                          
  return 0;
}


---実行---
% gcc 2b.c eps.c -lm
% ./a.out

---説明---
 細かい点で近似するため、長方形を描くeps_drawrectの幅・高さを
1ptに指定して点を描いた。この図形は媒介変数tを用いて、sinとcosを
駆使しハート型を表現している。作成するにあたり、
<http://zellij.hatenablog.com/entry/20111205/p1> を参考にした。
なお、x軸とy軸に当たる直線は長さが400ptあるためにそのままの縮尺
だと小さいので、x,yともに10倍ほどスケールを大きくしている。図形
の精度を上げるため、tの範囲は大きめに-500から500まで取った。また、
x軸・y軸・原点の表示をTimesの斜体で描いている。

---考察---
 x座標をy座標をそれぞれfor文内で変数で表すため、媒介変数表示さ
れた関数の描写が非常に楽だと感じた。実際に式を見たらどんな形にな
るのかイメージするのは難しいが、その式をそのまま入れることで簡単
に描けるので、ビジュアル的にわかりやすくする非常に便利なツールだ
と考える。

---アンケート---
Q1. プログラムを作るという課題はどれくらい大変でしたか?
C言語では型がガッチリしているので、何度もエラーに遭遇してRuby言
語よりも大変でした。その代わり、エラーの内容がわかりやすいのが良
いと感じました。

Q2. eps ライブラリを使ってみてどのように感じましたか。
基本的な図形はピクセルツールで描いた方が楽ですが、関数などはベク
ターで描いた方が楽だし、式によってはそのまま打つだけで表現してく
れるので便利だと感じました。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ
C言語の復習ができたとともに、C言語の新たな良さに気づくことができ
ました。ありがとうございました。

n1910496 1b:○ Sun May 17 20:49:44 2020


学籍番号:1910496
個人作業
提出日時;2020/5/17 20:50

〇1つ目の課題

・課題の再掲
整数nを入力し、n以下の素数を出力するプログラムを作る。

・プログラム
// 1-d
#include <stdio.h>
#include <math.h>
int main(void) {
    int n;
    printf("n> "); scanf("%d", &n);
    if(n == 1) { printf("no prime number.\n"); }
    if(n == 2) { printf("2\n"); }
    if(n == 3) { printf("2 3\n"); }
    if(n >= 3) {
        printf("2 3 ");
        for(int k = 2; k <= n; ++k) {
            int limit = (int)sqrt(k);
            for(int i = 2; i <= limit; ++i) {
                if(k % i == 0) { break; }
                else if(k % i != 0 && i == limit) { printf("%d ", k); }
            }
        }
    }
    printf("\n");
    return 0;
}

・プログラムの説明
例題で用いられていた素数判定のプログラムを用いて、それをn以下の
数字すべてで判定を行っている。
なお、素数判定のプログラムを用いているのはnが3以上の場合で限定している。
nが1、2、3のときだけ個別で結果を用意している。

・考察
例題の素数判定のプログラムは、2から指定した数字の平方根までの数で割っている。
これは調べる約数を半分に抑えるためであるが、2と3の平方根はどちら
も2を下回ってしまうためそもそもループが開始されず、2と3だけが出
力されなくなってしまう。
そこで例題の素数判定を用いずに、半分に抑えずすべて調べることを考
えると、実行する側の作業量としては2と3のときで個別に結果を設けた
ほうが少ない。
よって入力した数が3以上のときに例題の素数判定を用いて、あらかじ
め2と3は出力するようプログラムを作った。

〇2つ目の課題

・課題の再掲
epsライブラリを利用して面白い描画をおこなう。

・プログラム
// 2-d
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");
  for(int i = 0; i <= 6; ++i) {   
    eps_drawline(-200, -180+60*i, 200, -180+60*i);
  }
  for(int i = 0; i <= 6; ++i) {   
    eps_drawline(-180+60*i, 200, -180+60*i, -200);
  }
  for(int k = 0; k <= 2; ++k) {
    for(int i = 0; i <= 2; ++i) {
      eps_cmd("0.0 setgray");
      eps_fillcircle(-150+120*i, 150-120*k, 30);   
    }
  }
  for(int k = 0; k <= 2; ++k) {
    for(int i = 0; i <= 2; ++i) {
      eps_cmd("0.0 setgray");
      eps_fillcircle(-90+120*i, 90-120*k, 30);   
    }
  }
  for(int k = 0; k <= 2; ++k) {
    for(int i = 0; i <= 2; ++i) {
      eps_drawcircle(-90+120*i, 150-120*k, 30);   
    }
  }
  for(int k = 0; k <= 2; ++k) {
    for(int i = 0; i <= 2; ++i) {
      eps_drawcircle(-150+120*i, 90-120*k, 30);   
    }
  }   
  eps_close();                          
  return 0;
}

・プログラムの説明
1マス60×60ピクセルの碁盤目になるよう線がひかれ、そのマスに以下
のように白黒の丸い図形が同じ色とは隣合わないよう収まっている。

●〇●〇●〇
〇●〇●〇●
●〇●〇●〇
〇●〇●〇●
●〇●〇●〇
〇●〇●〇●

・考察
"● ● ● "
" 〇 〇 〇"
" ● ● ●"
"〇 〇 〇 "

上記の4つの横列のセットに分解してループを組むことを考えた。
始点の図形の中心座標を左上の角4マスでそれぞれ対応させれば横列の
ループは容易にできる。
上2列を埋めたその後、縦列で指定回数ループさせれば下4列も同様に埋まる。
書いたプログラムではループを4セットに分離しているが、ループ回数
が等しく、ループ変数も回数のみで対応しているため、同一ループ内に
PSコマンドを詰めたほうがすっきりしたプログラムになったと思われる。

〇アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
久しぶりに触れてアルゴリズムを組めるまでの速度が遅くなっていまし
たが、大変というほどではなかったです。
 
Q2. epsライブラリを使ってみてどのように感じましたか。
簡単な描画ができるのはいいですね。
 
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
次回も頑張ります。

n1910498 1b:○ Thu May 14 18:26:19 2020


レポート1b


ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

【選択した課題1】
演習 1-d. 
 整数nを入力し、n以下の素数を出力するプログラムを作成せよ。 

【コード1】
#include <stdio.h>
#include <math.h> 
#include <stdbool.h> 

bool isprime(int n) {
 int limit = (int)sqrt(n);
 for(int i = 2; i <= limit; ++i) {
  if(n % i == 0) { return false; }
 }
 return true;
}

int main(void){
 int n, i, cnt=0;
 printf("n> "); scanf("%d", &n);
 for(i = 2; i <= n; i++ ) {
   if( isprime(i) == true ) {
      printf("%d", i);
      printf("  ");
      cnt++;
   }
 }
 printf("\n");
 return 0;
}

【実行例1】
$ ./a.out
 n> 10
 2  3  5  7
$ ./a.out
 n> 100
 2  3  5  7  11  13  17  19  23  29  31  37  41  43  47  53  59
 61  67  71  73  79  83  89  97

【説明1】
 素数判定をするプログラムをまず書いた。
 そしてmainの中で、最小の素数である2から整数nまで順に素数判定
していき素数であるときその数を表示させるようにした。

【考察1】
 nまでの数を素数判定していき素数であったものを配列にいれるとい
う方向性も考えたが、上のものの方が行数が少なく済むと考え素数であっ
たときは書くというプログラムにした。
 配列にいれる方向せいでもプログラムを作成した。以下がそれのmainの部分である
int main(void){
  int n, i, j, cnt=0, a[n];
  printf("n> "); scanf("%d", &n);
  for(i = 2; i <= n; i++ ) {
    if( isprime(i) == true ) {
      a[cnt] = i;
      cnt++;
    }
  }
  for(j = 0; j < cnt; j++ ) {
    printf("%d", a[j]);
    printf("  ");
  }
  printf("\n");
  return 0;
}
実行例は全く同じようになった。やはりそのままの方が短くわかりやすいなと感じました。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

【選択した課題2】
演習 2-d. 
 その他epsライブラリを利用して面白い描画を行え。

【コード2】
#include <stdio.h>
#include <math.h>
#include "eps.h"
#define PI 3.1415

int main(void) {
  int i, j;
  eps_open("out.ps", 480, 480);    
  for(i = 0; i <= 9; ++i) {
   for( j = 0; j <= 9; ++j) {
    double k = sin(2*PI/9*j);
    eps_cmd("0.1 setgray");
    eps_fillrect(100*i+25*k, 1+50*j, 49, 49);
    eps_cmd("0.6 setgray");
    eps_drawrect(100*i+25*k, 50*j, 50, 50);
   } 
   eps_drawline(0, 0+50*i, 480, 0+50*i);
  }
 eps_close();                          
 return 0;
}

【説明2】
 単調増加する値i,jとforを使い、fillrectとdrawrectを使用し薄い灰
色で縁取られた正方形の位置をずらしながら繰り返し描いた。また、こ
の際に増減する値kを設定しx座標の値に加えることで正方形の位置が
(列で見た時に)ぐねぐねと曲るようにした。
 そして、drawlineを用いて(行ごとに)平行な横線を描いた。
 完成した図の画像を載せることはできないが、所謂“カフェウォール錯視”を描画した。

【考察2】
演習2-a(2)を応用して作成した。初めは
#include <stdio.h>
#include "eps.h"

int main(void) {
 int i, j;
 eps_open("out.ps", 480, 480);    
 for(i = 0; i <= 9; ++i) {
  for( j = 0; j <= 2; ++j) {
   eps_cmd("0.1 setgray");
   eps_fillrect(-199+100*i+25*j, 1+50*j, 48, 48);
   eps_cmd("0.6 setgray");
   eps_drawrect(-200+100*i+25*j, 50*j, 50, 50);
   eps_drawline(0, 0+50*j, 480, 0+50*j);
  }
  for( j = 3; j <= 5; ++j) {
   eps_cmd("0.1 setgray");
   eps_fillrect(-199+100*i-25*(j-4), 1+50*j, 48, 48);
   eps_cmd("0.6 setgray");
   eps_drawrect(-200+100*i-25*(j-4), 50*j, 50, 50);
   eps_drawline(0, 0+50*j, 480, 0+50*j);
  }
  for( j = 6; j <= 9; ++j) {
   eps_cmd("0.1 setgray");
   eps_fillrect(-199+100*i+25*j, 1+50*j, 48, 48);
   eps_cmd("0.6 setgray");
   eps_drawrect(-200+100*i+25*j, 50*j, 50, 50);
   eps_drawline(0, 0+50*j, 480, 0+50*j);
  } 
 }
 eps_close();                          
 return 0;
}
 このようなプログラムを作成したが、行数が多くもっと簡単にできな
 いかと考え三角関数を用いることにした。この際他の増減する関数も
 考えたが-1~1の範囲で増減する三角関数が一番ずれを考えるときにも
 わかりやすく見栄えもよいと考えた。この際kはintでなくdoubleにす
 る必要があることがわかった。
 また、eps_cmd("0.1 setgray")の色の指定を書かないと最初の一つは
 黒で塗りつぶされるが次のものからはeps_cmd("0.6 setgray")の指定
 のままループに入るので灰色になってしまうということが作成する中
 で分かった。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

【アンケート】
Q1. プログラムを作るという課題はどれくらい大変でしたか?
 例題を少し変える程度のものはよかったのですが、自分で好きなもの
を描画する課題は大変でした。
Q2. epsライブラリを使ってみてどのように感じましたか。
 なんとなくですが使いやすいなと思いました。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
 演習2-dをやった際に、最初にざっくり作った後画像を生成しながら
値を調整していったのですが、もっと余分なところ(はみ出す正方形)を
減らすことができるのではないかと思うので座標をもっと事前に考えな
がら作成したほうがいいと思いました。
 次回も頑張りたいです。

n1910506 1b:○ Tue May 12 14:36:17 2020


プログラミング課題

提出日時5月12日
一つ目の課題
#include <stdio.h>
#include "eps.h"

int main(void){
	int k;
eps_open("check.ps", 480, 480); 
eps_cmd("240 240 translate"); 
	for(int j = 0;j <= 4;++j) {
		k = j*15;
	for(int i = 1; i <= 5; ++i) {
		eps_num(i*0.1);
		eps_cmd("setgray");
		//eps_drawrect(i*15, k, 15, 15);
		//eps_fillrect(i*15,k,15,15)
		if((i %2==1 & j%2 ==0 )||(i%2 == 0 && j%2 ==1)){
		eps_fillrect(i*15,k,15,15);}
	}
}
	/*for(int i = 1; i <= 3; ++i) {
		eps_num(i*0.1);
		eps_cmd("setgray");
		eps_drawrect(i+20, i*20, 15, 15);
	}*/
	eps_close();
return 0;

塗り潰した四角形と何もしてない四角形を交互に出力する。

二つ目のプログラミングとその説明

#include <stdio.h>
#include "eps.h"

int main(void){
	int k;
eps_open("test.ps", 480, 480); 
eps_cmd("240 240 translate"); 
	for(int j = 0;j <= 3;++j) {
		k = j*20;
	for(int i = 1; i <= 4; ++i) {
		eps_num(i*0.1);
		eps_cmd("setgray");
		eps_drawrect(i*20, k, 15, 15);
	}
}
	eps_close();
return 0;
}
一定間隔ごとに4×4の四角形を生成する。
アンケート
	Q1.プログラムを作るという課題はどれくらい大変でしたか?
	 今回のはそんなに時間は掛からなかった。
	Q2.epsライブラリを使ってみてどのように思いましたか?
	 他のプログラミングとあまり変わらないので使いやすかった。
	Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ
	 ウォーミングアップにちょうどいい感じの難易度だった。

n1910507 1b:○ Fri May 15 23:47:30 2020


レポート課題1b

提出日時 5/15

演習1c
整数nを入力し、nの素因数分解を表示する。

プログラム
#include <stdio.h>
int main(void){
    int n, temp, i=2;
    printf("n> "); scanf("%d", &n);
    temp=n;
    while(i<=n){
        if(temp%i==0){
            printf("%d ", i);
            temp=temp/i;
        } else {
            i++;
        }
    }
    printf("\n");
    return 0;
}

説明
 まず、変数tempにscanfで入力した変数nの値を入れ、i=2からi=nにな
るまでwhileの中を繰り返す。whileの中は、tempがiで割り切れる数な
らばiを表示し、その後tempをiで割った数をtempに入れる。tempがiで
割り切れるなら、iに1を足す。例えば、n=60ならばtempに60を入れる。
60は2で割りきれるので2を出力し、60を2で割った30をtempに入れる。
30は2で割りきれるので2で割りきれるので2を出力し、30を2で割った15
をtempにいれる。15は2で割りきれないので、iに1を足す。このような
処理をして、素因数分解する。

考察
最初にfor文を使い繰り返そうと考えたが、同じ素因数を複数個持つ整
数の場合にうまくいかないと気づき、while文を使って目的のプログラ
ムを書いた。

演習1d
整数nを入力し、n以下の素数を出力する。

プログラム
#include <stdio.h>
#include <math.h>
#include <stdbool.h>
bool isprime(int n){
    int limit=(int)sqrt(n);
    for(int i=2; i<=limit; ++i){
        if(n%i==0){
            return false;
        }
    }
    return true;
}
int main(void){
    int n;
    printf("n> "); scanf("%d", &n);
    for(int i=2; i<=n; ++i){
        if(isprime(i)){
            printf("%d ", i);
        }
    }
    printf("\n");
    return 0;
}

説明
 素数かどうかを判定する関数isprime(n)をi=2からi=nになるまで繰り
返し、isprime(n)がtrueの場合、そのときのiを出力しn以下の素数をす
べて出力する。

考察
 ++iとi++の違いについて今まで疑問に思っていたが調べたところ、例
えば、y=x++ではyにxの値を入れてからxに1を足しているのに対し、
y=++xではxに1を足してからその結果をyに代入していることがわかった。

演習2b
好きな関数を1つ選びグラフを描画する。

プログラム
#include <stdio.h>
#include <math.h>
#include "eps.h"
int main(void){
  eps_open("out6.ps", 500, 500);
  eps_cmd("250 250 translate");
  double x1, x2, y1, y2;
  for(int i=-250; i<=250; ++i){
    x1=i; x2=(i+1); y1=100*sin(x1/50); y2=100*sin(x2/50);
    eps_drawline(x1, y1, x2, y2);
  }
  eps_close();
  return 0;
}

説明
 x1にはiを、x2にはi+1を入れ、y1、y2にはy=100*sin(x/20)で計算し
た値を入れ、(x1, y1)と(x2, y2)を結んだ線分を計500回生成してsinの
グラフを描画する。

考察
 ただ単純にy=sinxに代入し、折れ線で近似するだけでは、とても小さ
く見づらい形で描画されてしまうので、振幅と周期をそれぞれいじって
sinの関数のように見えるグラフを描画した。

アンケート
Q1. 1年生のときにたくさん書いたので、それほど大変だとは思わなかった。
Q2. 幾何学模様を描くのには適していると感じた。
Q3. 演習2cの課題を天気APIからデータを取得して、平均気温などをグ
ラフに使用と考えたが、自分のレベルでは到底実現できなかったことが
残念だ。今後より勉強して、このようなプログラムを書けるようにした
い。

n1910509 1b:○ Sun May 17 00:21:08 2020


n1910509 根古谷勇作 ペア学籍番号 h1910546
書いたプログラム	
//1a
#include<stdio.h>
#include<math.h>

double a(double x,double y){
  double z;
  z=x*x+y*y;
  return sqrt(z);
}

int main(){
  double x,y;
  printf("x>");scanf("%lf",&x);
  printf("y>");scanf("%lf",&y);
  printf("answer>%lf",a(x,y));
}
//1b
#include<stdio.h>

double aaa(int n){
double a,b=1.0;
if(n>=0){for(a=0;a<n;++a){b=b*2;}}
else{for(a=0;a>n;--a){b=b/2.0;}};
return b;
}
int main(){
int n;
printf("n?>");scanf("%d",&n);
if(n>=0){printf("%d\n",aaa(n));}
else{printf("%lf\n",aaa(n));}
}

2a(1)
#include <stdio.h>
#include "eps.h"

int main(){
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  for(int i=0;i<=3;++i){
    for(int m=0;m<=3;++m){
    eps_drawrect(-180+i*120,180-m*120,60,60);
    }
  }
  eps_close();
    return 0;
}
説明、考察
1a:値はすべて実数型とし、計算した値の平方根をとって値を返した。
  Include<math.h>を入れたため、コンパイル時に-lmを加えた。プロ
  グラムについては、友人に言われた通りわざわざ変数の計算を外に出
  さない方がより簡潔になると思った。
  1b:forのじょうけんしきa、正の場合大きくし、負の場合は小さくし
  てn回繰り返されるよう調整した。値はすべて実数型でまず処理し、
  mainのprintfで正負判別をし、正なら実数型、負なら整数型で分けて
  出力した。
2a(1):サイズは先生の例に倣い、eps_drawrectを変数n,mの繰り返しに
  より4×4分出力した。左から1行ずつ縦に正方形を出力している。
  
アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
手間取って時間がかかるものなので、あまり好きではないです。
Q2. eps ライブラリを使ってみてどのように感じましたか。
使いやすいのではと思う。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
今は苦手意識があるけれども、頑張って克服してスムーズにプログラミ
ングができるようになりたいです。

n1910513 1b:○ Mon May 18 19:36:53 2020


提出日: 2020/05/18

演習1 b.
2のn乗を出力する

プログラム:

#include <stdio.h>
double njou(int n) {
  double a=1;
  for(int i = 1; i <= n; ++i) {
    a=a*2;
  }
  return a;
}
int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  if(n>=0) { printf("%d\n", (int)njou(n)); }
  else { printf("%f\n", 1/njou(-n)); }
  return 0;
}


説明:
mainでnを入力し、n>=0のときはnを、それ以外は-nを引数として関数
njouを呼び出す。関数njouでは、実数a=1にforを用いて2をn回掛けてa
を返す。mainに戻り、n>=0のときは整数にして出力する。それ以外は実
数のままで逆数を出力する。

考察:
nが自然数のとき、2のn乗は掛け算だけで計算することができる。nが負
の整数のときも、掛け算の後に逆数にすれば計算できる。よって、ひと
つの関数にすることで簡潔なプログラムが作れることが分かった。

2のn乗を計算する関数をintにすると、n<0のときの逆数の計算で整数同
士の割り算になり0を返してしまうので、doubleにしなければならない
ことが分かった。

演習2 a.
正方形が16個並んだ絵を作成する

プログラム:

#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 500, 500);   
  for(int i = 1; i <= 4; ++i) {
    for(int j = 1; j <= 4; ++j) {
      eps_cmd("4 setlinewidth");         
      eps_drawrect(20+(i-1)*120, 20+(j-1)*120, 100, 100);  
    }
  }  
  eps_close();                          
  return 0;
}


説明:
500×500の領域を指定し、2重のループを使って100×100の正方形を左
下から上と右に向かって4×4=16回描いた。正方形同士の間隔は20にし
た。

考察:
規則性を見つけることでプログラムを短く書くことができた。原点は左
下のまま動かす必要が無いことが分かった。

アンケート:
Q1. 新しい内容では無かったので大変だと思わなかった。
Q2. 基礎プログラミングと変わらないと思った。
Q3. チャットにまだ慣れなかった。

n1910519 1b:○ Mon May 18 15:01:12 2020



一つ目の課題 演習1b
整数nを入力し、2のn乗を出力する。n>=0の時は整数、そうでない時は実数で出力する。

コード
#include<stdio.h>
#include<math.h>

int a(int n){
    double b;
    b= pow(2,n);
    return b;
}
int main(void){
    int n;
    double x;
    printf("n> "); scanf("%d",&n);
    if(n<0){
        n=-1*n;  x= a(n); x= 1/x; n=-1*n;
         }
    else { 
        x=(int) a(n);
         }
   printf("2の%d乗は%gです。\n",n,x);
   return 0;  
}

説明と考察
nが0以上の時はpowを使うだけで正しく計算できた。しかし、私のパソ
コンではnが負の数の時に0と表示された。これを解決するために、nが
負の場合は2のn乗が1÷(2の絶対値乗)に等しいことを使って計算した
ところ上手くいった。
また、変数を整数と実数で上手く使い分けるようにした。

二つ目の課題 演習1c
2以上の整数nを入力し、nの素因数分解を表示する。

コード
#include<stdio.h>
#include<math.h>

int isprime(int n){
    int  i=2;
    while(i<= n){
    if(n % i ==0){ printf("%d ",i); n=n/i; i=1;}
    ++i;
    }
    return 0;
}

int main(void){
    int n;
    printf("n> "); scanf("%d",&n); 
    isprime(n);
    printf("\n");
    return 0;
}

説明と考察

私は関数の中で全ての素因数を表示させたかったため、論理値を使わな
いコードになった。最初は教科書と同様にルートnまでwhile文で繰り返
していたが、nが素数の時は何も表示されなかったため、iをnまで繰り
返すことにした。その結果、nに素数を入力しても正しく表示されるよ
うになった。しかし、これではルートnまでの場合よりプログラムの実
行時間がかかってしまうことが問題だと考える。

アンケート
Q1,新しいことはあまりなかったので、そこまで苦労しなかった。
Q2,基本的な図形は簡単にかけて良かった。図形を組み合わせるのは大変だと思った。
Q3,次も頑張ります。

r1910720 1b:○ Mon May 18 09:36:54 2020


プログラミング通論(火3) #1 課題1b

課題の再掲:演習1a:直角三角形の直角をはさむ2辺の長さを入力し、斜
辺の長さを出力すること。

プログラムのソース:
// triarea --- length of a triangle
#include <stdio.h>
#include <math.h>
double triarea(double w, double h) {
  double s = sqrt(w*w+h*h);
  return s;
}
int main(void) {
  double w, h;
  printf("w> "); scanf("%lf", &w);
  printf("h> "); scanf("%lf", &h);
  double x = triarea(w, h);
  printf("斜辺の長さ = %g\n", x);
  return 0;
}

説明:直角三角形の斜辺の求め方は、各直角の辺を二乗して足す。さら
に平方根をとること。このプログラムでは、直角三角形の2つの辺を順
序入れ、最後に斜辺の長さが出ること。

考察:今回使った方法は普通ですが、さらに便利な関数hypotがあると
事後知りました。これは自分の勉強不足である。今後勉強し続けたいと
考える。

ーーーーーーーーーーーーーーーーーーーーー

課題の再掲:演習2c:お天気サイト等から自分の居住地(または出身地)
に関する何らかのデータ(月毎平均気温、月毎降水量、月毎の晴れ日
数など) を取得し、分かりやすいグラフにする。

プログラムのソース:
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("40 40 translate");
  eps_drawline(0, 0, 360, 0);
  eps_drawline(0, 0, 0, 300);
    for(int i = 1; i <= 25; ++i) {
    eps_drawline(0, i*10, 360, i*10);
  }
  eps_fillrect(50, 0, 20, 139.8);
  eps_fillrect(100, 0, 20, 196.1);
  eps_fillrect(150, 0, 20, 181.9);
  eps_fillrect(200, 0, 20, 213.4);
  int f1 = eps_newfont("Helvetica", 10);
  eps_puts(f1, -30, 300, "hours of sunshine/h");
  int f2 = eps_newfont("Helvetica", 10);
  eps_puts(f2, 360, -20, "month");
  int f3 = eps_newfont("Helvetica", 10);
  eps_puts(f3, 90, -30, " hours of sunshine for tokyo from Jan to Apr in 2020")\
;
  int f4 = eps_newfont("Helvetica", 10);
  eps_puts(f4, -20, 50, "50");
  int f5 = eps_newfont("Helvetica", 10);
  eps_puts(f5, -20, 100, "100");
  int f6 = eps_newfont("Helvetica", 10);
  eps_puts(f6, -20, 150, "150");
  int f7 = eps_newfont("Helvetica", 10);
  eps_puts(f7, -20, 200, "200");
  int f8 = eps_newfont("Helvetica", 10);
  eps_puts(f8, -20, 250, "250");
  int f9 = eps_newfont("Helvetica", 10);
  eps_puts(f9, 50, -10, "Jan");
  int f10 = eps_newfont("Helvetica", 10);
  eps_puts(f10, 100, -10, "Feb");
  int f11 = eps_newfont("Helvetica", 10);
  eps_puts(f11, 150, -10, "Mar");
  int f12 = eps_newfont("Helvetica", 10);
  eps_puts(f12, 200, -10, "Apr");
  int f13 = eps_newfont("Helvetica", 10);
  eps_puts(f13, 50, 149.8, "139.8");
  int f14 = eps_newfont("Helvetica", 10);
  eps_puts(f14, 100, 206.1, "196.1");
  int f15 = eps_newfont("Helvetica", 10);
  eps_puts(f15, 150, 191.9, "181.9");
  int f16 = eps_newfont("Helvetica", 10);
  eps_puts(f16, 200, 223.4, "213.4");
  eps_close();
  return 0;
}

説明:気象庁より、2020年の東京の月ごとに日照時間のデータを取得し、
棒グラフを描きました。横軸は各月で、縦軸は日照時間である。今回とっ
た座標原点は(40,40)で、横軸の長さは360,縦軸の長さは300を取った。
さらにeps_drawlineを用いて縦軸に目盛り線を書きました。
eps_fillrectは月ごとの日照時間を棒になって書きました。f1~f12はグ
ラフの要素(横軸、縦軸のラベル、表の説明)、f13~f16は棒の数値を
わかりやすくため、具体的な数値を書いたこと。

引用:http://www.data.jma.go.jp/obd/stats/etrn/view/monthly_s1.php?prec_no=44&block_no=47662&year=2020&month=&day=&view=a4

考察:目盛り線の書き方が横軸と縦軸のラベルの書くことにうまく行か
なかった。そのため、沢山のフォントを使ってしまい、大変工夫した。
for文の使い方がフォントに適用するのが難しいと考えました。

アンケート:
Q1. プログラムを作るという課題はどれくらい大変でしたか?
数値の計算は大体行けるですが、画像のプログラムの書くことが大変で
した。画像を描くため工夫しないといけないとわかった。

Q2. epsライブラリを使ってみてどのように感じましたか。
作業量が大変ですが、最後に画像が出来たことが嬉しいです。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
画像を描くことが面白でした。今後もいろんな画像を作りたいと思います。

s1910346 1b:○ Mon May 18 22:59:25 2020



--------ソース------------------------
#include <stdio.h>
#include <math.h>
#include "eps.h"

int main(){
    eps_open("out.ps", 480, 480);
    eps_cmd("240 240 translate");
    eps_drawline(-200, 0, 200, 0);
    eps_drawline(0, 200, 0, -200);
    for(int i = -200;i < 200;i++){
        eps_drawline(i, pow(i, 2), i + 1,pow(i + 1, 2));
    }
    eps_close();
    
}
--------------------------------------
グラフ上にy = x^2のグラフを表示する
プログラムを作成した。
とりあえず1plotずつずらして表示させてみたがうまく表示できた。
eps_deawlineの引数がdouble型であっても
大丈夫なのか気になった.

--------ソース2------------------------
#include <stdio.h>
#include <math.h>
#include "eps.h"

int main(){
    eps_open("out.ps", 480, 480);
    eps_cmd("240 240 translate");
    eps_drawline(-200, 0, 200, 0);
    eps_drawline(0, 200, 0, -200);
    for(int i = -200;i < 200;i++){
        eps_drawline(i, 30 * sin(i / 30), i + 1,30 * pow((i + 1) / 30));
    }
    eps_close();
    
}
--------------------------------------
グラフ上にy = 30 * sin(x/30)を表示する
プログラムを作成した。先ほどのx^2の関数の場合と異なり
線がガタガタとしたものになってしまった
より画素数の高い図を作るとうまく表示することができた.

Q1. プログラムを作るという課題はどれくらい大変でしたか?
構文のエラーを直すのは比較的簡単だったが,
プログラムが思った通りに動かないときに修正するのが難しかった
Q2. eps ライブラリを使ってみてどのように感じましたか。
使いやすいと感じた.
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。

C言語の基本的な文法について覚えていることを確認できてよかった.

t1810743 1b:○ Mon May 18 22:56:10 2020



.好きな関数のグラフを描画する
#include <stdio.h>
#include <math.h>
#include "eps.h"
int main(void) { 
    eps_open("out_b1_1.ps", 480, 480); 
    eps_cmd("240 240 translate"); 
    eps_drawline(-200, 0, 200, 0); 
    eps_drawline(0, 200, 0, -200); 
    for(float i = 0; i <= 100; i+=0.01) {
        eps_drawline(i*sin(i), i*cos(i), (i+0.02)*sin(i+0.02), (i+0.02)*cos(i+0.02)); 
    }
    eps_close();
    printf("finished\n");
    return 0;
}

説明
極座標表示の(r=θ,θ)を描画した。
結果は渦巻きであった。
i=θとして0~100まで0.01ずつ増やし、iと(i+0.02)における関数の座標を線で結ぶ。
はじめはiと(i+0.01)の関数の座標を線で結んで描画したが、生成された画像をアップすると
線が完全に繋がっておらず、ギザギザしていて気持ち悪かったので、
0.02にすることで少し線を重ねた結果、かなり見栄えがよくなった。



・データを可視化し、グラフにする
#include <stdio.h>
#include "eps.h"
#include <math.h>
int main(int argc, char* argv[]) { 
    FILE *fp;
    fp = fopen(argv[1], "r"); 
    if(fp == NULL) {
        fprintf(stderr, "File open failed\n");
        fclose(fp);
        return 0;
    }
    eps_open("out_b1_2.ps", 480, 480); 
    eps_cmd("0 240 translate"); 
    eps_drawline(0, 0, 400, 0); 
    
    float a, b, c;
    while(fscanf(fp, "%f %f %f\n", &a, &b, &c) != EOF){
        eps_fillcircle(a, -b, pow(0.75, c)*3); 
    }
    fclose(fp);
    eps_close();
    printf("finished\n");
    return 0;
}

説明
天文台から100個の星の位置と明るさを取得し、
描画するプログラム。
サイトから取得した星の赤経赤緯、等級をテキストファイルに保存し、取得する。
テキストファイルは
赤経 赤緯 等級
の形で100個の恒星のデータがある。
赤経をx座標、赤緯をy座標、等級を面積として対応させて円を描画する。
東京の夜空よりは多くの星が描画されたが、私の地元の長野に比べれば寂しい星空であった。
星座までは認識できない個数であったが、もっと多くの恒星データを
使用すればより綺麗な星空が描画できる。

・アンケート
Q1:レポートにまとめるよりプログラム書く方が楽だし楽しいです。
Q2:Processingみたいだと思いました。アンチエイリアスの機能などが
付いているのか気になりました。
Q3:描画できるプログラミングは楽しかったです。

w1810714 1b:○ Mon May 18 16:10:30 2020


提出日 5/18

課題1b. 整数nを入力し、2のn乗を出力する。
ソースコード
#include <stdio.h>
void pow2(int n) {
  double k = 1;
  if(n>=0) {
    for(int i = 0; i<n; ++i) {
      k = k*2;
    }
    printf("%g\n", k);
  } else {
    for(int i = 0; i>n; --i) {
      k = k/2;
    }
    printf("%f\n", k);
  }
}
int main(void) {
  int n = 0;
  printf("n> ");
  scanf("%d", &n);
  pow2(n);
  return 0;
}
結果
[w1810714@sol 1]$ ./a.out
n> 0
1
[w1810714@sol 1]$ ./a.out
n> 5
32
[w1810714@sol 1]$ ./a.out
n> -4
0.062500

説明

nとして入力された回数だけ1に2を掛ける(n<0なら割る)ようにしている。
場合分け時にprintfの書式を変えることで、nが正なら整数、負なら実
数で表示している。

考察

関数pow2内でnの正負の場合分けを行い、printfを用いた表示まで終わ
らせていることでmain関数内の記述が極めて簡潔なものになった。

課題2d. epsライブラリを利用して面白い描画を行う。
ソースコード
#include <stdio.h>
#include "eps.h"
int main(void) {
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  eps_cmd("0.3 setgray");
  eps_fillrect(-240, -240, 480, 480);
  for(int i = 1; i<=20; ++i) {
    eps_num(i*0.05); eps_cmd("5.0 5.0 sethsbcolor");
    eps_drawrect(-i*14, -i*14, i*28, i*28);
    eps_drawcircle(-250, 50, i*30);
    eps_drawcircle(280, 280, i*40);
  }
  eps_cmd("0.8 0 0.6 setrgbcolor");
  eps_drawcircle(-200, -200, 100);
  eps_cmd("0 0.3 0.9 setrgbcolor");
  eps_drawcircle(200, -200, 80);
  eps_cmd("0 0.7 0.5 setrgbcolor");
  eps_drawcircle(100, 200, 120);
  eps_close();
  return 0;
}

説明

描画面と大きさが等しい四角形を塗りつぶし、これを背景とした。また、
いくつかの正方形が放射状に描画されるようにした。その後円を複数個
並べた。

考察

正方形(四角形)の基準点が左下であったから、その(-(正方形の大きさ
の半分))を基準点のx, y座標に設定してあげることで正方形が常に描画
面の中心に表示されるようになっている。

アンケート
1.去年やった内容なのでそこまで苦にはなりませんでした。
2.図形を重ねていくと楽しいです
3.次回も頑張ります

w1910722 1b:○ Mon May 18 13:30:59 2020


学籍番号 1910722
個人作業
提出日時 5月18日

1つ目の課題
演習1のa
#include <stdio.h>
#include <math.h>
double triline(double a, double b) {
  double c = sqrt(a*a + b*b);
  return c;
}
int main(void) {
  double a, b;
  printf("a> "); scanf("%lf", &a);
  printf("b> "); scanf("%lf", &b);
  double x = triline(a, b);
  printf("line of triangle = %g\n", x);
  return 0;
}
説明
直角三角形の斜辺以外の2辺を与えて、斜辺の長さを計算し出力するプログラム。

考察
斜辺の値は他辺の2乗の和の平方根をとったため、整数型のintではな
くdoubuleにしたことで正確な実行結果が得られた。今回のプログラム
ではaとbの値のどちらも実数型であったが、どちらかの値、もしくは両
方の値を整数型にするとどのような実行結果になるのかは興味が沸いた。

2つ目の課題
演習2のa
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("1b.ps", 480, 480);   
  eps_cmd("240 240 translate");   
  
 int a = 0;

  for(int i = 0; i <= 29; ++i) {
    eps_cmd("4 setlinewidth");
    if (i % 6 == 0){
         a = a + 70;
     };         
    eps_drawrect(0 + (i%6)*50,a, 30, 30);  
  }    
  eps_close();                          
  return 0;
}
説明
太さが4ptの正方形を5×6の計30個描画するプログラム。
考察

x座標の起点は0で、ループ処理の度に増加するiを6で割った余りを活
用した。また、ループの度にiを6で割った余りの値が0かどうかを判定
し、0ならば正方形を描画するy座標の値を大きくするようにした。こ
れらによって、eps_drawrect1つで30個の正方形を描画できた。他に
も、5つのeps_drawrectを使って縦一列に正方形を描画し、x座標を変化
させるだけで30この正方形を描画することなども考えられたが、不必
要にコードが長くなるなどのマイナスの要素の方が多いだろうと判断し
たため、今回はこのようにプログラムを作成した。

Q1.休業期間中にあまり触れていなかったことや、そもそもC言語で理解
できていない事項が多々あるということもあって、戸惑うことがいくつ
かあり大変だったと感じた。

Q2.一つ一つのコマンドを理解することは大変ではなかったが、一つの
プログラムで大量に記述されていると、どのコマンドがどの描画と対応
しているかなどを理解することが少し大変だと感じた。

Q3.自分のプログラミング能力の低さがよく理解できたため、予習復習
に加えて時間が空いた時になんでもいいからプログラミングをしようと
感じた。

w1910726 1b:○ Tue May 12 14:22:41 2020


個人作業
提出日時 2020/5/12 14:22

演習1b

ソースコード
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>

int main()
{
    char buf[50];
    double n;
    printf("整数を入力してください。");
    fgets(buf, sizeof(buf), stdin);
    int ScanDid = sscanf(buf, "%lf", &n);

    if(ScanDid == 1)
    {
        if(n > 0) { printf("%d", (int)pow(2.0, n));}
        else { printf("%lf", pow(2.0, n));}
        return 0;
    }

    printf("入力が間違っています.");
    return 0;
}

説明
fgetsで入力するためのchar配列であるbufを用意し大きさは余裕をもって50バイトにした。
scanfではなくfgetsを用いた理由はsscanfを用いてそれが成功したかの
判断に用いる戻り値を受け取るためである。
そして、受けとった戻り値であるScanDidを用いてif文を分岐させelse
の場合は適切に入力されていないので、
入力が間違っていますと表示し終了。数字であった場合はif文で正と負
を分けそれぞれでpow関数を用いて表示させた。
pow関数の戻り値はdouble型なので整数で出力させる必要のある正の場
合では(int)でint型にキャストした。

考察
pow関数の戻り値をよく確認しなかったためにエラーが表示されないエ
ラーが出てしまったので、これは以前にも似たようなことがあったので
気を付けておきたいと思います。
また、if文やfor文の中身が一行の時は一行の時は見栄えのためによく
一行にまとめてしまうのですがこれは推奨されていることなのかと疑問
に思いました。

演習1d

ソースコード
#include<stdbool.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>

void PrimChec(int i, int *PriNumHe, int *HeCou);

int main()
{
    int PriCou, HeCou = 0;
    char buf[16];
    int *PriNumHe;
    printf("入力した自然数以下の素数を出力します。自然数を入力してください。\n");
    fgets(buf, sizeof(buf), stdin);
    int ScanDid = sscanf(buf, "%d", &PriCou);

    if(ScanDid == 1)
    {
        PriNumHe = (int*)malloc(sizeof(int) * PriCou);
	PriNumHe[0] = 0;	

        for(int i = 2; i <= PriCou; i++) { PrimChec(i, PriNumHe, &HeCou); }

        if(PriNumHe[0] == 0)
        {
            printf("入力された自然数以下の素数は存在しません。\n");
	  free(PriNumHe);
            return 0;
        }

        for (int i = 0; i < HeCou; i++) { printf("%d ", PriNumHe[i]); } 
    }
    else
    {
        printf("入力が間違っています。\n");
    }
    
    free(PriNumHe);
    return 0;
}

void PrimChec(int i, int *PriNumHe, int *HeCou)
{
    for (int j = 2; j < i; j++)
    {
        if(i % j == 0) { return; }
        if(j == i - 1) { PriNumHe[(*HeCou)++] = i;}
    }      
}

説明
演習1bと同じようにfgetsとsscanfを用いた。また、探し出した素数を
格納するための配列としてint型の動的配列を用いた。
なぜ、用いたのかというと通常の配列では入力されたnの値によって配
列の大きさを変えることができないため、nの入力によって大きさを変
えることができる動的配列を用いた。
素数であるかを調べるための関数としてPrimChec関数を用意した。
またこの関数はmain関数の下にあるため上にプロトタイプ宣言を用意した。
この関数は与えられた数iをfor文を用いてそれ以下の数で割っていきも
し割り切れるものがあった場合その時点で素数ではないのでreturn文で
関数を終了し次のiの値で再び同じ動作を行った。
この途中で終了する動作を実現させるためにこの過程を関数とした。
もし、この過程をreturnされずに最後まで行ったとしたらその数は自分
自身と1でしか割れない素数であるのでこれを動的関数に格納していっ
た。
また、この動作は1が含まれているとうまく働かないので1を除外している。
格納するときのインデックスはHeCou変数を用いて管理。
後置インクリメントを用いて値を格納した後にHeCouをインクリメントしていく。
これを、for文を用いて入力されたnの数だけ繰り返す。
あらかじめ動的配列の0番目に0を代入しておいたので0番目が0かによっ
て入力されたn以下の素数が存在するかどうかが分かる。
存在しない場合は、その旨のメッセージを表示させ終了。
存在する場合は、HeCou変数分だけfor文で動的配列の中身を表示させた。
returnで終了させるときは忘れずにfreeで動的関数を開放して終了した。

考察
今回のプログラムには動的配列を用いました。この動的配列はfreeをしっ
かり行わないとメモリがプログラム終了後も残ってしまうという問題点
があるが、有用であるので問題点を考慮しながら使っていくことが大事
だと感じた。
動的配列の開放に関してはgoto文を例外的に用いてもよいと感じた。
goto文を用いればメモリの開放の手順が一つにまとめられるのでもっと
ソースコードがスッキリ考えた。

アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
それほど大変ではないですがどんな時でもエラーが起きずにするように
するようにするとさまざまな状況を想定しなければならないので大変だ
と感じました。

Q2. eps ライブラリを使ってみてどのように感じましたか。
難しそうでしたが、よく読んでみると分かりました。新鮮で面白かったです。

Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
引き続き頑張りたいと思います。

w1910727 1b:○ Mon May 18 23:09:04 2020



1つ目の課題の掲載
演習1-c. 整数n (n> 1)を入力し、nの素因数分解を表示する。
作成したプログラムのソース
#include <stdio.h>

int factprime(int n){
  int i;
for(i=2; i<=n; ++i){
  if(n%i==0){
    printf("%d ", i);
    n=n/i;
      i=1;
  }
 }
}
int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  factprime(n);  
}

プログラムの説明
数を入力すると、その数の素因数が表示されるプログラムである。ある
数nを、iで割った余りが0ならばその数iはnの因数であることを利用し、
for文とif文を用いて因数iを見つけてはnを因数iで割り、nが1になるま
でループさせることで素因数分解を行う。

考察および疑問点
このプログラムならばnが素数でも因数分解ができると考えるが、途方
もなく素数や数に対する処理速度は遅いと考えられる。より速い処理を
行えるプログラムがあると考えられる。また、因数分解をするプログラ
ムなので、nが1や少数の時にも対応できるとよりよいと考えた。

2つ目の課題の掲載
演習2-a. 正方形が縦横一定間隔に、計16個あるような絵を作成する。

作成したプログラムのソース
#include <stdio.h>
#include "eps.h"

int main(void){
  eps_open("out.ps", 480, 480);
  for(int t = 1; t <= 4; ++t) {
  for(int i = 1; i <= 4; ++i) {
    eps_num(i*0.1);
    eps_cmd("setgray");
    eps_cmd("4 setlinewidth");
    eps_drawrect(i*80,t*80,60,60);
  }
  }
  return 0;
}

プログラムの説明
drawrectを用いてepsで正方形を描写し、for文を二度用いることで、正
方形のx方向とy方向移動を行い、簡単に16個の正方形を一定間隔で描写
するプログラムである。

考察および疑問点
forを二回用いることで簡潔に指定の絵を作成できたと考えるが、これ
よりも短くするプログラムは思いつかなかった。このような規則的な配
置をする絵に関しては、for文を用いて連続的に描写するのが効率的で
あると考える。

アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
かなり考えるものもあったので、やや大変でした。
Q2.epsライブラリを使ってみてどのように感じましたか。
簡単に図形が描写でき、便利だなと感じた。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
かなり基礎的な課題であったので、復習としてもってこいだったと感じた。

y1810658 1b:○ Mon May 18 22:39:02 2020


学籍番号:1810658
個人作業
提出日時:2020/5/18

一つ目の課題の再掲:1-a
直角三角形の直角をはさむ2辺の長さを入力し、斜辺の長さを出力する。

プログラムのソース:
#include <stdio.h>
#include <math.h>

int main(void){
  float a,b,c;

 printf("三角形の一辺の長さ:\n");
 printf("a=");
 scanf("%f",&a);
 printf("b=");
 scanf("%f",&b);

 c = sqrt(a*a + b*b); //斜辺を計算する
 printf("c = %.3f\n",c); //斜辺の値を出力する

 return 0;
}

考察
printfで細かく入力の誘導をしていて使いやすくなっていると思う。ま
た余計なコードは入れずに見やすく、誰が見ても内容を追いやすいプロ
グラムだと思う。


二つ目の課題の再掲:1-b
整数nを入力し、2**nを出力する。n≥0のときは整数、そうでないときは実数形式で出力すること。

プログラムのソース:
#include <stdio.h>
#include <math.h>

double ninon(int n){
     double ans = 1.0000;
      int a;
    for (a=1; a<=abs(n); a++){  //絶対値でnをとり正負での場合分けの手間を省く
      ans = ans*2.0000; //ループさせて2**|n|をもとめる
    }
     if(n<0){
       ans = 1.0000 / ans; //n<0の場合
    }
    return ans;
  }

  int main(void){
    int n;
    double ans;
    printf("n>"); scanf("%d",&n);
    ans = ninon(n);
    if(n>=0){printf("%d\n", (int) ans);return 0;}
    printf("%lf\n", ans);
    return 0;
  }

考察
今回はnが負の値をとるとき答えが小さくなることが考えられたので
double型をとった。nが正負の場合をfor文のループで分けて求める形を
とったがwhile文でも書ける。今回は自分の書き慣れたfor文を選んだ。

アンケート
q1:結構大変でした。
q2:今回は課題に選びませんでしたが自習時間に使用したいと思います。
q3:復習をしっかり行う。また今回は演習2から選ばずに課題をこなし
たので次回は挑戦したい。

y1910661 1b:○ Mon May 18 20:54:05 2020


2020/5/12

演習1e nの値を入力されたらある数列の最初のn項を出力するプログラム
[プログラム]
#include<stdio.h>

int main(void){
  int n;
  int a = 1,b = 1,c;
  printf("n> ");scanf("%d",&n);
  for(int i =0;i<n;i++){
    if(i < 2){printf("1 ");}
    else {
      c = a+b;
      a = b;b = c;
      printf("%d ",c);
    }
  }
  printf("\n");
  return 0;
}
[作成したプログラムの説明]
実行するとまずnの値の入力を求め,
入力されたらフィボナッチ数列の最初のn項を項の間にスペースを入れて画面に表示し
最後にエスケープをするプログラム.
[考察]
今回int型の大きさの整数が入力されてなおかつ,
対応するフィボナッチ数列もint型の範囲内であることを暗黙の了解としたが,
n=50程度でマイナスの値を返すので,int型は最上位ビットで符号を定めていることが分かった.
そしてフィボナッチ数列は途中からint型の範囲を超えてしまって正しい答えが出ないが,
これを解決するために入力値をどのように制限すればいいかは実際に計
  算しないといけないのが難しい.
  
演習2c 天気サイトから自分の居住地に関するデータを取得し,グラフにする.
[プログラム]
#include<stdio.h>
#include "eps.h"

int main(void){
  int width = 120;
  int height = 120;
  int x0 = 10;
  int bw = 120 / 12;
  //東京の月間降水量 /mL
  double rain[] = {16,42,117.5,90.5,120.5,225,193,110,197,529.5,156.5,76.5};
  //東京の月間平均気温 /C
  double temp[] = {5.6,7.2,10.6,13.6,20.0,21.8,24.1,28.4,25.1,19.4,13.1,8.5};

  eps_open("c.ps",240,240);
  eps_cmd("0 10 translate");
  //軸を作る
  eps_drawline(x0,0,x0,height);//左 y
  eps_drawline(x0+width,0,x0+width,height);//右 y
  eps_drawline(x0,0,x0+width,0);//x軸
  eps_cmd("0 255 255 setrgbcolor");
  int i = 0;
  //降水量の表示
  for(i=0;i<12;i++){
    eps_fillrect(bw*i+x0+3,0,5,rain[i]/5);
  }
  //気温の表示
  eps_cmd("255 0 0 setrgbcolor");
    for(i = 0;i<11;i++){
    eps_drawline(bw*i+x0+bw/2,temp[i]*3,bw*i+bw/2+bw+x0,temp[i+1]*3);
  }
  //数字など
//メモリ戦
  for(i=0;i<6;i++){
    eps_drawline(x0-1,height/5 * i,x0+1,height / 5 *i);
    eps_drawline(x0-1+width,height/5 * i,x0+1+width,height / 5 *i);
  }
  //文字列
  int f1 = eps_newfont("Courier", 5);
  int f2 = eps_newfont("Courier", 10);
  eps_puts(f1,x0+width+5,height+10,"Precipitation/ mm");
  eps_puts(f1,x0-5,height+10,"temperature/Celsius");
  eps_puts(f2,x0,height + 30,"2019 Tokyo");
  //月
  char str[8];
  for(i = 0;i < 12;i++){
    sprintf(str,"%d",i+1);
    eps_puts(f1,x0+bw*i + 2,-10,str);
  }
  //気温目盛
//  char str[8];
  for(i = 0;i < 6;i++){
    sprintf(str,"%d",i*5);
    eps_puts(f1,x0 - 10,height / 5 * i,str);
    sprintf(str,"%d",i*120);
    eps_puts(f1,x0+width+10,height / 5 * i,str);
  }
  eps_close();

}
[作成したプログラムの説明]
2019年の東京の月平均気温と降水量をpostscriptでグラフ表示するプログラム.
赤の折れ線が気温を表し,青の棒グラフが降水量を表す.
[考察]
文字列を縦向きにできなかったので,temperatureなどが不自然になった.
sprintfを使うことによって書式付文字列が使えるので,eps_cmdがより直感的に使えるになる.
プログラムを細分化したときに,
作る順番を考えてなかったせいでプログラム全体が読みづらくなってしまった.
プログラムを書くとき,例えばy軸の長さなど,意味のある数字を変数
などでまとめていなかったので
修正するときどこを修正すればいいのか一目でわからなくて修正に時間がかかった.
先にプログラムの全体像をしっかり決めたほうが時間がかからずに済んだのかもしれない.

[アンケート]
Q1.
4時間くらいかかるほど大変だった.
Q2.
for文などでメモリをまとめて作れるのは便利だと思った.
Q3.
ベクター形式でグラフを書こうとすると,書きながら修正が難しいので大変だと思った.

y1910662 1b:○ Fri May 15 03:56:24 2020


個人作業
提出日時:2020/05/15


【課題1】演習1b
《プログラムのソース》
#include <stdio.h>

int plus(int n){
    int a = 1;
    for(int i=0;i<n;i++){
        a *= 2;
    }
    return a;
}

double minus(int n){
    int b = 1;
    for(int i=0;i<(n*(-1));i++){
        b *= 2;
    }
    double c = 1.0/b;
    return c;
}

int main(void) {
    int n;
    printf("n> "); scanf("%d", &n);
    if(n >= 0){
        int x = plus(n);
        printf("answer is %d\n", x);
    }else{
        double y = minus(n);
        printf("answer is %g\n", y);
    }
    return 0;
}
 
《プログラムの説明》
整数nを入力すると2^nを返すプログラムである。
main関数は入力と出力を担当している。nによって出力形式を変える必
要があることから、n>=0の時の計算を行う関数plusとそれ以外の時に計
算を行う関数minusを定義し、mainではif文でnによってどちらの関数で
計算した値を出力するか決まるようにした。
plusでは、C言語は累乗の演算子がないためfor文を用いて2をn回かける
ようにした。なおn=0の場合を考慮しかけられる数int aの初期値は1と
した。
minusでは、nが負の数であるとき2^n=1/(2^(-n))であることを利用し、
まずnを正の数にした上でplusと同じようにfor文で計算を行い、1を得
られた値で割るようにした。この時結果が実数形式にしなければならな
かったためdouble型を利用した。
   

《考察》
出力形式が異なるためmainの中でnの条件によってif文で分岐させる必
要があると考え、それならばそれぞれに対応した関数を用いればよいの
ではないか、と考えてこのようなプログラムを書いた。
そのため処理はわかりやすくなっているとは思うが、同時にプログラムが長いと感じた。
plusでもminusでも、for文でほとんど同じ処理を行っていることから、
計算部分は1つの関数にまとめて、if文を用いてnが負の場合はnの符号
を反転させてからfor文に入り、そのあと1を割るという構成にしたほう
がスマートだったかもしれない。
また、累乗についても今回はfor文で処理したが、pow関数を用いた方が
より短く書けたかと思う。

【課題2】演習1e
《プログラムのソース》
#include <stdio.h>

int formula(int n){
    int answer;
    if(n==0){
        answer = 1;
    }else{
        answer = formula(n-1)*n;
    }
    return answer;
}

int main(void) {
    int n;
    printf("n> "); scanf("%d", &n);
    for(int i=1; i<=n; i++){
        int x = formula(i);
        printf("%d\n", x);
    }
    return 0;
}
 
《プログラムの説明》
整数nを入力し、数列の第n項までを出力するプログラムである。任意の
数列でよいとのことだったので、数列を{an}と表せる時、a1=1、
a(n)=a(n-1)*nである数列を実装した。
   
main関数は入力と出力を行う。初項から第n項までを順に出力する必要
があることからfor文の中にprintfを入れて、第i項の値を計算する関数
formulaに1~nを順に代入していきその結果をその都度出力するように
した。

formulaは再帰呼び出しで今計算している項の1つ前の項を引っ張ってき
て、そこに対してnをかけるようにした。なお初項を計算する時に必要
になるためn=0の時にformulaで返ってくる値は1としておいた。

《考察》

再帰呼び出しを用いることで、前の項と何らかの関係がある数列を計算
するプログラムを簡単に実装することができる。これを利用すればより
複雑な数列も簡単に計算させることができそうである。

main関数内のfor文について、int iは単純に処理を行う回数だけではな
く、formulaに代入する値、すなわち第i項を計算することを表している
ため、n個の値が得られればよいからといってint i=0;i<nとしてしまう
と計算結果がずれてしまうので注意が必要である。

また、この部分の値を変えることによって、初項から第n項までを計算
するだけでなく第2、第3項など数列の途中から計算をおこなったり、第
n項から先の項の計算を行うこともできる。

アンケート:
Q1:C言語を触るのが久しぶりだったので思い出しながら書くという感じ
だったが、復習であったためそこまで苦労はしなかった。

Q2:epsライブラリについて、自分のパソコンでは環境構築がうまくいか
ず、色々調べながらやってみたもののきちんと動作するところまで持っ
ていけなかったので、最初の環境構築が面倒だなとは思った。しかしう
まく動けばプログラムが何をやっているかが目に見えてわかるというと
ころは良いなと思った。

Q3:今回は1年次の復習だったが今後難しくなっていくと思うので、頑張っていきたい。

y1910668 1b:○ Tue May 12 20:47:58 2020


学籍番号:1910668
ペア:個人作業
提出日時:2020/5/12

<一つ目の課題>

(再掲)
2-a(2)…四角がマス目のように飛び飛びで描いた

(ソース)
#include <stdio.h>
#include "eps.h"

int main(void){
  eps_open("out.ps",450,450);
  for(int i = 0; i <= 4; i+=2) {
    eps_cmd("255 0 0 setrgbcolor");
    eps_fillrect(0,i*90,90,90);
    eps_fillrect(90,(i+1)*90,90,90);
    eps_fillrect(180,i*90,90,90);
    eps_fillrect(270,(i+1)*90,90,90);
    eps_fillrect(360,i*90,90,90);		
  }   
  eps_close();                          
  return 0;
}

(説明)
一辺の長さを5分割できる450にして、縦列ごとに資格をループで描いた。
一辺を450にしているので四角の一辺は90となり、各列のx座標は90毎に
大きくなる。そしてy座標は、1,3,5列目と2,4列目に分けてループを使っ
て決めた。四角は一つ飛びなので変数のiは2ずつの上昇にした。

(考察)
図形を描くとき、どこが基準となっているのかを注意しなくてはならな
いと改めて分かった。

<二つ目の課題>

(再掲)
2-a(3)…中が白の円を四辺に沿って描いた
(ソース)
#include <stdio.h>
#include "eps.h"

int main(void){
  eps_open("out.ps",450,450);
  for(int i = 0; i <= 4; ++i) {
    eps_cmd("0 0 0 setrgbcolor");
    eps_fillcircle(45,i*90+45,45);
    eps_fillcircle(405,i*90+45,45);
    eps_fillcircle(i*90+45,45,45);
    eps_fillcircle(i*90+45,405,45);
    eps_cmd("255 255 255 setrgbcolor");
    eps_fillcircle(45,i*90+45,44);
    eps_fillcircle(405,i*90+45,44);
    eps_fillcircle(i*90+45,45,44);
    eps_fillcircle(i*90+45,405,44);   
  }   
  eps_close();                          
  return 0;
}

(説明)
こちらも円が5つ入る図形なので枠を450で作り、円の半径を45とした。
描くときは、左、右、上、下の4つに分けてループを使い黒い円を描い
た。その後、ほんの少し半径の小さい白円を同じ基準で上から塗りつぶ
して穴の開いた円を作った。

(考察)
決まった間隔の並びは数値で管理するときれいにできることが分かった。
また、複雑な形も図形の組み合わせ、塗りつぶしなどを使えば作ること
ができると感じた。

<アンケート>
Q1.楽しかったのでそこまで大変ではなかった。
Q2.難しくはあるが、使いやすいものだと感じた。
Q3.今回のは、まだ余裕のある程度だったので楽しくできた。

y1910670 1b:○ Mon May 18 14:10:36 2020


学籍番号:1910670
ペア:個人作業
提出日時:5月18日

・一つ目の課題

演習1b「整数nを入力し、2^nを出力する。n >= 0の時は整数、そうでな
い時は実数形式で出力すること。」

<コード>

<コード>

#include <stdio.h>
#include <stdbool.h>
double triarea(int n) {
    int i = 1;
    int s = 1;
    while(i <= n){
        s = s * 2;
        i++;
    }
  return s;
}

int main(void) {
  double n;
  printf("n> "); scanf("%lf", &n);
    if(n >= 0) {
        int x = triarea(n);
        printf("%d\n is positive.\n",x);
        
    }
    else {
        double x = triarea(n);
        printf("%f\n is not positive.\n",x);
        
    }
    return 0;
}

<説明>

whileを用いて、n回2をかけるという考え方で計算を行った。

<考察>

nが0以上かどうかの判別をどこに入れるかによって、nの定義が矛盾し
てしまう場合があるので、そこを十分注意する必要があった。

・二つ目の課題

演習1c「整数n (n > 1)を入力し、nの素因数分解を表示する。たとえば
60を入力したら「2 2 3 5」を出力する(出力の順番や形式は任意)。」

<コード>


#include <stdio.h>
int prime(int n){
    int i, num;
    num = n;
    i = 2;
    while(num > 1){
        if(num % i == 0){
            num = num / i;
            printf("%d ", i);
        }else{
            i += 1;
        }
    }
}

int main(void) {
    int n;
    printf("n> "); scanf("%d", &n);
    prime(n);
    return 0;
}


<説明>

iを2から増やしていくが、iでnを割り切れなかったら、iに1足して行
き、割り切れたら、iはそのままで、n = n ÷ iにする。

<考察>

同じ因数が複数持つ整数の場合の因数の羅列方法に十分注意する必要が
ある。それを今回は、割り切れるかどうかと、iを1ずつ増やして行く
という方法で表した。

・アンケート
Q1:大変でした
Q2:ちょっと難しい
Q3:なし

y1910671 1b:○ Mon May 18 18:21:47 2020


課題1b

個人作業
提出日時: 2020/05/18

No.1
演習1-c

[課題の再掲]
正の整数nを入力してその素因数分解を表示する。

[ソースコード]
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

// 1-c
void primeFact(int n){
    int i = 2;
    while(n != 1){
        while(n % i == 0){
            printf("%d ", i);
            n /= i;
        }
        i++;
    }
    printf("\n");
}

int main(void){
    int n;
    printf("n > "); scanf("%d",&n);
    primeFact(n);
    return 0;
}

[実行例]
[y1910671@sol #1]$ ./a.out
n > 60
2 2 3 5 
[y1910671@sol #1]$ ./a.out
n > 8
2 2 2 
[y1910671@sol #1]$ ./a.out
n > 6
2 3 
[y1910671@sol #1]$ ./a.out
n > 100
2 2 5 5

[説明]
nを引数として受け取って素因数分解をするメソッド(返り値なし)と
nを入力させるmain関数部分で2つに分けた。
素因数分解のメソッドprimeFactはnを受け取ってprintfによって画面に
割った数を順番に出力する。ここでいう割る数というのは、nの制限が2
である事より初期値を2で設定してnへと何回も割っていき、割ることが
出来なくなったらインクリメントしている。main関数部ではnを入力さ
せる部分と、画面に文字を出力するprimeFactを実行している。

[考察]
例えばnに60を入れたときに2 2 3 5と出力するためには、
同じ数で割る事がある→つまり回数ループの変数のように単純にiを設定すると
2が二回続くような出力にならないことを考えた。
そのため、まずはじめのループの終了条件をn == 1とした。
これはできるだけ小さい数(ここではi)を割っていくときに、
n = n/iとしてどんどん小さくしていけば必ず最終的にn == 1となるからである。
素因数をそれぞれ表示する部分はnをiで割った余りが0である間、そのiを
出力し続ければそれすなわち素因数になると考えたため2つめのループ
として入れ子にした。

No.2
演習1-e
[課題の再掲]
nを入力して、何らかの数列の最初のn項を出力する。

[ソースコード]
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

// 1-e

int fib(int n){
    if(n == 1 || n == 2){
        return 1;
    }else{
        return fib(n - 1) + fib(n - 2);
    }
}

void seq(int n){
    int i;
    for(i = 1; i <= n; i++) printf("%d ",fib(i));
    printf("\n");
}

int main(void){
    int n;
    printf("n > "); scanf("%d",&n);
    seq(n);
    return 0;
}

[実行例]
[y1910671@sol #1]$ ./a.out
n > 5
1 1 2 3 5 
[y1910671@sol #1]$ ./a.out
n > 10
1 1 2 3 5 8 13 21 34 55 

[説明]
数字nを入力すると、フィボナッチ数列の最初のn項が出力されるようにした。
fibメソッドは引数に与えた整数項目のフィボナッチ数列の値を返す。
seqメソッドは1から引数に与えたnまでfibメソッドから受け取った値
を画面に出力する。mainではnを入力してseqを呼び出すことで求めたい
数列の1項目からn項目を出力する。

[考察]
フィボナッチ数列は第n項目の値が直前の項の数字とそのもう一つ前の
項の数字を足したものである。このことからfibメソッドでは再帰的に
n項目を求めるのが効率的だと考えた。コードの見やすさを考えて数列
の項の導出と出力を分けたが、それらを1つに統合することもできる。
具体的には、fibメソッドの中身をループで包んで、ループ変数のイン
クリメント時にprintfして出力すればよいと考えた。

~アンケート~
Q1.早寝早起きすることと比べたらそこまで大変ではない。
Q2.画像処理はターミナルで数値計算することと比べて難しい。
Q3.様々なアルゴリズムを学んでいきたい。

y1910673 1b:○ Sun May 17 01:46:45 2020


プログラム通論第1回B課題
学籍番号:1910673
氏名:山﨑 千寛
提出日時:5月17日 

[取り組んだ課題‐1つ目]
演習1.c. 整数 n (n > 1) を入力し、n の素因数分解を表示する。
たとえば 60 を入力したら「2 2 3 5」を出力する (出力の順番や形式は任意)。 

[作成したプログラム]
#include <stdio.h>

int solve(int n) {
  int s; int i;
  for(i = n - 1; i > 0; --i) {
    if(i == 1) {return n;}
    else if(n % i == 0) {s = n / i; printf("%d \n", s); solve(i); break;} 
  }
}
int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  printf("%d\n",solve(n));
  return 0;
}

[プログラムの説明]
solve内のfor文において、i:n-1→1というように進めていくにつれて、
nを割り切れるiが見つかったときに変数sを n / i で入力しnの約数と
してprintfで出力する。
この後、nを約数sで割った数としてiを使ってsolve(i)で再帰的に約数を打ち出していく。
最後の素数に行き着いたらfor文内での変数iは1に行き着くのでその時
最後の素数nを返して再帰を終える。
その後、再び再帰が起こらないようにbreakでfor文を抜けて終了する。

[考察]
再帰的に処理することで少ない文字数のプログラムで動かすことができ
た点は非常に良かった。
また、solve(n)のnが素数のときfor文内で必ず変数iが1に行き着くこ
とから i == 1 のときnを返してきれいに出力を終えれるようにできた
のはよいと感じた。

[取り組んだ課題‐2つ目]
演習2.a 次の図のような絵を作成する。色や線幅は自由にしてよい
     (1) 
      □□□□ 
      □□□□
      □□□□
      □□□□

[作成したプログラム]
・既存のプログラムeps.cに追加したプログラム
void eps_square(double x, double y, double a) {
        rect(x, y, a, a); fprintf(fd, "stroke\n");
}

・メインのプログラムのソース
#include <stdio.h> 
#include "eps.h"
int main(void) {
	eps_open("out.ps", 480, 480); 
	eps_cmd("240 240 translate");
	for(int x = -240; x < 240; x = x + 120){
            for(int y = -240; y < 240; y = y + 120) {
                eps_square(x, y, 100);
            }    
        }
	eps_close(); 
	return 0;
}

[プログラムの説明]
・既存のプログラムeps.cに追加したプログラム"eps_square"
長方形に線を引くプログラムはすでにあったが、この課題では正方形を
使うため、作成したほうがいいと感じ作成した。
座標(x, y)と一辺の長さaの入力から出力できる。

・メインのプログラム
この課題で登場する正方形は16個だが規則的に並んでいるため、x軸
とy軸のそれぞれで繰り返して実行することで書くプログラムの量を減
らすようにした。
"eps_square"については上記の通りである。

[考察]
繰り返し文を使うことで書く量を減らし、全体的に見やすくできた点はよかった。
規則的な図形に関してはこのように繰り返し文を使うことでより効果的
にプログラムが作成できると感じたのと同時に、
描く範囲内で再帰的に繰り返して書くこともできると考えた。

[アンケート]
Q1. C 言語のプログラミングは好き/嫌いどちら? 理由は? 
なにがダメなのか比較的わかりやくいと思うから好きです。
Q2. eps ライブラリについてどのように思いましたか。 
便利だと思いました。
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
特にありません。

y1910677 1b:○ Mon May 18 18:47:38 2020


情報領域演習第二:#01b
提出日付 2020/5/18

【課題の再掲(演習1b)】
整数nを入力し,2のn乗を出力する.nが0以上の時は整数,そうでない
ときは実数形式で出力する.

【実施した内容】
プログラム:
#include <stdio.h>
#include <math.h>

void plus(int n){
  int result = 2;
  for(int i = 1;i<n;++i){
    result *= 2;
  }
    printf("answer=%d\n",result);
}

void minus(int n){
  double result = 0.5;
  for(int i = 1;i<-n;++i){
    result *= 0.5;
  }
  printf("answer=%lf\n",result);
}

int main(void){
  int n;
  int result = 1;
  printf("n>");scanf("%d",&n);
  if(n>0){
    plus(n);
  }else if(n==0){
    printf("answer=%d\n",result);
  }else{
    minus(n);
  }
  return 0;
}

説明:
このプログラムではまず,入力された値nが正の数だった時と負の数だっ
た時それぞれに関しての関数を作った.もし入力された値が正の数だっ
た場合,resultという初期値が2でint型の変数に,for文を使用して
(n-1)回だけ2を掛けていく.(n-1)回掛け終わったらその結果を整数型
で出力する.nが負であったときはresultをdouble型にし,初期値を0.5
とした.また,同様の手順で(-n-1)回だけ0.5を掛けて実数型で出力し
た.メイン関数ではn,resultをint型で定義し,nの値を入力させた.さ
らにn>0の時はplusを実行,n==0の時は1を出力,n<0の時はminusを実行
するようにした.最後に0を返した.

【考察】
実行した結果としては,n=8のときに256が出力され,n=-3の時には
0.125000が出力された.n=0の時は1が出力された.
今回の課題を取り組んだ時,まず型の変換により出力の際に指示通りに
できないかと考えた.しかし,型を変更する際にうまく変換ができなかっ
たため今回のように関数を正負別に作り計算する方法を考えた.
また,最初は関数の型を出力する関数に合わせて変えていたが,プログ
ラムの中でreturnをしていないため,voidでもいいのではと変更したと
ころ正常に動いた.しかし,intやdoubleにしていても正常な結果は得
られた.そのため,返す値がない場合はvoidでいいことが確認できたが,
返す値がない場合関数の型による違いが本当に何もないのかは疑問に思っ
た.

【課題の再掲(演習1c)】
1より大きい整数nを入力し,nの素因数分解を表示する.
【実施した内容】
プログラム:
#include <stdio.h>
#include <math.h>

void primeFactorization(int n){
  double m = sqrt(n);
  int i=2;
  while(i<=m){
    if(n%i==0){
      printf("%d ",i);
      n = n/i;
    }else{
      i++;
    }
  }
  if(n!=0 && n!= 1){
    printf("%d",n);
  }
}

int main(void){
  int n;
  printf("n>");scanf("%d",&n);
  primeFactorization(n);
  printf("\n");
  return 0;
}
説明:

今回のプログラムではまず,primeFactorizationという関数を作った.
そこでは入力された値nの平方根を取った値をdaouble型の変数mに代入
した.iの初期値を2にしてwhile文でiがm以下の間のみ,もしnをiで割っ
た余りが0であればiの値を出力し,nにn/iを代入し,そうでなければi
に1を足した.その後最終的にnが0または1でなければnの値も出力した.
これにより,最終的に素数が残った場合も出力することができる.1は
素数ではないため今回は出力されないようにした.メイン関数では,n
を入力させ,関数を実行,最後に改行を出力し0を返した.


【考察】

今回のプログラムを実行すると,n=1では「」(何も返さず),n=2では
「2」,n=8では「2 2 2」,n=6では「2 3」を返した.私はいつもはfor
文を使うことが多いが,今回はwhileを使用した.それは入力された値
がいくつ同じ素数を持つかわからなかったため,条件によりiに1を足せ
るwhile文の方が便利だと思ったからである.また,pushなどを使い配
列に追加していく方法も考えたが,今回はその値を使用するわけではな
く出力のみなので,printfでの出力の方が楽だと考えた.

【課題の再掲(演習2a-1)】
正方形を4×4に並べた絵を作成する.
【実施した内容】
プログラム:
// 02-a1.c
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("2a1.ps", 480, 480);
  int i,j;
  for(i = 0; i <= 3; ++i){
    for(j = 0; j<= 3; ++j){
      eps_num(i*0.1);eps_cmd("setgray");
      eps_drawrect(32+i*112,32+j*112,80,80);
    }
  }
  eps_close();
  return 0;
}

//以下eps.cのファイル
// eps.c --- eps library implementation
#include <stdio.h>
#include "eps.h"
static FILE *fd = NULL;
static int fontid = 0;
void eps_open(char *fname, int w, int h){
  fd = fopen(fname, "wb");
  fprintf(fd, "%%!PS-Adobe-2.0\n%%%%BoundingBox: 0 0 %d %d\n", w, h);
}
void eps_close(void) {
  fprintf(fd, "showpage\n"); fclose(fd); fd = NULL;
}
void eps_cmd(char *cmd) {
  fprintf(fd, "%s\n", cmd);
}
void eps_num(double val) {
  fprintf(fd, "%.2f ", val);
}
void eps_drawline(double x0, double y0, double x1, double y1) {
  fprintf(fd, "newpath %.2f %.2f moveto %.2f %.2f lineto stroke\n",x0, y0, x1, y1);
}
static void rect(double x, double y, double w, double h) {
  fprintf(fd, "newpath %.2f %.2f moveto %.2f 0 rlineto\n", x, y, w);
fprintf(fd, " 0 %.2f rlineto %.2f 0 rlineto closepath\n", h, -w, -h);
}
void eps_drawrect(double x, double y, double w, double h) {
  rect(x, y, w, h); fprintf(fd, "stroke\n");
}
void eps_fillrect(double x, double y, double w, double h) {
  rect(x, y, w, h); fprintf(fd, "fill\n");
}
static void circle(double x, double y, double r) {
  fprintf(fd, "%.2f %.2f %.2f 0 360 arc closepath\n", x, y, r);
}
void eps_drawcircle(double x, double y, double r) {
  circle(x, y, r); fprintf(fd, "stroke\n");
}
void eps_fillcircle(double x, double y, double r) {
  circle(x, y, r); fprintf(fd, "fill\n");
}
int eps_newfont(char *font, double size) {
  fprintf(fd, "/%s findfont %.2f scalefont /font%d exch def\n",font, size, ++fontid);
  return fontid;
}
void eps_puts(int id, double x, double y, char *s) {
  fprintf(fd, "font%d setfont %.2f %.2f moveto (%s) show\n",id, x, y, s);
}

説明:

(eps.cの方に関しては教科書のサンプルコードのままなので省略します)
メイン関数ではまず,2a1.psというファイルを開き480×480の大きさを
用意する.ここでint型のi,jを定義する.まず,for文で0〜3までiを1
ずつ足していき,その中で同様にjを0〜3まで1ずつ足していく.その中
で色をi*0.1にしてsetgrayで色を付ける.次にx座標が32+i*112,y座標
が32+j*112,大きさ80の正方形を描く.これにより計16の正方形が一定
間隔をあけて描かれる.for文が終わったらeps_closeを実行し,0を返
す.

【考察】

今回の課題ではepsライブラリに触れた.読んだだけではどこの場面で
numを使用し,どこの場面でそのまま計算式を中に書けばいいのかよく
わかっていなかったが,実際に手を動かすことで理解できた.また,基
礎プロでお絵かきをしたときに比べ,rectなどが用意されているため簡
単に絵が描けると感じた.
今回は絵が指定されていたため全体のサイズから計算した値を使用して
正方形を描いた.しかし高さ,横幅,空き幅などの変数を作った方が,
値の変更が容易であると考える.また,見返した時にも何の数字なのか
がはっきりとしてミスが防げると考えた.

【アンケート】
Q1.プログラムを作るという課題はどれくらい大変でしたか?
今回は比較的復習のような内容だったため,楽しんで課題に取り組めた.
Q2.epsライブラリを使ってみてどのように感じましたか.
ライブラリを使用することで直感的に描きやすくなったと感じた.
Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ.
お絵かきの課題が楽しかったです.

y1910679 1b:○ Mon May 18 22:44:21 2020


提出日時;5/18 22:43
ペアの学籍番号;個人作業
1つ目の課題
〜演習1.c〜
*課題の再掲
  整数n (n> 1)を入力し、nの素因数分解を表示する。たとえば60を
入力 したら「2235」を出力する(出力の順番や形式は任意)。

*プログラミングのソース
#include <stdio.h>
int factoring(int n){ 
  int i;
  printf("%d =",n);
  for (i=2; i<=n ; i++)
  {
   while(n % i == 0)
   {
    printf("%d *",i);
    n /= i;
    }
   }
   printf("\n");
   return 0;
}
int main(void){
  int n;
  printf("n>");scanf("%d/n",&n); 
  factoring(n);
}
*丁寧な説明
ある整数nの因数分解するプログラミングをfacrtoringとした。「素因
数分解ができる=ある数で割れる」ということなので2〜nまでの数を
割れるかfor文で実行した。この時ある数nが素数mで2回以上割れる可
能性がある。よって、while文を用いてこの可能性を消した。ある整数
で割ることができた場合、printfで出力させた。mainの部分では整数n
の入力、出力を行った。

*考察
[課題をやってみてわかったこと]
今回のプログラミングでは素数や整数nを扱うときは、scanfの変換指定
子をdにしなければプログラミングがうまく作動しなかった。つまり整
数型を入力した場合、出力も整数型にしなければならない。

[分析]
for、whileを用いて第三者からみてわかりやすいようなプログラミング
のコードを描くことができたと思った。mainの部分と計算部分をしっか
り分けることができた。しかし、素因数を表示させるprintfの部分も
mainで書くことができたらとても見やすいと思った。

[疑問点]
1、どういった場合に変数指定子を区別すればよいのかいまだによくわ
かっていない。また、scanf、printfでの区別ができなかった。

2つ目の課題
〜演習2、a(1)
*課題の再掲
a. 次の図のような絵を作成する。色や線幅は自由にしてよい
*プログラミングのソース
#include <stdio.h>
#include "eps.h"

int main(void) {
//サイズを決める
  eps_open("squre.ps", 480, 480);      
//線を引く
  int n = 0;
  for( int i = 1; i <= 4; ++i)
  {
    for( int j = 1; j <= 4; ++j)
    { 
      while(n <= 1)
      {
       eps_drawline(10+40*n+i*90, 10+j*90 ,10+40*n+i*90 , 50+j*90);
       eps_drawline(10+i*90, 10+40*n+j*90 ,50+i*90 , 10+40*n+j*90);
       n += 1;
       } 
       n = 0;
     }
   }              
  return 0;
}
*丁寧な説明
eps_openで出力するファイル名、絵のサイズを決めた。正方形を線を使っ
て書いた。まず線を書くために、darawlineをつかった。次に、for文を
つかって均等に線がずれていくようにした。whileでは2種類の縦の線
(右辺と左辺)を表現した。

*考察
[分かったこと]
whileをfor文の中で使用するときに、毎回n=0にしなければループがで
きないことが分かった。また、for文の中でintなどの変数を定義できる
ことを初めて知った。

[分析]
今回、正方形を線で描こうと思ったが、drawlineの中身の式がとても複
雑になってしまったので、わかりにくい。//をつかってどこの部分がど
の作業になっているのかわかりやすい。なので、次回からも継続して続
けるとよいと思った。

[疑問点]
この問題では特になかった。

y1910681 1b:○ Fri May 15 16:10:48 2020



一つ目の課題の再掲
入力された整数の因数を表示する

ソース(演習1c)

--c1.c--

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
bool prime(int a){
    int i = 0;
    for (i = 2; i < a; i++){
        if(a % i == 0){ return false;}
    }
    return true;
}


int main(){
    int a,b;
    printf("a <\t");
    scanf("%d", &a);
    if(a < 1){ return 0;}
    if(a == 1){ printf("1\n"); return 0;}
    for (b = 2; b <= a; b++){
        while(prime(b) && a % b == 0){
            a /= b;
            printf("%d ", b);
        }
    }
    printf("\n");
    return 0;
}

説明
primeは2から(引数-1)までの数で引数を剰余演算をし0になったらfalse
をならなかったらtrueを返す関数.
main関数内ではifで以下の計算を行えない1以下の入力された整数を処
理する方法を書いた.以下の計算だがbを1ずつ上げていきaとbの剰余演
算で0かつbが素数の時にaをbで割る.この時bを出力していく.

考察
おそらくだが配列を使えばメモリの圧迫量は増えるが時間的には短くなると思われる

2つめの課題の再掲
正方形を4×4で規則正しく空白空けながら並べられたような絵を出力する

--eps.c--

#include <stdio.h>
#include "eps.h"
static FILE *fd = NULL;

void eps_open(char *fname, int w, int h){
fd = fopen(fname, "wb");
fprintf(fd, "%%!PS-Adobe-2.0\n%%%%BoundingBox: 0 0 %d %d\n", w, h);
}
void eps_close(void) {
fprintf(fd, "showpage\n"); fclose(fd); fd = NULL;
}
void eps_cmd(char *cmd) {
fprintf(fd, "%s\n", cmd);
}

static void rect(double x, double y, double w, double h) {
fprintf(fd, "newpath %.2f %.2f moveto %.2f 0 rlineto\n", x, y, w);
fprintf(fd, " 0 %.2f rlineto %.2f 0 rlineto closepath\n", h, -w);
}
void eps_drawrect(double x, double y, double w, double h) {
rect(x, y, w, h); fprintf(fd, "stroke\n");
}

--eps.h--

void eps_open(char *fname, int w, int h);
void eps_close(void);
void eps_cmd(char *cmd);
void eps_drawrect(double x, double y, double w, double h);


--a2.c--

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "eps.h"

int main(void) {
      eps_open("out.ps", 480, 480);
      eps_cmd("240 240 translate");
      int a[4] = {-130, -60, 10, 80};
      int i,j;
      for (i = 0; i < 5; i++){
	for (j = 0; j < 5; j++){
	  eps_drawrect(a[i], a[j], 50, 50);
	}
      }
      eps_close();
      return 0;
}


説明
--eps.c--
eps_openはfopenでFILE型ポインタfdに大きさ480×480のfnameをのアドレスを代入しfprintfでfdのファイルに文字列を出力
eps_closeはfdのファイルに文字列を出力し,fcloseで処理する
eps_cmdはfdのファイルに文字列を出力
rect関数はfdファイルにpsコマンドを出力する
eps_drawrect関数はrect関数を呼び出し出力されたpsコマンドにstrokeを追加して出力

eps.hはヘッダーファイル

--a2.c--
eps_open("out.ps", 480, 480)で出力するout.psに480×480の大きさを指定
eps_cmd("240 240 translate")で原点位置を(240,240)に変更
int a[4] = {-130, -60, 10, 80}で原点からの正方形の左下角の位置を縦と横どちらにも決定した
次のforでa×aの16個の直積を<縦, 横>の位置として指定し、その位置にspe_drawrectで幅50の正方形を描く
eps_closeでファイルを閉じる

考察
eps_drawrectで作られる正方形が始点が角なので回転させる時にやりや
すそうだなと思った.単に偶然かもしれないがこういうところまで気が
回るともっと時短ができるかもしれない.

アンケート

Q1. プログラムを作るという課題はどれくらい大変でしたか?
A1. eps.cの内容を理解することが2hかかるほど大変だった
Q2. eps ライブラリを使ってみてどのように感じましたか。
A2. postscriptについて忘れてしまっていたので助かった.
Q3. リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
A3. ミスがなくなってきてc言語に慣れてきてことを実感した 

y1910684 1b:○ Mon May 18 16:17:43 2020


レポート1B
提出日時 2020/05/18

[課題1]

<演習1c>
整数n(n > 1)を入力し、nの素因数分解を表示する。

<ソースコード>
#include <stdio.h> 
#include <stdbool.h>
void t(int b) {
    int lim = b;
    bool s = true;
    while(s){
        for(int i = 2; i <= lim; ++i) {
            if(b % i == 0){ b = b / i;printf("%d ",i);break;}
            if(i == lim){s = false;}
        }  
    }
}

int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  if(n <= 1){
      printf("2以上の整数を打ち込んでください\n");
      return 0;
  }
  t(n);
  printf("\n");
  return 0;
}

<説明>

まずmain関数でnを入力させ、もしnが1以下で合ったら"2以上の整数を
打ち込んでください"とメッセージを出し、終了させた。2以上の場合の
み関数tに移動し、t内では、for文で2からbまでの値でbを割ってみて割
り切れたらその時の割った数を次々と出力させた。その後、bにiを割っ
たものを新たにbとして、breakしbまで行ったらbool型のsをfalseにし、
whileを終了させた。whileを使うことでfor文を最初から繰り返すこと
ができるので2から因数を繰り返し調べることができる。

<実行例>
[y1910684@sol L01]$ ./a.out
n> 0
2以上の整数を打ち込んでください
[y1910684@sol L01]$ ./a.out
n> 4
2 2 
[y1910684@sol L01]$ ./a.out
n> 12
2 2 3 
[y1910684@sol L01]$ ./a.out
n> 47
47 

<考察>

実行例を見ると正しく動作してることが分かる。なお、今回は2から順
に因数を探索しているため出力される数が小さい順に並んでおり、素因
数分解に適した表示ができた。このソースコードでは一つの整数を素因
数分解したが、これを応用し、例えば素因数分解した結果を配列に入れ、
それを複数の整数で行って最大公約数や最小公倍数を求める関数ができ
るなどほかの操作に応用できると考えられる。また、今回は簡単な操作
のためすべてmain関数に入れることも可能だが、関数tとして作成する
ことでもし複数の関数を作成したときにわかりやすく整理できるのでこ
れから複雑なソースコードを作成するときに活用ができそうだと考えた。

[課題2]

<演習2a>
スライドp35のa,(1)の絵を作成する。

<ソースコード>
#include <stdio.h>
#include "eps.h"

int main(void) {
  eps_open("out.ps", 480, 480);   
  eps_cmd("240 240 translate");
 
  for(int i = 0; i < 5; ++i){
  eps_drawrect(8,8,100,100);
  eps_drawrect(8,124,100,100);
  eps_drawrect(124,8,100,100);
  eps_drawrect(124,124,100,100);
  eps_cmd("90 rotate");
  }
  
  eps_close();                          
  return 0;
}

<説明>

絵を見ると、左右上下対称となってるため中心を原点としたとき、右上
部分(座標だと第一象限)のみを作成し、それを90度回転させる動作を4
回繰り返し絵を作成した。四角形はdrawrectで四つ分作成した。なお正
方形の一辺の長さは100とした。

<実行例>
省略

<考察>

実際に作成したout.psを見るときれいに正方形が16個並んだ図形が表せ
た。今回は回転を使ったが、それ以外にも繰り返しを使用してx座標y座
標を変えながら図形を描くやり方も考えられる。今回は色をつけなかっ
たがもし色を導入したときに回転のほうであると、四角形の辺が回転し
たときにどの向きにあるかが少しわかりにくいので得策とは言い切れな
い。その時はfor文を使うのがやりやすいと考えた。

<アンケート>
Q1.プログラムを作るという課題はどれくらい大変でしたか?
一年の時にプログラムを作成する機会が多かったのでだんだん慣れてい
きそこまで大変ではなくなった。
Q2.epsライブラリを使ってみてどのように感じましたか。
絵を描くというより図形を組み合わせるパズルみたいで面白かった。
Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
演習2aをやっている際にrotateをsotateとあった誤植を気づくことがで
きてよかった。最初sotateにしてエラーがでたのでエラー対処の練習が
できたと思う…

y1910685 1b:○ Mon May 18 19:14:48 2020


学籍番号:1910685
ペア:個人作業
日付:5/18

[実行した課題]
演習1.c(与えられたnを素因数分解する)
演習2.b(好きな多項式のグラフを描画する)

[プログラムのソース (1.c)]

#include <stdio.h>
#include <stdbool.h>
#include <math.h>

#define MAX 100000

int list[MAX];

//これまでの素数を使って素数か否かの判定を行う
bool check(int m,int s){
  int m2 = (int)sqrt(m);
  for(int k = 1; k < s;k++){
    if(m2 < list[k])break;
    if(!(m % list[k])){
      return false;
    }
  }
  return true;
}

//与えられたn以下の素数を出力する
int prime(int n){
  //sは今何番目の探索をしているかを示す
  int s = 2;
  list[0] = 2;
  list[1] = 3;

  //nが素数か否かの判定数
  for(int k = 5; k <= n;k += 2){
    if(check(k,s)){
      list[s] = k;
      s++;
      if(s >= MAX){
        printf("許容数を超えました\nここまでの結果を表示します\n");
        break;
      }
    }
  }

  return s;
}

int main(void){
  int input = 0;
  printf("素因数分解を行う数を入力して下さい: ");
  scanf("%d",&input);
  printf("***result***\n");
  for(int n = 0;n<prime(input);n++){
    while(input % list[n] == 0){
      printf("%d ",list[n]);
      input = input/list[n];
    }
  }
  printf("\n");
  return 0;
}

[実行例 (1.c)]
素因数分解を行う数を入力して下さい: 1234
***result***
2 617

素因数分解を行う数を入力して下さい: 12345
***result***
3 5 823

[ソースの説明 (1.c)]
まず、prime関数のために、これまでに求めた素数を格納するlist配列
を用いて与えられた数が素数であるかを判別するcheck関数を作成した。
そして、check関数を用いて、与えられた整数nに対して、n以下の素数
を全てlist配列に書き出し、素数の数をsに書き出す関数primeを作成し
た。そしてそれらを利用して、与えられたnをlist配列の小さい数で割
れるだけ割っていき、割り切れる度にその数を出力することで素因数分
解を行うプログラムを作成した。

[考察等 (1.c)]
課題aで行った演習1.dでは、今何番目の素数の判別をしているかを示す
整数sがグローバル変数となっていたが、今回はprime関数の中で定義す
ることで特に他の関数でアクセスしないグローバル変数を減らすことで、
混乱しにくくなった。

割れるだけ割るという部分で、while文を用いて、割れなければlist配
列の次の要素へ進むという風にすることで、if文などによる無駄な分岐
なく未定回数の試行が行えた。

[プログラムのソース (2.b)]

#include <stdio.h>
#include "eps/eps.h"

#define SIZE 300

double f(double x);

int x_limit,y_limit;

int point(double x){
  return (f(x)/y_limit)*x_limit;
}

int main(void){
  printf("xの変域と、yの値域を入力してください(整数 -n ~ nになります): ");
  scanf("%d %d",&x_limit,&y_limit);

  eps_open("out.ps", SIZE*2, SIZE*2);

  //xy軸、大まかな目盛りを書き込む
  eps_drawline(0, SIZE, SIZE*2, SIZE);
  for(int n = 0; n< (SIZE/50 - 1)*2;n++){
    eps_drawline(SIZE - 10,100 + n*50,SIZE + 10,100+n*50);
  }
  eps_drawline(SIZE, 0, SIZE, SIZE*2);
  for(int n = 0; n< (SIZE/50 - 1)*2;n++){
    eps_drawline(100 + n*50,SIZE - 10,100+n*50,SIZE + 10);
  }

  //100分割することにより曲線を折れ線で近似する
  for(int n = 0;n < 99; n++){
    eps_drawline(n*(SIZE/50),point(-x_limit + n*(x_limit/50)) +
  SIZE,(n+1)*(SIZE/50),point(-x_limit +(n+1)*(x_limit/50)) +
  SIZE );
  }
  eps_close();
  return 0;
}

double f(double x){
  return x*x*x/100;
}

[実行例 (2.b)]
xの変域と、yの値域を入力してください(整数 -n ~ nになります): 300 10000

[ソースの説明 (2.b)]
x,yの範囲を入力してもらうことで縮尺を決定し、y = f(x)のグラフを
出力するプログラムである。
f(x)の値が画面でどの程度の高さであるのかをpoint関数によって、出
力する。これを利用して、xの範囲を100分割し、その決められたx座標
のf(x)の示すy座標をpoint関数により導いて、折れ線により曲線を近似
している。

[考察等 (2.b)]
f(x)をプロトタイプ宣言しておいたことでf(x)の中身を後から変更する
ことが容易なため、sin関数や、多項式、または条件分岐をもつ関数な
どにも対応できる描画プログラムを作ることができた。これは大きなプ
ログラムを作成する際に、後から変更を加える場合の混乱や、定義の順
番によってエラーが発生するなどの問題を回避するために非常に重要で
あると感じた。分割をさらに細かくすることで、さらに曲線を滑らかに
できると予想されるが、あまり細かくしすぎると勾配がおかしくなって
しまったり、分割が等間隔でなくなる場合があった。これはピクセルに
対して多すぎたり、割り切れない数での分割を行った場合にピクセル数
は整数であるから曲線が滑らかでなくなったのであると考えられる。

[アンケート]
Q1.プログラムを作るという課題はどれくらい大変でしたか?
他人に自分のソースの説明を書くというのが非常に大変でした。

Q2. epsライブラリを使ってみてどのように感じましたか。
感覚的に使えて非常に便利でした。

Q3.リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
頑張ります。

y1910686 1b:○ Mon May 18 23:29:02 2020



[提出日] 2020/05/18
 
 
 
[演習1c] 素因数分解を行い、結果を表示する
「小さな素因数factorから順に割れるだけ割っていく」というのが本コー
ドの基本の考え方である。factorで割れた回数はそのままfactorの指数
に対応するので、素因数分解の結果に順次書き込んでいくことができる。
最後に余った「つなぎ目の文字」をコンソール制御文字によって削除し
ているのもポイントである。

#include <stdio.h>

int main()
{
    int trg, count;
    printf("prime factorization> ");
    scanf("%d", &trg);

    for (size_t factor = 2; factor <= trg; factor++)
    {
        count = 0;
        while(trg % factor == 0)
        {
            trg /= factor;
            count++;
        }
        if (count >= 2)
        {
            printf("%d^%d * ", factor, count);
        }
        else if (count == 1)
        {
            printf("%d * ", factor);
        }
        
    }
    printf("\033[3D\n");
}

[考察]

アルゴリズム的な工夫を何もしていないにも関わらず、10桁の数字でも
(体感上)高速に素因数分解することができてしまったことは興味深い。
パフォーマンスがそこまで重視されない局面においては、効率の良いア
ルゴリズムが思いつかずにうなっているよりはとにかく力技で実装し、
一刻も早く「とりあえず動く」プログラムを作成することも肝要である。


[友人によるレビュー]
ちゃんと最後にDeleteされてて見やすかったです.
なんでカーソル移動&改行で消える仕様は知りませんでした.



[演習2d]epsライブラリを用いた面白い描画
フラクタル図形に興味があったので、今回はコッホ雪片を描画するプログラムの作成に挑戦した。
コッホ雪片はコッホ曲線を3つ正三角形上に配置したものである。
コッホ曲線は、単純な手続きを再帰的に繰り返すことで描くことができる。
1. 2点の組(A, B)を決め、その間を1:2, 2:1に内分する点をP, Qする。
2. PQを1辺とする正三角形の頂点のうち、P, Qと異なるものをRとする。
3. (A, P), (P, R), (R, Q), (Q, B)を新たな組として手順1, 2を行う。
本コードではあらかじめP, Q, Rの点の集まりを定義しておき、それらを適切な位置へ平行・回転移動させることにより
手順1, 2を実装している。手順3は、再帰関数の引数に点Aの位置とABの傾き角の情報を持たせることで実装している。

#include <stdio.h>
#include <math.h>
#include "eps.h"
#include "vtx.h"
#define PI_THIRD 1.04719755119

//vtx.h 行列計算を用いた頂点座標の1次変換を行うライブラリ
typedef struct s_vtx
{
    double x, y;
} vtx;

extern int vtxcnt;
extern vtx vertices[];

vtx mrxtovtx(double m[2], vtx *v);
vtx vtxtomrx(vtx *v, double m[2]);
vtx transform(vtx *v, double x, double y, double scale, double angle);

void addvtx(vtx v);
void affine2d(double x, double y, double scale, double theta, double *res);
void arrcpy(double *d, double *s, int n);

//vtx.c vtx.hの実装
#include <stdio.h>
#include <math.h>
#include "vtx.h"
#define VERTICES_SIZE 65536

int vtxcnt = 0;
vtx vertices[VERTICES_SIZE];

vtx mrxtovtx(double m[2], vtx* v)
{
    v->x = m[0], v->y = m[1];
}

vtx vtxtomrx(vtx* v, double m[2])
{
    m[0] = v->x, m[1] = v->y, m[2] = 1;
}

void addvtx(vtx v)
{
    if (vtxcnt == VERTICES_SIZE)
        return;
    vertices[vtxcnt++] = v;
}

void affine2d(double x, double y, double scale, double theta, double *res)
{
    double costh = cos(theta) * scale, sinth = sin(theta) * scale;
    res[0] = costh,  res[1] = sinth, res[2] = 0;
    res[3] = -sinth, res[4] = costh, res[5] = 0;
    res[6] = x, res[7] = y, res[8] = scale;
}

void arrcpy(double *d, double *s, int n)
{
    for (size_t i = 0; i < n; i++)
    {
        d[i] = s[i];
    }
}

vtx transform(vtx *v, double x, double y, double scale, double angle)
{
    double m_vtx[3];
    vtxtomrx(v, m_vtx);
    double m_aff[9];
    affine2d(x, y, scale, angle, m_aff);
    double res[3];
    vtx ret;

    for (size_t j = 0; j < 2; j++)
    {
        double sum = 0;
        for (size_t k = 0; k < 3; k++)
        {
            sum += m_vtx[k] * m_aff[3 * k + j];
        }
        res[j] = sum;
    }
    mrxtovtx(res, &ret);
    return ret;
}

//kochcurve.c コッホ雪片の描画を行うメイン部分
vtx elem = {1, 0};
vtx koch_unit[5] = {
    {0, 0},
    {1, 0},
    {1.5, 0.86602540},
    {2, 0},
    {3, 0}
};
double kochsnowflake[3][3] = {
    {180, 100, PI_THIRD * 2},
    {30, 360, PI_THIRD * 0},
    {330, 360, PI_THIRD * -2},
};

void make_kochcurve(double x, double y, double scale, double angle, int depth)
{
    vtx res[5];
    for (size_t i = 0; i < 5; i++)
    {
        res[i] = transform(&koch_unit[i], x, y, scale, angle);
    }
    if (depth > 1)
    {
        make_kochcurve(res[0].x, res[0].y, scale / 3, angle, depth - 1);
        make_kochcurve(res[1].x, res[1].y, scale / 3, angle + PI_THIRD, depth - 1);
        make_kochcurve(res[2].x, res[2].y, scale / 3, angle - PI_THIRD, depth - 1);
        make_kochcurve(res[3].x, res[3].y, scale / 3, angle, depth - 1);
    }
    else
    {
        for (size_t i = 0; i < 5; i++)
        {
            addvtx(res[i]);
        }
    }
    
}

void kochcurve(double scale, int depth)
{
    make_kochcurve(0, 0, scale, 0, depth);
}

int main()
{
    eps_open("koch.ps", 400, 300);
    kochcurve(100, 5);
    
    for (size_t vtxptr = 0; vtxptr < vtxcnt; vtxptr += 5)
    {
        for (size_t line_id = 0; line_id < 3; line_id++)
        {
            vtx res = transform(
                &vertices[vtxptr], 
                kochsnowflake[line_id][0], 
                kochsnowflake[line_id][1], 
                1, 
                kochsnowflake[line_id][2]
            );
            eps_cmd("newpath");
            eps_num(res.x);
            eps_num(res.y);
            eps_cmd("moveto");
            for (size_t i = 1; i < 5; i++)
            {
                vtx res = transform(
                    &vertices[vtxptr + i], 
                    kochsnowflake[line_id][0], 
                    kochsnowflake[line_id][1], 
                    1, 
                    kochsnowflake[line_id][2]
                );
                eps_num(res.x);
                eps_num(res.y);
                eps_cmd("lineto");
            }
            eps_cmd("stroke");
        }
        
    }
    eps_close();
}

[考察]
再帰の深さを色々変えて図形のようすを観察してみた。深さ7だと細か
すぎて、それ以上値を大きくしても見た目に大きな変化はなかったうえ
に、画像が生成されるまでにかなり待たされるので時間の無駄であった。
実行時間と見た目の細かさのバランスがちょうどよかったのは深さ5の
ときであった。

[友人によるレビュー]
make_kochcurveの内容を読めば全体像をつかめる構造で,親子関係を上
手に使って情報隠蔽できていると思います.他の場所のコードは読んで
いませんがこの部分と実行結果でプログラムの概要が把握できました.
命名も含めてわかり易かったです.

[アンケート]
Q1. コアダンプの対処法がわからず、苦戦した。
Q2. 自分で新しい機能を追加したくなった。
Q3. 友人がC++を学びたいと言っていた。私も同感である。

y1910688 1b:○ Fri May 15 14:46:04 2020


プログラミング通論(久野先生)
第一回B課題

個人作業
提出日時 2020/5/15

一つ目の課題 演習1c

・作成したプログラム
#include <stdio.h>

int prime_factorization(int n) {
  int i,flag=0;
  for(i=2;i<(n-2)/2;i++){
    while(flag==0){
      if(n%i==0){
        printf("%d ",i);
        n/=i;
      }else{
	flag++;
      }
    }
    flag=0;
  }
  if(n!=1){
    printf("%d",n);
  }
}

int main(void) {
  int n;
  printf("n> "); scanf("%d", &n);
  prime_factorization(n);
  return 0;
}

・実行例
[y1910688@sol ~/pp20]$ ./a.out
n> 120
2 2 2 3 5
[y1910688@sol ~/pp20]$ ./a.out
n> 8
2 2 2 

・説明
nの素因数分解を表示するプログラム。
関数prime_factorizationの中では、2から(n-2)/2までの数を
nで割り切れるかどうか総当たり的に調べている。
割り切れた場合のみprintfで表示するようにした。
同じ素因数が2個以上出てくる場合に対応するため(例n=60で2,2,3,5)、
flagという変数をiの値で割り切れなくなった時を感知するフラグとして、
while文をfor文の中で回した。

・考察
今回のプログラムでは1の素因数分解は1という定義だとすると、
n=1の時に何も表示されないのが問題点の一つである。
flagを使って処理するのが個人的には好きなので使っているが、使わない書き方も考えたい。
for文の中にwhile文が入るのはあまり見たことない形なので、もう少し
うまく書けそうと感じた。

二つ目の課題 演習1e

・作成したプログラム
#include <stdio.h>

int fibonacci(int n){
  int i;
  int fib[n+2];
  fib[0]=1;fib[1]=1;
  for(i=0;i<n;i++){
    printf("%d ",fib[i]);
    if(i!=0){
      fib[i+1]=fib[i-1]+fib[i];
    }
  }
}

int main(){
  int n;
  printf("n> ",n); scanf("%d",&n);
  fibonacci(n);
  return 0;
}
  
・実行例
[y1910688@sol ~/pp20]$ ./a.out
n> 10
1 1 2 3 5 8 13 21 34 55 

・説明
任意の数列について第n項まで順に表示するプログラム。
今回はフィボナッチ数列を表示するようにした。
関数fibonacciの中でn項分の配列fibを作りフィボナッチ数列の各項をfor文内のif文で
一つ前の数とその時のiの和を次の項を生成している。
初項と第2項は1と決まっているのでこのようなif文の処理を用いた。

・考察
今回は数列fibの要素数をn+2と設定したのでfor文の中に条件分岐を無理やりつけたので、
もう少しスマートに書けるのではと感じた。
フィボナッチ数列は再帰を用いて表現することもできるので、そちらで
もプログラムを書いてみたい。

・アンケート
Q1のA 
今回作成したプログラムは今までの情報系授業の延長線上なのでそこま
で大変には感じなかった。

Q2のA
単純な図形の組み合わせや関数によって様々な絵やグラフを描くことが
できる点で有用と感じる。

Q3のA
C言語の復習ができて良かった。

y1910692 1b:○ Tue May 12 22:03:27 2020


#1B課題レポート
学籍番号:1910692
ペアの学籍番号:個人作業
提出日時:5/12 10時ごろ

[課題の再掲①]
演習1-e:整数nを入力し、何らかの数列(選択は任意)の最初のn項を出力する。

[実際に書いたコードとその説明①]
下記は今回作成した演習1-eのコードである。

#include<stdio.h>
#include<math.h>
int main(){
    int n,i;
    printf("n>");scanf("%d",&n);
    for(i=1;i<=n;++i){
        printf("%d ",(4-(int)(pow(3,i))));

    }
    return 0;
}

上記のコードは数列3-4^nの数列を初項から第n項まで表示させるプログ
ラムである。指数を用いる数列なのでpow関数を用いるためにmath.hを
inclueした。nに値を入れて計算すると数列の結果は、出るのでiの値だ
け変化させて同じ動作を繰り返すようにfor文で数列の計算と表示を同
時に行うことによって数列4-3^nの初項から第n項までを表示させること
ができる。
 
[考察①]
他の数列を扱うにしろ、計算式の部分を変えるだけで、このやり方がで
きるといのがわかる。今回は、nに代入するだけで数列が求まるが、3
項間漸化式のような数列だと代入するだけとはいかず、計算が終わった
後計算結果で出た値を使った変数に代入するという方法を取る必要性が
ある。数列を扱うには色々なアプローチ方を把握している必要があるな
と思った。

[課題の再掲②]
演習2-d: その他epsライブラリを利用して面白い描画をおこなう。

[実際に書いたコードとその説明②]
下記は今回作成した演習2-dのコードである。

#include<stdio.h>
#include<math.h>
#include"eps.h"
#define PI 3.14159265
void sinstar(double x,double y){
  int i=0;
  for(i=0;i<=20;++i){
    eps_cmd("255 255 0 setrgbcolor");
    eps_fillrect(x+50*i,y+150*sin(i*PI/3.2),7,7);
  }
}


void ex_hanabi(double x,double y){
    int i;
    for(i=100;i<y;i+=200){
      eps_fillrect(x,i,4,4);  //i=700
    }
        int j;
    for(j=0;j<=7;++j){
        eps_drawline(x-175*cos(PI*j/8),i-175*sin(PI*j/8),x+175*cos(PI*j/8),i+175*sin(PI*j/8));
    }
}


int main(){
  eps_open("hanabi.ps",1000,1000);
  eps_cmd("0 0 0 setrgbcolor");
  eps_fillrect(0,0,1500,1500);
  eps_cmd("255 255 0 setrgbcolor");
  sinstar(55,800);
  sinstar(55,900);
  sinstar(55,600);
  sinstar(55,400);
  sinstar(55,200);
  eps_cmd("255 0 0 setrgbcolor");
  ex_hanabi(150,600);
  eps_cmd("0 0 255 setrgbcolor");
  ex_hanabi(700,500);
  eps_cmd("255 0 255  setrgbcolor");
  ex_hanabi(400,300);
  eps_cmd("0 255 255  setrgbcolor");
  ex_hanabi(850,700);
  return 0;
}


今回epsライブラリで描いた絵は「星空に浮かぶ花火」である。星空と
いうとまずは背景を黒く塗る必要があるので、eps_openで画面を生成し
た後、縦の長さと横の長さを余分にとった黒の長方形で画面を塗りつぶ
した(eps_cmd("R G B setrgbcolor")とeps_fillrectを使用)。星は、
黄色い小さな四角で表すことにした。しかし、配置する数が多くて一つ
一つ関数を使って描くとコードの長さがとても長くなってしまい、手間
もかかるのでsinstarという関数を作成した。eps_fillrectを何個も配
置するためにfor文を使用し、また配置を疎らにするために、y座標を三
角関数を使って表した。このsinstarを適度に使うことで、画面上に星
をたくさん作ることができる。次に花火を作ることにした。花火を撃っ
た跡を四角で表して、花火の花びらを演習2-aの4つ目のような図形で表
すことにした。これも花火を4つぐらい作ろうとしたので、コードの長
さと手間を減らすためにex_hanabiという関数を作成した。引数として
渡したx座標、y座標からスタートさせ、y座標に値を加えつつ、小さな
四角を表示させることで跡を描いた。また、花火の花びらは、形が円状
なので中心を関数内のxとiで定めた。円のx座標、y座標が半径rと位
相θを使って、x=rcosθ、y=rsinθで表せることを用いて円の端から端
へ直線をまばらに引くようにfor文内にeps_drawlineで、花びらを描い
ていった。できた関数ex_hanabiとeps_cmd("R G B  setrgbcolor")で色
が事なる花火を4つほど作ることで絵が完成した。

[考察②]
epsライブラリを使うにあたって、postscript言語の知識も必要となっ
てくる。今回は例題に乗っていたpostscript言語を理解して絵を作成し
たが、他のpostscript言語を取り入れればもっと楽に、あるいは、もっ
と質の良い絵が生成できるのではと思った。また、コードを書いてて気
づいたのは、関数を使う大切さである。何度も作業を繰りかえす際には、
とても重要な役割を果たすというのが星を描いているときにとても実感
できた。また、星の配置ををまばらにするために三角関数を用いたり、
円の座標の媒介変数表示を用いたりと数学の知識を用いることで表現で
きるものがたくさんあるのだなと思った(花火の花びらが開き終わった
後の描写を2次関数のグラフを表示できるのでは?と思った。)。

そういう面において今回のこの課題は面白味もあったし、学んだことも多かった。

[アンケート]
Q1プログラムを作るという課題はどれくらい大変でしたか?
今回に至っては、それほど苦ではなかった。
Q2 eps ライブラリを使ってみてどのように感じましたか。
postscript言語を学べばもっと面白味の増える機能だなと感じた。
Q3  リフレクション (今回の課題で分かったこと)・感想・要望をどうぞ。
eps昨日のような遊びのような感覚でできる授業内容が増えると面白味
も増えて授業への意欲も上がりそうと思った。

y1910693 1b:○ Mon May 18 16:49:56 2020


レポート課題1b


[プログラム1ーb]
#include <stdio.h>

int main(void){
  int n,i,x;
  double y;
  x=1;
  y=1;
  printf("n=");scanf("%d",&n);
  if(n>0){
    for(i=1;i<=n;i++){
    x=2*x;
    printf("%d\n",x);
  }
  return 0;
  }
  else if(n<0){
  for(i=-1;i>=n;i--){
    y=y/2;
    printf("%f\n",y);
  }
  return 0;
  }
  else{printf("1\n");
  }
}

[1ーbについて]

整数nを入力すると、n > 0 のときは 2^n が整数で出力され、
n < 0 のときは実数で出力され、n = 0 のときは1が出力される。
nが正のときと負のときで一度にfor文でまとめて出力しようとすると
エラーがでるので一個ずつ分けて出力した。
while文を用いても同様の内容を書くことが出来る。

[プログラム1-d]

#include <stdio.h>
#include <stdbool.h>

int main(){
  
  int i,j;
  int flag;
  int n;

  printf("n>"); scanf("%d",&n);
  
  for(i=2;i<n;++i){
    flag = 0;
    for(j=2;j<i;++j){
      if(i%j==0){
	flag = 1;
	break;
      }
    }
    if(flag==0)
      printf("%d\n",i);
  }
  return 0;
}

[1-dについて]
素数以外の値が入力されたときはflag1になり外れるが、
それ以外のときは、flag0のままのこり、もし、flagが0ならば
最後のif文によって出力される。
flagを用いることで効率的になると考えられる。
効率という雨天で考えれば√を用いたほうがループの長さが短くなり、
効率的になるだろうと考えられる。

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
5けた×5けたの掛け算を暗算でやるくらい大変でした。
Q2. epsライブラリを使ってみてどのように感じましたか。
ヘッダーファイルに書いてある内容が理解できないところがあったので
コメントアウトしてほしいと感じました。
図形を描こうと思ったときに必要なものがヘッダーファイルに
入っていたのでとても楽だった。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
次第にやることが増え難易度が上がると思うが何とか頑張っていきたい

y1910695 1b:○ Mon May 18 02:52:17 2020


個人作業
提出日時:

一つ目
演習1-c・素因数分解
[コード]
#include<stdio.h>

int main(void)
{
    int n;
    printf("n> ");
    scanf("%d",&n);

    int a = 2;
    int x = n;
    while(a <= x)
    {
        if(x%a == 0)
        {
            printf("%d ",a);
            x = x/a;
        }else{
            a += 1;
        }
    }
    printf("\n");
    return 0;
}

[説明]
与えられた値をxに入れ、2から順番に割った、割り切れた場合はxに商
を入れ、除数を出力した。

[考察]
今回は2から順番に1ずつ足した値で割っていったが、素数以外で割る必要はないと考えた。
そのため、2以降の偶数を省くために、最初に2で割った後に3から順番
に2ずつ足した値で割っていった方がより良いものになると考えた。

二つ目
演習1-d・n以下の素数を出力
[コード]
#include<stdbool.h>

bool isprime(int n)
{
    int lim = (int)sqrt(n);
    for(int i = 2; i <= lim; i++)
    {
        if(n % i == 0)
        {
            return false;
        }
    }
    return true;
}

int main(void)
{
    int n;
    printf("n> ");
    scanf("%d",&n);

    if(n >= 2)
    {
        printf("2 ");
    }

    for(int i = 3; i <= n; i += 2)
    {
        if(isprime(i))
        {
            printf("%d ",i);
        }
    }
    printf("\n");
    return 0;
}

[説明]
isprimeは与えられた値が素数かどうかを判定し、trueかfalseで返す関
数で、テキストを参考にした。main関数では、最初に与えられた値が2
以上だった場合は2を含むため、2を出力した。その後、3から順番に2ず
つ足していき、その値が素数かどうか判定した。

[考察]
この課題をやる前まで、キャスト演算子についてあまり理解ができてい
なかったが、この課題でキャスト演算子が出てきたため、調べて理解を
深めることにした。double型からint型にキャストすると少数部分は四
捨五入ではなく切り捨てられることがわかった。

bool型を用いて素数かどうかの判定を行うことによって、ifが使いやす
くなっていると感じた。この課題のように判定する場合はbool型を使え
ばやりやすいと思った。

[アンケート]
Q1.プログラムを作ることに少しずつ慣れてきているため、今回の課題
は比較的難しくはなかったように感じた。
Q2.様々な図形や色を扱うことができるため、色々と工夫できると感じた。
Q3.久しぶりにプログラムを作成したが、しっかりとできたためよかっ
た。

y1910697 1b:○ Sun May 17 16:47:10 2020


学籍番号 1910697
個人作業
5月17日 提出

演習2a 図形の作成
コード
#include <stdio.h>
#include "eps.h"

int main (void){
eps_open("2a1.ps",360,360);
int i,j;
for (i=0;i<4;i++){
	for(j=0;j<4;j++){
		eps_num(0.1);eps_num(i*0.1);eps_num(j*0.1);eps_cmd("sethsbcolor");
		eps_cmd("2 setlinewidth");
		eps_drawrect(i*95,j*95,75,75);
	}
}
eps_close();

eps_open("2a2.ps",360,360);
for (i=0;i<5;i++){
	for(j=0;j<5;j++){
		eps_num(0.1);eps_num(0.5);eps_num(0.5);eps_cmd("sethsbcolor");
		if((i+j)%2==0){
			eps_fillrect(i*72,j*72,72,72);
		}				
	}
}
eps_close();

eps_open("2a3.ps",360,360);
for (i=0;i<5;i++){
	for(j=0;j<5;j++){
		eps_num(0.1);eps_num(0.5);eps_num(0.5);eps_cmd("sethsbcolor");
		if(i==0||i==4){
			eps_drawcircle(38+68*i,38+68*j,34);
		}else if(j==0||j==4){
			eps_drawcircle(38+68*i,38+68*j,34);		
		}				
	}
}
eps_close();


eps_open("2a4.ps",360,360);
eps_cmd("180 180 translate");
eps_cmd("2 setlinewidth");
for (i=0;i<4;i++){
	eps_drawline(-150, 0, 150, 0);  
  	eps_drawline(0, 150, 0, -150); 
	eps_cmd("22.5 rotate"); 
}
eps_close();

return 0;
}
説明
4枚の画像を作成します。2a1.psが(1),2a2.psが(2),2a3.psが
(3),2a4.psが(4)に相当します。きれいにつくるために画像サイズより
すこし小さく図形をつくっています。いずれもfor文による繰り返しを
利用して同じ図形を並べています。(2)は全体を5かける5の格子状に考
えて0~4の番号をふってその和が偶数の時だけ塗り潰すように条件文を
設定しています。(3)は繰り返しの中に条件文をいれて構造が複雑にな
らないようにしました。(4)は原点を画像の中心に移動させて回転と直
線を引くことを繰り返している。

考察
(4)でrotateの回転角に少数が使えた。値はラジアンではなかった。
math.hではsinなどで角度を用いるときはラジアンが使われているので
すこし違和感があった。きれいに回転が行われていたので少数を切り上
げ切り捨てして描写してくれる機能もついていると考えられる。

演習2b 関数のグラフ
コード
#include <stdio.h>
#include "eps.h"
#include <math.h>
int main(void){
int i;
eps_open("2b.ps" ,360,360);
eps_cmd("180 180 translate");
eps_cmd("4 setlinewidth");
eps_drawline(-160,0,160,0);
eps_drawline(0,-160,0,160);
eps_cmd("2 setlinewidth");
for(i=-160;i<=160;i++){
	eps_cmd("1.0 0 0.5 sethsbcolor");
	eps_drawline(i,160*sin(M_PI*i/160),i+1,160*sin(M_PI*(i+1)/160));
	eps_cmd("0.2 1 0.5 sethsbcolor");
	eps_drawline(i,160*cos(M_PI*i/160),i+1,160*cos(M_PI*(i+1)/160));
}
int f =eps_newfont("Times-Italic",20);
eps_puts(f,120,-60,"cosx");
eps_cmd("1.0 0 0.5 sethsbcolor");
eps_puts(f,120,160,"sinx");
eps_close();
return 0;
}
説明
画面中央に縦直線と横直線を引いて座標軸としてその交点を原点として
sinxとcosxのグラフをそれぞれ1周期分書きます。細かい直線を
eps_drawlineで引くことを繰り返してグラフを引きます。2つのグラフ
は色を変えています。グラフ中の文字sinx,cosxはeps_putsで書いてい
ます。

考察
eps_drawlineのなかの座標を表す値に直接cosやsinを使える。実数は
pixelに表すときに整数にキャストされていると考えられる。設定した
色は変更するまで同じ色が使われる。eps_drawlineはすきまがないよう
に線を引いてくれる。拡大してもsin,cosのジャギーが見えないので
postscriptでは文字の解像度は1pixelより高いように考えられる。

アンケート
Q1 プログラムをつくる課題はどのくらいたいへんでしたか?
A1 昨年度からやっていることなのであまりたいへんには感じませんでした。
Q2 epsライブラリを使ってみてどのように感じましたか?
A2 かなり直感的に使えたと思います。math.hによる三角関数などもそ
のまま使えて楽でした。
Q3 リフレクション
A3 epsライブラリを使うことはできたと思います。きれいに描写してく
れるので修正する手間がなくて良かったです。使いやすいライブラリを
つくることの大事さがわかったと思います。おもしろい描写は思いつき
ませんでした。

y1910698 1b:○ Thu May 14 02:34:51 2020


プログラミング通論レポート#1b
提出日付:2020年5月14日

[取り組んだ課題と作成したプログラム:1つ目]
課題:1b
・実際に作成したプログラム
//整数nを入力して、2^nを出力する
//n>=0の場合は整数、それ以外では実数を返す

#include <stdio.h>

int main(void)
{
    int n;
    double s=1;
    int a=1;

    printf("n:"); scanf("%d",&n);

    int m = n; //mにnの値を入れておく

    if(n>=0)
    {
      for (;n>0;n--)
       {
           a = a*2;
       }
       printf("2の%d乗は%dです。¥n",m,a);
    }

    else
    {
        for (;n<0;n++)
        {
            s = s/2;
        }
        printf("2の%d乗は%fです。¥n",m,s);
    }

    return (0);
    
}


[プログラムの説明]
整数nを入力すると、n>=0の場合は整数、そうでない場合は実数値とし
て2nを返すプログラムです。大した長さにならないプログラムだと思っ
たのですべてをmain関数で行えるようにしました。nが0以上のときとn
が0以下のときで場合分けをして計算をするようにしています。

[考察]
2の累乗を求めるだけのプログラムならばもっと短くかけたと思う。自
分の知る知識の中ではpow関数を用いたらさらに分量を減らしてかける
と思った。また、調べてみるとシフト演算子というものでもかけそうだ
なと思った。シフト演算子は今後も役に立つと思うのでうまく使えるよ
うになろうと思う。

[取り組んだ課題と作成したプログラム:2つ目]
課題:1c
・実際に作成したプログラム
//nの素因数分解を表示

#include <stdio.h>

int number (void)
{
    int n;
    printf("2以上の整数を入力してください:"); scanf("%d",&n);
    return (n);
}

void bunkai (int n)
{
    int i;
    printf("%d = ",n);
    while (n % 2 == 0) { //偶数は2から割る
        printf("2 ");
        n /= 2;
    }

    for (i=3; i<= n; i++) //2以外の素数で割る
    {
        while (n % i == 0) {
            printf("%d ",i);
            n /= i;
        }
    }
    
}

int main (void)
{
    int n;
    n = number();
    bunkai(n);
    return(0);	
}

[プログラムの説明]
整数nを入力するとそのnの素因数分解を表示するプログラムです。まず、
number関数は入力された整数nを受け取るものです。また、bunkai関数
は素因数分解を行うものです。まず、偶数の場合はその整数が偶数でな
くなるまで2で割り続けます。次に2以外の素数で割れるかを3から順に
試していきます。これらの関数を用いてmain関数では実際に整数nの素
因数分解を表示します。

[考察]
素因数分解のプログラムの作成がとても大変だった。普段自分が脳内で
処理していることをプログラムに行わせようとすることがこんなにも難
しいことなのかと思った。プログラムではこの数で割れそうだな…といっ
たような勘はないし、どこかに穴があるようなものでもいけないとなる
と、付け加える条件はこれで本当に正しいのか?とプログラムを作成し
ながら不安にもなったため、プログラミングの大変さを味わうことになっ
た。もっと短時間でプログラムの構成を考えて作成できるようになりた
いと思った。

[アンケート]
Q1. 結構大変だった。特に素因数分解のもの。
Q2.正直自分でファイルを作成したほうが早いのではないかと思ってしまった。
Q3. 文字コードをutf-8に変換してから提出するのをいつの日か忘れて
しまいそうでそれがとても怖い。

y1910699 1b:○ Sun May 17 20:45:18 2020


プログラミング通論 第一回レポート課題(b)
2020/5/17

【プログラム1(整数または小数nを入力し、2^nを出力する)】

1 #include <stdio.h>
2
3 double power(double n) {
4   double x = pow(2, n);
5   return x;
6 }
7
8 int main(void) {
9   double n;
10  printf("n> "); scanf("%lf", &n);
11  double x = power(n);
12  printf("The %g th power of 2 = %g\n", n, x);
13  return 0;
14 }

【説明】

1行目では、stdio.hをインクルードしている。
3行目では、powerという名前の関数がdouble型(小数)であり、また
引数としてdouble型のnを用いることを宣言している。
4行目では、xに2のn乗を代入している。累乗の計算にはpow関数を用いている。
5行目では、累乗の計算を終えたxを関数に返している。
10行目では、"n>"と出力させ、さらにそこに入力した数字をnとして
扱えるようにしている。
11行目では、main関数内の数字xに、power関数にnを入力した結果返
された値を代入している。
12行目は「The n th power of 2 = x」と出力している。

【実行結果】

n> 6
The 6 th power of 2 = 64

n> 2.1
The 2.1 th power of 2 = 4.28709

n> 0
The 0 th power of 2 = 1

【考察】

pow関数は標準ライブラリであるmath.hに含まれる関数であるはずだが、
stdio.hのインクルードのみで実行出来た。
プログラムを実行する際、ターミナルには

1b.c:4:11: 警告: 組み込み関数 ‘pow’ の互換性がない暗黙的な宣言です
1b.c:4:11: 備考: include ‘<math.h>’ or provide a declaration of ‘pow’
1b.c:2:1:
+#include <math.h>

と書かれていた。これは、「pow関数はこのままだと使用できない暗黙
的な宣言だから、プログラムの2行目に"include <math.h>"を追加した」
という意味だろうか。もしこれが正しければ、以降<math.h>をインクルー
ドせずとも、必要であればターミナルが勝手に追加してくれるのではな
いか、と考えた。

また、プログラムの12行目について、出力結果を、例えば「The 8th
power of…」のようにするにはどうすれば良いのだろうか、と思った。
入力した数字と"th"との間にスペースを空けなければ、%gth となって
しまい、これではフォーマット指定子として正しく無くなってしまうの
では無いか、と考えた。
そこで、試しに %gth としてプログラムを実行してみた結果、予想と異
なり、数字と"th"がくっついた状態で正常に表示された。
このことから、フォーマット指定子は前後のスペースではなく、決めら
れた文字列そのものによって認識されていることが分かった。

【プログラム2(入力した整数の素数判定、及び素因数分解を行うプログラム)】

1  #include <stdio.h>
2  #include <math.h>
3
4  int pfact(int n) {
5    int i;
6    int flag = 1;
7
8    if (n == 57) {
9        printf("57\n");
10       printf("Dr. Grothendieck says it's a prime number, so it's probably a prime number!");
11     }
12  
13   for (i=2; n>i; i++) {
14     if (n%i == 0) {
15     flag *= 0;
16     }
17   }
18  
19   for (i=2; n>=i; i++) {
20     if (n%i == 0 && n != 57) {
21       printf("%d ",i);
22       n = n/i;
23       i--;
24     }
25   }
26   
27   printf("\n");
28   
29   if (flag == 1) {
30     printf("It's a prime number! FEVER TIME, yeah!!!\n");
31   } 
32 }
33 
34 int main(void) {
35   int n;  
36   printf("n> ");
37   scanf("%d", &n);
38   printf("The prime factors are ");
39   pfact(n);
40   return 0;
41 }

【説明】

1行目では、stdio.hをインクルードしている。
2行目では、math.hをインクルードしている。
4行目では、pfactという名前の関数がint型(整数)であり、また引数
としてint型のnを用いることを宣言している。
5行目では、int型の整数iを定義している。
6行目では、int型の整数flagを定義し、さらに初期値を1としている。
8~11行目では、入力された数字が57であった場合のみの例外処理をしている。
具体的には、57は本来素数では無いが、これがグロティンダーク素数で
あることに因んで、素因数分解の結果を57と表示させ、さらに
「Dr. Grothendieck says it's a prime number, so it's probably a
prime number!」と表示させている。
13~17行目では、入力させた数字が素数であるかどうかを判定している。
for文を用いて、nを2~n-1(=i)で割っていき、もしも割り切れる計算が
存在した場合、変数flagに0をかけている。
つまり最終的には、入力された数字が素数であればflagは1のまま、そ
うでなければ0になっている。
19~25行目では、入力させた数字の素因数分解の結果を表示している。
for文を用いて、nを2~n-1(=i)で割っていき、もしも割り切れる計算が
存在した場合、その時のiの値を出力して、nにn/iの値を代入している。
また、その場合、次のnも同じiで割れる可能性があるため、iを1小さく
してからfor文のi++を行うことで、同じiの値での計算を再度行ってい
る。
27行目では、改行をしている。
29~31行目では、flagの値を参照している。
flagが0、すなわち入力された整数が素数であれば「It's a prime
number! FEVER TIME, yeah!!!」と表示させている。
34行目~40行目はmain関数である。
35行目でnがint型であることを宣言し、36行目では、"n>"と出力さ
せ、37行目ではそこに入力した数字をnとして扱えるようにしている。
38行目では「The prime factors are 」とだけ出力している。39行
目でpfact関数を実行しているので、この文字列の後に続けて素因数分
解の結果が出力される。

【実行結果】

n> 60
The prime factors are 2 2 3 5
n> 97
The prime factors are 97
It's a prime number! FEVER TIME, yeah!!!
n> 57
The prime factors are 57
Dr. Grothendieck says it's a prime number, so it's probably a prime number!

【考察】

私はこのプログラムで、本当は「The prime factors are 2,2,3,5」の
ように表示させたかった。自分なりにかなり考えたのだが、出力される
数字の直前にカンマを入れれば最初の数字の前にも入っていしまい、数
字の直後にカンマを入れれば最後の数字の後にもカンマが入ってしまう。
次に、最初に割り切れた時と2回目以降に割り切れた場合でprintfの中
身を変える事を考えた。int型の変数flag2の初期値を0にし、割り切れ
る計算を行うごとに1ずつ加え、最後にflag2=1の時とそれ以外の時で
出力を変えようかと思ったのだが、エラーを吐かれ続けてしまって、今
の自分の実力では実装することが出来なかった。おそらくprintfの直前
をif文で条件分岐させれば出来るはずなのだが、このレポート課題を提
出後も考えてみたいと思う。

【アンケート】
Q1:プログラムを作るという課題はどれくらい大変でしたか?
→今回の課題は1年次の復習であったため、そこまで大変ではありませんでした。

Q2:epsライブラリを使ってみてどのように感じましたか。
→自分の家の環境だと実行時の動作が重くなってしまい、大変でした。

Q3:リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
→次回も頑張ります。

y1910705 1b:○ Sun May 17 16:06:28 2020


1bレポート
[課題1]演習1c
~作成したプログラム~
#include <stdio.h>
int bunkai(int n){
  int i;
  while(n % 2 ==0){
    printf("2 ");
    n = n / 2;
  }
  for(i=3; i<=n; ++i){
    while(n % i ==0){
      printf("%d ", i);
      n = n / i;
    }
  }
  printf("\n");
}

int main(void){
  int n;
  printf("n>"); scanf("%d", &n);
  bunkai(n);
  return 0;
}

~実行例~
n>28
2 2 7
n>99
3 3 11

~プログラムの説明~
自然数nを素因数分解するプログラムを作成した。ループと剰余を用い
て因数を求め、nを因数で割っていくことによって重複がないようにし
た。

~考察~
最初はn以下の素数を探すのではないかと考えたが、実際にやってみる
と、ループと剰余の条件を用いて素因数分解できることがわかった。さ
らに、nを因数で割っていくことで重複しないようにするというところ
が個人的に難しかった。

[課題2]演習2a(3)
~作成したプログラム~
#include <stdio.h>
#include "eps.h"

int main(void){
  eps_open("out.ps", 480, 480);
  eps_cmd("240 240 translate");
  for(int i = 1; i <= 5; ++i) {
    eps_drawcircle(-160, 220-i*80, 40);
    eps_drawcircle(160, 220-i*80, 40);
  }
  for(int j = 1; j <=3; ++j) {
    eps_drawcircle(-160+j*80, 140, 40);
    eps_drawcircle(-160+j*80, -180, 40);
  }
  eps_close();                          
  return 0;
}

~プログラムの説明~
演習2a(3)のような図の絵を作成した。半径40の円を正方形型に一辺に五つ並べた。

~考察~
PostScriptをどのように用いているのかやコマンドを理解することに最初苦戦した。
色や明るさ、幅など自由に調整して絵を描けるとができることがわかった。
今回作成したものは円を並べるだけであったが、どのように設計するか
を考える際、イメージをどのようにプログラムするか考えることが大事
だとわかった。

[アンケート]
Q1. プログラムを作るという課題はどれくらい大変でしたか?
休暇中はほとんどやっていなかったため、基礎的なことから復習するのは大変だった。
Q2. epsライブラリを使ってみてどのように感じましたか。
自由に絵を描けてすごいと思った。
Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
次回以降も頑張りたい。

y1910707 1b:○ Wed May 13 17:44:24 2020



演習1.e
#include <stdio.h>
int fib(int n){
if (n <= 2){
 return 1;
} else if (n >= 3){
 return fib(n-2) + fib(n-1);
}
}

int main(void) {
 int n;
 printf("n> "); scanf("%d", &n);
 printf("%d", fib(n));
 printf("\n");
 return 0;
}

説明

これはフィボナッチ数列の第n項を再帰関数で計算, 出力するものであ
る. フィボナッチ数列は通常f(n)=f(n-1)+f(n-2)と計算されることから,
最初に宣言したfib()で再起計算を行っている.

考察
コードに関しては十分に分かりやすく書けているだろう. フィボナッチ
数列は分岐を元としたその総数の数列であるためプログラミングにおい
てもその役割はあり, 再帰関数はもともと処理が重いものであるのでC
言語のような計算が早い言語にはよい題材であっただろう.
 

演習2.b
#include <stdio.h>
#include "eps.h"

int main(void) {
 double a, b, n;
 printf("a> "); scanf("%lf", &a);
 printf("b> "); scanf("%lf", &b);
 printf("n> "); scanf("%lf", &n);
 double dx = 1/n;
 double x = 0;
 double i = 0;
 double l = 0;
 double y, dy;
 eps_open("out.ps", 480, 480);
 eps_drawline(200, 400, 600, 400);
 eps_drawline(400, 600, 400, 200);
 while (x < b){
   y = x*x + 4;
   dy = 2*x*dx;
   eps_drawline(x+400, y+400, x+dx+400, y+dy+400);
   x += dx;
 }
  x = 0;
 while (x > a){
   y = x*x + 4;
   dy = 2*x*dx;
   eps_drawline(x+400, y+400, x-dx+400, y-dy+400);
   x -= dx;
 }
 eps_close();
 return 0;
}

説明
y=x^2+4の二次関数を図示するプログラムである. 始めにa, b(a<=0,
b>=0)で範囲を指定し, 自然数nでxの分割数を決めて, yの増加量に応ず
る微少直線を生成するという仕組みになっている. なおx=a~0, 0~bの二
通の生成処理を行っている.
 

考察
本コードではwhile文を二度使っているが, 一応は一つにまとめること
ができる. 扱う変数が増えてしまうのを避けたつもりでいたが, 実際に
は分割せずx=a~bとすれば実現ができた. このプログラムは微分可能な
関数ならば大抵図示することができ, コンピューターグラフィックの分
かりやすい例と言えるだろう.

アンケート
Q1. 久々だったので手が止まることがたまにあった.
Q2. 似たようなものを扱ったことはあるので難しくはなかった.
Q3. 知識を学び直しつつ勉強に励んでいきたい.

y1910708 1b:○ Tue May 12 19:12:41 2020


1910708 個人作業 2020/5/12

1-b

#include <stdio.h>
#include <math.h> // sqrtを使う場合必要
#include <stdbool.h> // true, false, boolを使う場合必要

int main(void) {
int n;
int a;
double b;
printf("n> "); scanf("%d", &n);

if(n >= 0){
    a = 2;
    for (int i = 1; i < n; i++){
        a = a*2;
    }
    printf("answer is %d.\n", a); 
}else{
    b = pow(2, n);
    printf("Answer is %f.\n", b); 
}

return 0;
}

整数nを入力し、2^n を出力する。n ≥ 0のときは整数、そうでないときは
実数形式で出力するコード
nの判断はifで行い、2^nの計算はそれぞれpowとforを用いて計算した。

n>=0 のときとn<0のときの両方でpowを用いて計算しようとしたが、powが整数では
使えなかったため、n = n*2 という形で計算した。

1-c

int main(void) {
int n;
int a;
double b;
printf("n> "); scanf("%d", &n);

for (int i = 2; i <= n ; i++){
    if(n % i == 0){
        n = n / i;
        printf("%d", i);
        i = 1;
    }
}
return 0;
}

整数n (n > 1)を入力し、nの素因数分解を表示するコード
nが素数でないかをfor,ifを用いて判断し素数でないならば、nを最小の数で割り、
その商を新たにnとした。

2-a-1

#include <stdio.h>
#include "eps.h"

int main(void){
    eps_open("out.ps", 480, 480);   
    for(int i0 = 1; i0 <= 4; ++i0) {
        for(int i1 = 1; i1 <= 4; ++i1) {
             eps_drawrect(200 + 30*i1, 400 - 30*i0, 15,15);
         } 
   }
    eps_close();                          
    return 0;  
}

等間隔で並ぶ正方形を4*4の16個描くコード
縦横に描くのはfor文を2回重ねてコードを書いた。

2-a-2

int main(void){
    eps_open("out.ps", 480, 480);   
    for(int i0 = 1; i0 <= 3; ++i0) {
        for(int i1 = 1; i1 <= 3; ++i1) {
             eps_fillrect(200 + i0*60, 400 - i1*60, 30, 30); 
         } 
   }
    for(int i0 = 1; i0 <= 2; ++i0) {
        for(int i1 = 1; i1 <= 2; ++i1) {
             eps_fillrect(230 + i0*60, 370 - i1*60, 30, 30); 
         } 
   }

    eps_close();                          
    return 0;  
}

5*5のチェッカー柄を描くコード
黒いマスが3つの行と2つの行に分けて上記と同様にfor文で回した

2-a-3

int main(void){
    eps_open("out.ps", 480, 480);   
    for(int i = 1; i <= 5; ++i) {
        eps_drawcircle(200 + i*30, 400, 15);   
        eps_drawcircle(200 + i*30, 280, 15);   
    }
    for(int i = 1; i <= 3; ++i) {
        eps_drawcircle(230, 400 - i*30, 15);   
        eps_drawcircle(350, 400 - i*30, 15);   
    }
    
    eps_close();                          
    return 0;  
}

正方形の辺上に円を配置した図形を描くコード
円の数が5つの行と円の数が3つの列に分け、同様にfor文で回した

3つともコードを書いてから、eps_cmd("translate") に気付いたため、
今回のコードの座標は手探りで丁度よいところをさがし、その値を原点
として扱ったため、
他の人が見た時にx座標やy座標がイメージしにくいように思われる。

アンケート
Q1. プログラムを作るという課題はどれくらい大変でしたか?
今回は比較的楽ではあった。

Q2. epsライブラリを使ってみてどのように感じましたか。
少し扱いに手こずった。

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
次回も頑張りたい。

y1910709 1b:○ Sun May 17 20:21:12 2020


学籍番号:1910709
@@氏名:吉田昂太
個人作業
提出日時:5/17

<1つ目の課題(演習1b)>
課題の再掲:
整数nを入力し2n を出力する。n ? 0のときは整数、そうでないときは
実数形式で出力すること。

プログラムのソース:
#include <stdio.h>

int power1(int n){
int i;
int p = 1;
for (i = 1; i <= n; ++i) {p = p * 2;}
return p;
}

double power2(int n){
 int i;
 double p = 1;
 n=0-n;
 for (i = 1; i <= n; ++i) {p = p * 2;}
  return 1 / p;
}

int main(void) {
   int n;
   printf("n> "); scanf("%d", &n);
   if(n > 0) {printf("%g\n", power1(n));}
   else {printf("%g\n",  power2(n));}
   return 0; 
}

説明:
変数nを入力し,n>0の時関数power1を実行し,それ以外の時関数power2を実行する。
power1関数はfor文を使って変数iがn以下の時繰り返し変数pに2をかける。power2は、
変数nがマイナスなのでpower1と同じことをしてから変数pを逆数にする。
それぞれの関数でp,1/pをreturnすることで2のn乗を返す。

考察:
for文やif文の基本的な文法を実際に使うことで学ぶことができた。途
中で変数や関数の型の定義
で正しく結果が出力されないことがあった。どんな値を扱うか注意する
ようにしたい。

<2つ目の課題(演習1c)>
課題の再掲:
整数n (n > 1)を入力し、nの素因数分解を表示する。たとえば60を入力
したら「2 2 3 5」を出力する(出力の順番や形式は任意)。

プログラムのソース:
#include <stdio.h>

int decompose(int num) {
  int i;
  printf("%d =", num);
  while (num % 2 == 0){ 
        printf("%d *",2);
        num = num / 2;      
    }
  for (i = 3; i < num; i++){ 
        while (num % i == 0){
            printf("%d *", i);
            num = num / i;
      }
    }  
    printf("%d ",num);
    return 0;
}
int main(void) {
  int num;
  printf("num> "); scanf("%d", &num);
  decompose(num);
  return 0;
}

説明:
変数numを受け取り関数decomposeを実行する。素因数分解した後残るの
はすべて素数なので素数のなかで唯一の偶数である2でnumをwhile文で
割っていく。これ以上2で割れなくなったらfor文で変数iに3を代入し3
以上の数でwhile文でnumを繰り返し割っていく。この時割った数を
printf()で画面に表示する。for文のループが終わった後残った数を最
後にprintfで出力する。


考察:
このプログラムを書く上で素数の性質や効率的な素因数分解のやり方な
どを考える必要があった。ソースコードを書くことにはあまり時間はか
からなかったが、どうしたらソースコードをより簡潔に書くことに苦戦
した。

アンケート:
Q1 そんなに大変ではなかった
Q2 幾何学的な模様を描くのには便利だと思った
Q3 コードを簡潔に書くためには工夫が必要だと感じた。

y1910714 1b:○ Sun May 17 22:57:36 2020


個人作業
提出日時:5月17日 23:00頃

1e
【ソースコード】
#include <stdio.h>

int odd(int n) { return 2*n+1; }
int main(void) {
  int n; printf("n> "); scanf("%d",&n);
  for(int i = 0; i < n; i++ ) {
    printf("%d ",odd(i));}
  printf("\n");
  return 0;
}

【説明】
入力した整数nの項数だけ、ある数列を出力するプログラムを作った。
簡単のため奇数列を扱った。テキストのreturn文の説明、すなわち「式
の値は、関数の値となるので、それを計算に使ったり変数に代入できる」
ことを参考に、
まず、奇数列の第n項を返す関数oddを作った。そしてmain部では、nを
入力し、カウンタiを0からn-1までインクリメントしながらodd(i)を出
力した。

【考察など】
数列の出力の仕方は様々でしょうが、簡単に思いつくのが「一般項a[n]
に具体的なnを代入する」ような方法です。
初歩的な考えを初歩的なコードで実現できる仕組みは、融通の利く感が
あって好ましく思います。

1c
【ソースコード】
#include <stdio.h>
#include <math.h>

int fipn(int n) {
  int limit = (int)sqrt(n);
  for(int i = 2; i <= limit; ++i) {
    if(n % i == 0) { return i; }
  }
  return n;
}
int main(void) {
  int n; printf("n> "); scanf("%d",&n);
  while(n != 1) {
    int p = fipn(n); printf("%d ",p); n = n/p;
  }
  printf("\n");
  return 0;
}


【説明】
入力した整数nの素因数分解を表示するプログラムを作った。
素数判定の例題を参考に、判定でfalseを返す部分で除数を返し、true
を返す部分でnを返すような関数fipn(n)を定義した。main部では、まず
nを入力し、そしてnが1でないあいだ次のことを繰り返すようにした。
すなわち、整数pにfipn(n)を代入し、pを出力し、最後にnをpで割ってn
に代入した。
pはnの約数なのだからnは必ず割り切れるし、nが素数のときn/p=1とな
るからループが終了するので、うまくいくのである。

【考察など】
参照した素数判定を考えてみたい。判定はnが1とその数以外に「約数を
もつか否か」を見るものだから、約数の候補は2から√nまでで十分であ
る。調べる範囲を限定するこの考え方は、nが大きくなればなるほど効
果が増す。例えば、n=4なら約数の候補が[2,3,4]から[2]へと2個減るだ
けだが、n=10000ならその個数は9999個から100個へ9899個も減る。

1d
【ソースコード】
#include <stdio.h>
#include <stdbool.h>
#include <math.h>

bool isprime(int n) {
  int limit = (int)sqrt(n);
  for(int i = 2; i <= limit; ++i) {
    if(n % i == 0) { return false; }
  }
  return true;
}
int main(void) {
  int n;
  printf("n> "); scanf("%d",&n);
  for(int i = 2; i <= n; i++) {
    if(isprime(i)) { printf("%d ",i); }
  }
  printf("\n");
  return 0;
}

【説明】
入力した整数n以下の素数を列挙するプログラムを作った。
これは、素数判定の関数bool isprime(n)をそのまま使えばよい。すな
わち、main部において、nを入力し、カウンタiを2からnまでインクリメ
ントしながら、iが素数なら出力するようにすればよいのである。
「iが素数なら」の部分はif(isprime(i))と簡単に表現できる。
【考察など】
私が今回初めて知ったbool型はとても有用であることがわかった。bool
型はtrueを1,falseを0で返すが、
これは比較演算子を用いた式の値と同じだから、授業で指摘のあったよ
うに、例えばif文なんかで「isprimeがtrueなら」などと日常会話に近
い感覚でコードを書くことができる。

【アンケート】以下、Q1の回答をA1などと記す。
A1.例題をちょっといじっただけなのでそれほど。今後、それだけでも
時間のかかるような複雑な内容になれば、他の科目の課題より大変に感
じるかも。

A2.rubyで描図したときより大変でした(例題を写すにとどまりました、
すいません)。ただ、1aのQ2で答えたようなC言語のメリットもあるので、
こうしたプログラムの書き方に慣れたいです。

A3.頑張ります。。

y1910715 1b:○ Sun May 17 18:02:09 2020


学籍番号:1910715
「個人作業」

一つ目の課題の再掲:課題1-c
整数nを入力し、nの素因数分解を表示する。

作成したプログラム:
//prifactor.c --- display prime factor.
#include <stdio.h>
#include <math.h>

void prifactor(int n){
    while(n>1){
        int i;
        for(i=2; n%i !=0; ++i);
        n = n/i;
        printf("%d",i);
        if(n==1){
            break;
        }
        printf(" ");
    }
}

int main(void){
    int n;
    printf("n> "); scanf("%d", &n);
    if(n<0){
        n = -n;
    }
    printf("prime factor of %d is [", n);
    prifactor(n);
    printf("]\n");
    return 0;
}

説明:
関数prifactorは与えられた正の整数nに対してその素因数をfor文を使っ
て、小さいものから順に表示している。for文の中では素因数かの判定
に使う変数iは初期値が最も小さい素因数である2に設定されている。そ
こからnをiで割った余りが0でない間iの値を一ずつ増やして素因数を探
している。余りが0になったらforループを終え、nの値をnをiで割った
ものに変え、iを表示した。この時、nが1になっていたらそこでwhile文
を抜けて関数prifactorの処理を終える。なぜここでbreakを使って終わ
らせたかというと、普通にprintf("%d",i);の部分で空白を入れると表
示結果の最初もしくは最後に余分な空白が生まれてしまうからである。
素因数を表示した後に空白を表示し、もしこれ以上素因数がなければ、
(nが1であれば)空白を表示せずに関数prifactorを終わらせ、表示結
果をなるべくきれいにした。またwhileの条件がn>1となっているのも渡
されてくる最初のnの値が1や0の場合はwhilenの中身を実行させないた
めである。main関数では以上の関数をふまえて、入力された整数nに対
して負の数の場合はそれを正の数に直してから関数prifactorに数字を
渡している。また、printf("prime factor of %d is [", n);と
printf("]\n");で表示結果の外枠を表示している。


考察:今回のコードでは一つの素因数を得られたら変数iを2に戻して最
初から調べているが、小さい順に調べている以上得られた素因数より小
さい素因数は存在しないか既に調べてあるはずである。つまり、次の
for文のループでは変数iをひとつ前のループで得られた素因数から始め
ればもう少し処理する数が減るのではないかと考える。

二つ目の課題の再掲:課題1-d
整数nを入力し、n以下の素数を出力する。

作成したプログラム:
//fprime.c --- display the prime which is N and below.
#include <stdio.h>
#include <math.h>
#include <stdbool.h>

bool isprime(int n){
    int limit = (int)sqrt(n);
    for(int i= 2; i<= limit; ++i){
        if(n%i == 0){return false;}
    }
    return true;
}

void fprime(int n){
    for(int i= 2; i<= n; ++i){
        if(isprime(i)){
            printf("%d", i);
            //if(i== n){break;};
            printf(" ");
        }

    }
}

int main(void){
    int n;
    printf("n> "); scanf("%d", &n);
    printf("prime of %d and berow is [");
    fprime(n);
    printf("]\n");
    return 0;
}

説明:
今回は与えられる数は正の整数とした。
与えられた数が素数かを判定するのにはisprimeを使い、n以下の素数を
表示させるのにはfprimeを使った。isprimeは教科書に記載されている
のと同様に、与えられて数の平方根までの間に割り切れる数がないかを
確認することで素数かどうかを判定しているfprimeでは渡された数n以
下の値を順にisprimeに渡し、trueが返ってきたら表示している。main
関数では結果表示の外枠と入力された数をfprimeに渡している。

考察:
今回も一つ目の課題と同じように表示結果に余分な空白を入れないよう
にしたかったが、for文の中にあるif文の中に結果を表示させる部分が
あるため、breakではforループを抜け出せなかった。(動作テストの時
には//if(i== n){break;};の//は外しました。)
そのため表示結果もたとえばn=15の時にはprime of 232978944 and
berow is [2 3 5 7 11 13 ]となってしまった。そのため、二重ループ
を抜け出す方法などについてこれから調べていきたい。

アンケート:
Q1. プログラムを作るという課題はどれくらい大変でしたか?
今回の課題はまだ一年生の時の復讐のようなものだったので難しくはな
かったが、それでも推敲できる部分があり一発で理想のコードは書けな
いと感じた。

Q2. eps ライブラリを使ってみてどのように感じましたか。
GUIで何かを書くより、論理的に考えて画像をつくるというところで、
webページのレイアウトなどと似たものを感じた。

Q3. リフレクション(今回の課題で分かったこと)・感想・要望をどうぞ。
途中、情報領域学演習第一で使っていた関数などを忘れてしまっている
ことが多く、完全な定着には至っていないと感じた。

y1910716 1b:○ Sun May 17 17:08:41 2020


学籍番号 1910716
個人作業
提出日時 2020年5月17日 

[課題1]
演習1 c. 整数n(n >1)を入力し、nの素因数分解を表示する。

ソース
#include<stdio.h>

int main(void){
	int n,j;
	printf("n> ");scanf("%d",&n);
	while(n!=1){
		for(j = 2;j <= n;j++){
			if(n%j == 0){
				printf("%d ",j);
				break;
			}
		}
		n = n/j;
	}
	printf("\n");
	return 0;
}

説明
整数nを入力させた。
次にその整数を2からnまでの整数で割っていき、その中で割り切れる
最小の整数を求め、その整数を表示させた。nを求まった整数で割り、
その商をnに代入した。以上の動作をnが1になるまで繰り返した。

考察
本来は素数の中で割り切れる整数を探せばいいが、このプログラムは最
大で2からnまでのすべての整数で割り算を行うため動作に無駄がある。
そのため演習1 d.のプログラムと組み合わせることで無駄がなくなる
と思う。

[課題2]
演習2 a.(2)チェッカーボード・チェックの作成

ソース
#include <stdio.h>
#include "eps.h"

int main(void){
	int i,j;
	eps_open("b2.ps",250,250);
	eps_cmd("0 0 translate");
	for(i = 0; i < 5; ++i) {
		for(j = 0;j < 5; j++){
			if((i+j)%2==0){
				eps_cmd("0.2 setgray");
				eps_fillrect(i*50,j*50,50,50);
			}
			else{
				eps_cmd("0.8 setgray");
				eps_fillrect(i*50,j*50,50,50);
			}
		}
  	}
	eps_close();
	return 0;
}

説明
x軸y軸をそれぞれ5等分し、それぞれの番号の和が偶数の時は濃色の四
角を配置し、奇数の時は淡色の資格を配置した。

考察
一年の時に作成した絵は左上を原点としていたが、今回は左下が原点になっている。

[アンケート]
Q1.
時間はかかったが、楽しかったので大変とは感じなかった。

Q2.
原点が思っていた位置と違ったので原点ずらしがうまくいかなかった。
x軸y軸が実際のグラフと同じ方向だったので、絵を描くよりグラフを
作るほうが得意であるように感じた。

Q3.
epsで絵を描くことに手こずった。まだ試せていないepsのコマンドがあ
るので時間があるときにでも試してみたい。