yoppa.org


immediate bitwave

SFC – デザインとプログラミング 2017

反復と乱数

プログラムを構造化していく際に、3つの重要な構成要素があります。それは、「順次 (Sequence)」、「反復 (Iteration)」、「分岐 (Selection)」 です。今回は、この3つの構成要素の中の反復 (Iteration) に注目して、Processingで形を描きながら反復について考えます。また、一意的でないランダムな値である「乱数」と反復を組合せて、どのような表現か可能となるか探求していきます。

スライド資料

反復 (Iteration)

  • コンピューターは、「くりかえし」の天才!
  • 最近のマシンであれば、1秒間に2億回(2GHz)以上のクロックで、正確に計算をくりかえすことができる
  • 反復は、プログラムの基本的で重要な構造のひとつ
enter image description here

構造化プログラミング、3つの構成要素

  • 順次 (Sequence)、反復 (Iteration)、分岐 (Selection)

30年前のPCでのプログラムを再現

  • Commodore 64
enter image description here
  • HELLOをひたすらくりかえす
10 PRINT “HELLO”
20 GOTO 10
  • 実行結果 – プログラムを止めるまで、延々と“HELLO”を出力
  • ちょっとアレンジ
10 PRINT “/\”;
20 GOTO 10
  • 波の模様が延々と続く
  • さらに、乱数(後述)を利用して…
10 PRINT CHR$(205.5+RND(1));
20 GOTO 10
  • 迷路のような複雑な模様が浮かびあがる
  • Mac OS Xのターミナルでも、以下のコードで同じ現象を再現可能
yes 'c=(╱ ╲);printf ${c[RANDOM%2]}'|bash

参考: 10 PRINT CHR$(205.5+RND(1)); : GOTO 10

enter image description here
  • では、Processingをつかって、くりかえしの実験をしてみましょう!

変数

  • クイズ: 正方形の中に、ちょうど半分の幅と高さの正方形を描く
size(400,400);

rect(100,100,200,200);
rect(100,100,100,100);
  • クイズ2:さらに半分の正方形を中に
size(400,400);

rect(100,100,200,200);
rect(100,100,100,100);
rect(100,100,50,50);

手で計算するのではなく、もっとかしこくできないか

  • 描いた四角形の常に半分のサイズで次の四角形を描く
  • 現在のサイズの値を記録できると便利なのに…
  • 「値を記録する」→ 変数を利用する

変数

  • 変数とは?
  • 一時的に値(文字、文字列、数字など)を記憶しておく場所
  • データを入れておく「箱」のようなもの
  • データ型 – 値の種類
  • Processingでよく用いられるデータ型
    • int:整数 (-1, 0, 1, 2, 3….)
    • float:少数 (-0.01, 3.14, 21.314)
    • boolean:ブール値、真か偽か、(true, false)
    • char:1文字分のデータ(a, b, c, d…)
    • color:色の情報を保存
  • 宣言:使用する変数の名前の箱を準備する
int hoo;
  • 代入:変数の箱に値を入れる
hoo = 0;
  • 演算:変数の値を計算する
hoo = hoo + 1;
  • 先程の例は、変数を利用すると下記のようになる
size(400,400);
float rectSize = 200;

rect(100,100,rectSize,rectSize);
rectSize = rectSize / 2;
rect(100,100,rectSize,rectSize);
rectSize = rectSize / 2;
rect(100,100,rectSize,rectSize);
rectSize = rectSize / 2;
rect(100,100,rectSize,rectSize);
rectSize = rectSize / 2;
rect(100,100,rectSize,rectSize);
rectSize = rectSize / 2;
...
  • どんどん小さな正方形を描いていくことができる
  • 変数を利用して、いろいろ実験してみましょう
  • 変数の使用例
size(800, 600);
background(0);
noStroke();
fill(0, 127, 255, 100);

float x, y, diameter;
x=width/2;
y=height/2;
diameter=height;

ellipse(x, y, diameter, diameter);
diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);
diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);
diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);
diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);


– 実行結果

繰り返し – for文

  • さっきのプログラムに注目
diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);

diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);

diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);

diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);

...
  • 繰り返しの部分を何度も書かずに一気に指定したい
diameter = diameter / 1.4;
ellipse(x, y, diameter, diameter);

例えば、これを10回くりかえすという指定はできないだろうか? → for文を使う!

  • for文の書きかた
for (初期化式; 継続条件式; 再初期化式) {
    文;
}
  • 初期化式:初期化の際の条件式
  • 継続条件式:繰り返しを継続する条件式
  • 再初期化式:繰り返されるたびに実行される式

Processingでfor文を使ってみる

  • forを用いた、繰り返しの例 1
int i;

for (i=0; i < 100; i=i+1){
    print("+");
}
  • forを用いた、繰り返しの例 2
int i;

for (i=0; i < 100; i=i+1){
    print(i + ", ");
}
  • 同心円をくりかえしを用いて描画
//初期条件の設定
size(800, 600);
background(0);
noStroke();
smooth();
fill(0, 127, 255, 40);

float x, y, diameter;
x = width / 2;
y = height / 2;
diameter = height;
int i;

//同心円を描いていく
for (i=0; i < 32; i++) {
  ellipse(x, y, diameter, diameter);
  diameter = diameter / 1.2;
}

くりかえしによる入れ子構造

  • for文を使ってくりかえし図形を描く
  • 工夫することで図形が入れ子状に連なった図形を描くことが可能

入れ子状の矩形

size(800, 600);
background(0);
stroke(0);
fill(0, 127, 255, 100);
float x, y, w, h;
x = 0;
y = 0;
w = width;
h = height;

for (int i = 0; i < 32; i++) {
  w = w / 2;
  h = h / 2;
  rect(x, y, w, h);
  rect(x + w, y + h, w, h);
}

入れ子状の円

size(800, 600);
background(0);
stroke(0);
fill(0, 127, 255, 100);
float x, y, diameter;
x = width/4;
y = height/2;
diameter = width/2;

for (int i = 0; i < 32; i++) {
  ellipse(x, y, diameter, diameter);
  ellipse(x + diameter, y, diameter, diameter);
  diameter = diameter / 2;
  x = x / 2;
}

回転のくりかえし

size(800, 800);
background(0);
noStroke();
fill(0, 127, 255, 30);

float diameter;
diameter = width/1.8;

translate(width/2, height/2);
rotate(PI/4);
for (int i = 0; i < 256; i++) {
  ellipse(diameter / -2, 0, diameter, diameter);
  ellipse(diameter / 2, 0, diameter, diameter);
  diameter = diameter / 1.05;
  rotate(PI / 24.0);
}

色について – RGBモデルとHSBモデル

  • 色を指定するには?
    • R(赤) G(緑) B(青)の三原色で指定する
    • 加法混色 (光の三原色であることに注意) ←→ 色料の三原色
  • RGB以外の方法での色の指定
    • HSB (HSV)による指定
      • 色相 (Hue):色の種類
      • 彩度 (Saturation):色の鮮かさ
      • 明度 (Brightness):色の明かるさ
  • プログラミングしながら色を指定する場合は、HSBの方が直感的に望みの色を指定し易いことも多い
  • HSB (HSV) 色空間の視覚イメージ
    • 色相:外環の角度
    • 彩度:中心点からの距離
    • 明度:高さ
  • HSB (HSV) 色空間への切替方法
  • colorMode関数を使用する、
  • それぞれのパラメータの範囲も同時に指定する
  • 例:
    • 色相:360階調(°)
    • 彩度:100階調(%)
    • 明度:100階調(%)
colorMode(HSB, 360, 100, 100);

繰り返しによる色の表現

  • HSB (HSV) 色空間と繰り返しを使用して、色の変化をつかった様々な表現に挑戦

例:虹色のグラデーション

//変数の宣言
int i;

//初期設定
size(800,600);
colorMode(HSB, 360, 100, 100);
noStroke();

//色相を変化させながら四角を描いていく
for(i=0; i < width ; i++){
  fill(i * 360.0/800.0,100,100);
  rect(i,0,1,height);
}

乱数

  • プログラムは、設定された手続きを何度でも繰り返す
  • そのままでは、予想外の挙動はしない
  • 完全に全てをコントロールするのではなく、偶然性、意外性をとり入れたい
  • 実行する度に毎回異なる値を出力する仕組み
  • 乱数
    • 何ら法則性、規則性のない、でたらめな値を出力
    • ランダム

Processingで乱数を生成 – 乱数の範囲を指定する

  • 例:0から100の乱数を生成
random(100);

  • 例:100から1000の乱数を生成
random(100, 1000);

ランダムに色を塗る

//ランダムに色を塗る

//変数の宣言
int i;

//初期設定
size(800, 600);
colorMode(HSB, 360, 100, 100);
noStroke();

//ランダムに色を塗る
for (i=0; i < width ; i++) {
  fill(random(360), random(100), random(100));
  rect(i*1, 0, 1, height);
}

似たような色相で

//似たような色相で色を塗る

//変数の宣言
int i;

//初期設定
size(300,300);
colorMode(HSB, 360, 100, 100);
noStroke();

//似たような色相で色を塗る
for(i=0; i < 300; i++){
    fill(random(200,220),random(100),random(100));
    rect(i*1,0,1,300);
}

似たような明度と彩度で

//変数の宣言
int i;

//初期設定
size(800, 600);
colorMode(HSB, 360, 100, 100);
noStroke();

//似たような明度と彩度で色を塗る
for (i=0; i < width ; i++) {
  fill(random(360), 60, 90);
  rect(i*1, 0, 1, width);
}

例6:たくさん円を描く

  • さらに図形を描く場所もランダムに!
//変数の宣言
int i;

//初期設定
size(800, 600);
background(0);
colorMode(HSB, 360, 100, 100, 100);
noStroke();

//たくさんの円を描く
for (i=0; i < 100; i++) {
  float diameter;
  fill(random(200, 240), random(50, 100), random(50, 100), 50);
  diameter = random(50, 200);
  ellipse(random(width), random(height), diameter, diameter);
}

たくさん四角形を描く

//たくさんの四角形を描く

//変数の宣言
int i;
//初期設定
size(800, 600);
background(0);
colorMode(HSB, 360, 100, 100, 100);
rectMode(CENTER);
noStroke();
//たくさんの四角形を描く
for (i=0; i < 100; i++) {
  fill(random(0, 40), random(50, 100), random(50, 100), 50);
  rect(random(800), random(600), random(5, 200), random(5, 200));
}