それが僕には楽しかったんです。

僕と MySQL と時々 MariaDB

確率的解析と乱択アルゴリズム

確率的解析と乱択アルゴリズム

確率論と計算機における問題解決の手法として
確率的解析と乱択アルゴリズムについて解説していく。
ここでは以下の問題を考える。

雇用問題

問題:
あなたは秘書を雇おうとしている。
その際にあなたは代理店を利用することにした。
その代理店は毎日1人、秘書を送ってくる。
あなたはその候補者と面談し、採用するかどうか選択する。
しかし、面談するのにあなたは費用を払う必要がある。
それに加え実際に候補者を雇うには更に費用がかかる。
そこであなたは現在の秘書よりも候補者が優れている場合すぐにでも雇う。
しかし、いくら費用がかかるか見積もりたい。
かかる費用を求めなさい。

この問題における重要点

この問題の解として、候補者を逐一比較していき数値的に高ければ雇う。
といったアルゴリズムが考えられる。
またこの問題では実行時間どうこうより
計算によって算出される費用に重点が置かれるため一般的な計算量の解析とは異なりコスト解析を行う必要がある。

このアルゴリズムにおける面談コスト(C_i)は雇用コスト(C_h)より小さい。
それを踏まえてn人の候補者からm人雇用した場合
このアルゴリズムのコストはO(nC_i+mC_h)となる。
しかしn人の面談は必ず行わなければならないので、nC_iは変化しない。
よって、この問題では雇用コストC_hの解析に集中するべきである。

最悪の場合

この問題における最悪の場合は、面談した全ての候補者を雇用するときである。
つまり候補者の能力値が昇順になっているときである。
この時の雇用コストはO(nC_h)となる。

確率的解析 ~Probabilistic Analysis~

上記の問題の様に確率を用いる問題の解析を「確率的解析」という。
確率的解析とは実行時間解析の手法ではない。
上記の問題における雇用コストを解析する手法である。
確率的解析を適用するには、入力分布に関する知識を用いるか
その分布自体を仮定してしまう必要がある。
それが完了すれば実行時間の平均を計算してアルゴリズムを解析する。
平均は入力可能な分布の上でとること、つまり全ての可能な入力に対する平均を算出する。

入力分布は慎重に決める必要がある。
というのも、問題において入力集合に対してある分布をとることで効率のよいアルゴリズムを設計するためである。
しかし妥当な入力分布が存在しない場合ある。
そのようなときには確率的解析を用いることができない。

今回の雇用問題の場合、候補者はランダムに出現し二人の候補者を比較できると仮定できる。
この仮定より、以下を満たす全順序が存在する仮定する。

{ \displaystyle
集合Xにおいて関係 \le が全順序付けられるなら、Xの任意の元a,b,cに対して \\
1)a \le b かつ b \le a ならば a=b(反対称律) \\
2)a \le b かつ b \le c ならば a \le c(推移律) \\
3)a \le b または b \le aのいずれかが成立する(完全律) \\
}

ここまで示した仮定により、候補者全員を1からnまでの数でただ一通りにランク付け出来る。
それを求める関数をrank(i)とし候補iのランクを表し、大きいほど能力値が高いとする。
よって候補者がランダムに出現することと、ランクから作られるn!通りの置換のうち
任意の一つに一致する事象はどれも等確率である。
またはランクリストは一様ランダム置換と言える。

確率的解析における乱択アルゴリズム

確率的解析を用いるには入力分布に関する知識が必須となる。(上記)
しかし、入力分布がわからない場合も多く、たとえ分布がはっきりとわかっていたとしても
必ずアルゴリズムとしてモデルに出来るとは限らない。
そこでアルゴリズムの振る舞いをランダムにしてアルゴリズム設計の道具としてランダム性を使うことが出来る。
ただこのままでは最初に挙げた問題の解決法としては使えないので
問題を以下のように変更する。

代理店が候補者リストを事前に送ってくる。
そのリストから私達が毎日面談するものを決める。

こうすることでランダムな順序で送られてくる候補者に
ランダムな順序を強制する。

乱択アルゴリズム

ここまで長々と書いてきたが乱択アルゴリズムとは何か?
多少変更した上記の問題に対して、解決方がランダム性を使うものとする。
こういった場合に、アルゴリズムの振る舞いは入力データと乱数生成器の2つで決まる。
そのように乱数を使用するアルゴリズム乱択アルゴリズムという。
ただし計算機で用いる乱数生成器は、統計的にランダムに「見える」値を返す
決定アルゴリズムを用いた擬似乱数生成器によって実現されることが多い。

指標確率変数

雇用問題やその他乱択アルゴリズムを用いるであろう多数のアルゴリズム解析に指標確率変数を用いる。
これは確率と綿密な関係にある期待値をお互いに変換する便利な道具のようなものである。
標本空間Sと事象Aが与えられている時、Aに関する指標確率変数を

{\displaystyle
\begin{eqnarray}
I\{A\} = \left\{ \begin{array}{11}
1 & Aが発生する時 \\
0 & Aが発生しない時
\end{array}\right.
\end{eqnarray}
}

と定義する。

例1

一枚のコインをコイントスして表がでる回数の期待値を求めてみる。
標本空間はS=\{H,T\}とし、コインには裏表しかないので確率Pr\{H\} = Pr\{T\}=1/2となる。
今回は表が出る回数の期待値なので、事象H(表が出るとき)に対して指標確率変数X_Hを定義できる
ここでは上記の指標確率変数にしたがって、表が出るなら1、裏がでるなら0とする。

{\displaystyle
\begin{eqnarray}
X_H=I\{H\}
= \left\{ \begin{array}{11}
1 & Hが発生する時 \\
0 & Hが発生しない時(Tが発生する時)
\end{array}\right.
\end{eqnarray}
}

この様にこの問題(コイントスで表が出る回数の期待値を求める)に対して指標確率変数を定義した。
この場合の期待値をEとして、指標確率変数X_Hを用いて期待値を計算する。

{
\displaystyle
\begin{equation}
E \left[ X_H \right] \\ = E\left[ I\left\{ H \right\} \right] \\ = 1*Pr \left\{H \right\}+1*Pr \left\{T \right\} \\ = 1*(1/2)+0*(1/2) = 1/2
\end{equation}
}

よって1枚のコインをコイントスした時、表がでる回数の期待値は1/2となる。
今回のコイントス問題の様に単純な問題だと指標確率変数を用いるのは面倒に感じるかも知れないが
これが一回のコイントスではなく一般のn \in \mathbb{N}回に対してだと圧倒的に便利になる。
その例を次に示す。

例2

例1で考察した問題に対して一回のコイントスではなく一般のn \in \mathbb{N}に対して考察してみる。
ここでi番目の指標確率変数をX_iとする。この時0 \le i \le nn,i \in \mathbb{N}とする。
例1で示した様に指標確率変数の和で表せるので、全体の確率変数Xは次の様に表せる。

{\displaystyle
\begin{equation}
X = \sum_{i = 1}^{n} X_i
\end{equation}
}

上の式に対して両辺で期待値を取ると

{\displaystyle
\begin{equation}
E \left[ X \right]  \\ 
= E \left[ \sum_{i = 1}^{n} X_i \right] \\
= \sum_{i = 1}^{n} E \left[ X_i \right] \\
X_i = 1/2より \\
= \sum_{i = 1}^{n} 1/2 \\
= n/2

\end{equation}
}

となることがわかる。
これで一般の自然数nに対して期待値を指標確率変数を用いて表現することができた。
なぜ指標確率変数がすぐれた解析技術として使えるかというと、期待値が線形性を持つからである
またこの例で指した様に、指標確率変数を用いる事で大幅に計算を単純化することができる。

雇用問題への応用

候補者は上記の例同様n人いるのでX_ii人目の指標確率変数となる。
それではi人目の指標確率変数を以下のように定義する。

{ \displaystyle
\begin{eqnarray}
X_i = \left\{ \begin{array}{11}
1 & 候補者iが雇用される時 \\
0 & 候補者iが雇用されない時 \\
\end{array} \right.
\end{eqnarray}
}

また確率変数Xは次の様に定義できる。

{\displaystyle
X = X_1 + X_2 + X_3 + ... + X_n
}

更にi人目が最初の候補者からi-1人目までの候補者の中で最も優れている確率は1/iなので
上記の例2よりこの問題の期待値は次の様に表せる。

{\displaystyle
\begin{equation}
E \left[ X \right]  \\ 
= E \left[ \sum_{i = 1}^{n} X_i \right] \\
= \sum_{i = 1}^{n} E \left[ X_i \right] \\
X_i = 1/iより \\
= \sum_{i = 1}^{n} 1/i \\
= \log n +O(1)

\end{equation}
}

乱択アルゴリズムの適用

雇用問題やコイントスの問題のような入力全ての置換が等しい確率で成立するという過程が成り立つような問題では
確率的解析が乱択アルゴリズムを用いた開発の指針となる。
具体的にはアルゴリズムが実行される前に候補者の順番をランダムに並び変え
強制的に全ての置換の出現確率を等しくするようにする。

確率的解析と乱択アルゴリズムの違い

上記の記述より、候補者がランダムに出現しようと期待値は変化しない
決定的なアルゴリズム、つまり解が一意に決まるものと比べて
乱択アルゴリズムは入力時ではなく、アルゴリズム内で入力分布のランダム化が発生する。
そのため、特定の入力に対して情報の更新が何回起こるかわからない。
しかし、以前の実行と今回の実行でランダム化が同じである確率はとても低い。
よって、特定の入力があったとき、その入力に対して常に最悪の処理を引き起こすことがない
乱択アルゴリズムが最悪の処理を実行してしまうのは単に運が悪かったからというだけである。

ここまでの解説で雇用問題に乱択アルゴリズムを適用するのに必要なことは
単に入力配列のランダム化である。
これで乱択アルゴリズムが適用できる。

またこの他にも、以下のような問題にも適用できる。


追加問題

N個の箱がある。
箱を開けるまでわからないが半分は当たりになっている。
あたりを見つけよう。

この場合、最悪のケース。先頭から探索するとして
当たりが全て箱の後ろ半分に偏っていた場合O(n/2)かかってしまう。
しかし、その箱の列をランダム化すると
開ける箱の個数に対する期待値は2以下なので、すぐに発見することができる。

最後に

ここまでざっくりと乱択アルゴリズムと確率的解析について解説した
そのうち実際に乱択アルゴリズムを適用したソースコードでも載せようと思う。

gdbを使ってコアダンプの原因を解析

コアダンプは嫌いだ。

大学やその他情報系専門科のある学校に通ったことがある人が一度は触ったことがあるであろう、C言語
こいつは近年の言語に比べてものすごく面倒くさい書き方をするし、手間もかかる。
中でも最悪なのが「コアダンプ」の文字。
これは、多くのC言語ユーザを苦しめてきただろう。
一応エラーの部類に入ると思うが、こいつはどこで問題が起きているか普通は出力してくれないから直すのがすごく大変。


というわけで、今回はコアダンプの原因をgdbというデバッガを使用して解析してみる(※linuxユーザ向け)

それでは解析の準備

これはこの前自分が実際にコアダンプを発生させてしまったコード。
このファイルをここでは「quicksort.c」とする。

#include<stdio.h>

void quicksort(int array[],int left_index,int right_index){
    
    int left = left_index,right = right_index,tmp;
    
    //基点を左右の中間から選択
    int pivot = array[(left+right)/2];

    while(1){

        //左側で基点より大きい値を探す
        while(array[left] < pivot)
            left++;
        //右側で基点より小さい点を探す
        while(pivot < array[right])
            right++;//ここが間違っている

        if(right < left)
            break;

        //値の入れ替え
        //基点より小さい数値を左、大きい数値を右へ移動させる
        tmp = array[left];
        array[left] = array[right];
        array[right] = tmp;

        left++;
        right--;
    }

    //再帰関数による分割統治法を行う
    if(left_index < right)
        quicksort(array,left_index,right);
    
    if(left < right_index)
        quicksort(array,left,right_index);

}

int main(){
    int i;
    int array[] = {2,4,5,3,1,6,3,1,9};
    quicksort(array,0,9-1);
    for(i = 0; i < 9; i++)
        printf("%d\n",array[i]);

    return 0;
}

こいつを実行させると、もちろんこのメッセージがターミナルに出力される。

Segmentation fault (コアダンプ)

それでは解析を始める

まずコアダンプを解析するためにコンパイラオプションの「-g」をつける。

gcc quicksort.c -g -o quicksort
  • gオプションをつけることで、デバッグ情報を出力するようにする。

つぎに、大抵コアダンプ機能が無効にされているの以下のコマンドで有効にする

ulimit -c unlimited

それが終わったら、問題のある(コアダンプのある)実行ファイルを実行する

./quicksort
Segmentation fault (コアダンプ)

となる。

この時lsでカレントディレクトリを確認すると「core」や「core.xxxxx(xは数字)」といったファイルが生成される。


ここまでできたら、以下のコマンドでデバッグする

gdb ./quicksort core

そうすると、こんな出力が見えるはず(プログラムによって違う)

GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.04) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./quicksort...done.

warning: core file may not match specified executable file.
[New LWP 13685]
Core was generated by `./base1'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000400616 in quicksort (array=0x7ffe0656abc0, left_index=0, 
    right_index=8) at quicksort.c:26
26	        while(pivot < array[right])
(gdb) 

これで大体わかってしまうんだけど、今回はちゃんと手順を踏む。

まずはBackTraceする。

(gdb) bt

#0  0x0000000000400616 in quicksort (array=0x7ffe0656abc0, left_index=0, 
    right_index=8) at quicksort.c:26
#1  0x0000000000400747 in main () at quicksort.c:56

これで問題のありそうな関数の番号を選択すると

#今回は#0のquicksort関数に問題がありそうだから0を選択
(gdb) frame 0

#0  0x0000000000400616 in quicksort (array=0x7ffe0656abc0, left_index=0, 
    right_index=8) at quicksort.c:26
26	        while(pivot < array[right])

というわけでこの26行目になにやら問題があることがわかった。
今回は

right++;

で配列の範囲外を参照してしまうことが問題になっていた。

というわけで、解析完了!


これがポインタなどの場合は以下の様に確かめることができる

(gdb)p [ここに確かめたい変数名、引数名など]

そうするとそれの値やメモリアドレスを指してくれる。
よくあるのは「0x0」というメモリアドレス。これはメモリが確保されていないことを指すので該当部分のポインタやアドレスまわりの処理を見なおそう。

コアダンプの原因

ここでさくっと、どうしてコアダンプが発生するかまとめておく。

  • メモリ違反
    • これが一番多いと思う
    • 配列の範囲外や未代入ポインタの参照など
  • 無限再帰や深すぎる再帰
    • 再帰から脱出できないときによくおきる

最後に

メモリ関連を特に見なおそう、配列やポインタなど。
gdbをうまく使えればコアダンプを瞬殺できるから使えると便利だけど、面倒くさいのでなるべくコアダンプが発生しないコードを書こう。

セキュリティミニキャンプ北海道2016に参加してきた

初めてミニキャンに参加してきた

LOCAL学生部に加入して、しばらくしてからミニキャンのお知らせが回ってきたから何をやるんだという感じで詳細を見てみると…。Linuxでハードウェア制御、スマホアプリでサーバサイドへの攻撃と対策、クラウドセキュリティ基礎…。これほど興味を引く内容なのに申し込まないわけにはいかない!
というわけで、申し込んで無事行くことになって、この前の週末に参加してきた。

ミニキャンに参加してみて

セキュリティあんまり興味なかったけど、セキュリティキャンプはセキュリティ以外にも色々実習を行ったり、チームに分かれてディスカッションしたりと面白かった。

印象に残った講義の感想

初日に行われた、「スマホアプリでサーバサイドアプリケーションへの攻撃と対策を学ぶ」はミニキャンプが開催された二日間の中で特に面白かった。
BurpSuiteという脆弱性診断によく使用されるソフトウェアを使用して、実際にHTTP通信を覗いたり編集したりして脆弱性を探して行ったわけだけど、チーム別に行ったのでチームのみんなで「ああだこうだ」言いながら色々試していった。別のチームの事情はよくわからないけど、自分たちのチームはセキュリティとかネットワークに強いひとがいなくてすごく苦戦した。だけど、そういう分野に強い人が居なかったが故にみんなで意見出し合いながら実習を行えたのはすごくいい経験になったし、今までセキュリティの仕事ってどんなことするのか曖昧だったものが経験を通して「これがセキュリティの仕事だ」といったものを実感できたのも良かった。


それ以外で面白かったのは「クラウドセキュリティ基礎」。
これはいろんなパワーワードが飛び交ってすごく笑えた。特に「今日のサーバはペットではなく家畜」と「耐用年数間近のサーバなんて電気代バカ食いするだけのゴミ」が最高にキテる。それにこの講義の内容はwebサービスやサーバ関連の内容からクラウドに移っていったので、最近VPSを借りてwebサービスを構築しようとしていた自分にとってはすごくためになることばかりだった。それに今までサーバとかクラウドといった環境が用意できないからという理由でweb系の事を全くやってこなかったので、本当に基礎的な部分から始まった講義は全てが新鮮だった。


その次は、なんといっても「Linuxを用いたハードウェア制御」の講義がためになった。
実はミニキャン前はこれをすごく楽しみにしていた。実際はすごくハードウェア的なことを中心にやっていくものだったけど未経験だった電子工作的な部分とシステムコール的な処理を同時に学べたのは最高に面白かった。出来ることならハードウェア制御という名前らしく、自分でハードウェアを制御するプログラムを作ってみたかった。

全体を通して

ミニキャンが開催された、二日間は最高の時間だったと振り返ってみて思う。それもそうで、普段大学では、一年生ということもあるのかこういった内容はみんな勉強しないで、たとえやったとしても「授業でやるから」「テストがあるから」といったような理由で自分がわくわくするようなことがまず無い。それに一応存在するプログラミングサークル的なものではUnityとか他のライブラリ使ってゲームを作るだけなどと、もっといろんな事を勉強して吸収していきたい自分にとってはすごく生温い環境の中にいたからである。ただ、ミニキャンは違った。ミニキャンは、みんなが何かしら自分が得られるものを全部自分のものにしてしまえ。といったような気迫ややる気と言ったものが感じられ、チームディスカッション一つとっても今までの自分にはなかった発想がたくさん展開され、演習も「そうするのか!?」といった驚きばかりだった。生温い環境に居た自分にはこういった時間がすごく刺激的だった。その中で、自分のスキルの無さを多少なりとも実感し、このままでは数年後に社会に出て、今回出会った人たちと競っていくことになったら自分は確実に劣るといった根拠の無い確証も得た。

今回、ミニキャンに参加しそういった実感があったからこそ、それをそのままにせず、自分はもっともっと成長しなければいけない、成長し続けなければいけないと思う。ミニキャンは消えそうだった自分のエンジニア魂を燃えさせるいいきっかけとなった。参加して本当に良かった。

出来ることなら来年のセキュリティキャンプの全国大会に参加したい。きっとそこにはもっと成長できる刺激的な環境が待っているだろうから。

後日談

キャンパーはその後紀伊国屋に集結してしまった。やはり似てるものはある。
あと、これからOS、カーネルの人になろうと思う。
「普段は何の人ですか?」と聞かれて答えれないのはあれだから。

さくっとアセンブリ入門 hello,world編

さくっとアセンブリ入門

なんとなくアセンブリをはじめてみる
レジスタがーとかメモリがーっていうお話は割愛

とりあえず「Hello,World」

まずはお決まりの「Hello,World」から

.globl main
main:
movl $4,%eax
movl $1,%ebx
movl $msg,%ecx
movl $13,%edx
int $0x80
movl $1,%eax
movl $0,%ebx
int $0x80
.data
msg: .asciz "Hello,World\n"

今まで色々なプログラミング言語に触れてきたけど、これほど初見で難解なhello,worldを見たことがない

わからないながら解説

まず一行目の「.globl」のように.から始まる命令をディレクティブといいアセンブラに対する命令を表している
その中でも「.globl」はエントリーポイントを指定するものである。またGASを使用するのでmainを指定しておく

二行目の「:」これがつくとC言語と同様にラベルになる
またラベルはプログラム実行中のメモリアドレスの代わりとして定数のように使用可能(最終行で使用)

三行目から六行目までは代入処理で「mov命令」と呼ばれるもの
この命令は以下の形式を取る

mov [代入元],[代入先]

詳しくは後述
$から始まるものは数値で%から始まるものはレジスタ

そしてこのmov命令を今回のコードの中では「mov」ではなく「movl」となっている
これは「long word」のlである。
アセンブリはただ数値としてすべてを扱うので何ビットずつ数値を扱っているかということを示す必要がある
これをオペレーションサフィックスといい以下のようなものが使える

サフィックス サイズ
b バイト、8ビット
s ショートで16ビット整数かシングルの32ビット浮動小数
w ワード、16ビット
l ロングで32ビット整数か64ビット浮動小数
q クワッド、64ビット
t 10バイト、80ビット浮動小数

これらから、Byte値を代入するなら「movb」となりWord値を代入するなら「movw」となる
今回はLongWordなので「movl」となる


七行目の謎の処理は「割り込み命令」といい、CPUが処理をしている時に現行作業を復帰可能な状態にして割り込みする処理を実行するといったもの
またここでは16進数で0x80を指定しているが、これは割り込み番号0x80番でシステムコールを実行してくださいということ

11行目でのdataディレクティブでは実行用のコードがおいてあるのではなく、初期化済みのデータを置くと宣言している

最後はascizディレクティブは文字列を置くと宣言するもので最後にnull文字を付与する

メディアンフィルタ

メディアンフィルタ

移動平均法ではn*nの局所領域における濃度の平均値を領域中央の画素が保有する濃度として扱ったが
別の画質改善法にメディアンフィルタというものがある
これは局所領域中の濃度値の中央値を領域中央の画素が保有する濃度として扱う方法である

例えば以下のような画素の一次元配列が存在すると仮定する
1,4,5,6,7,3,2,9,5
これを移動平均方では平均値を領域中央として扱ったが
メディアンフィルタでは
1,2,3,4,5,5,6,7,9
と並べ替えて中央値を領域中央の値とする

詳しい仕組み

メディアンフィルタでは、局所領域における画素の中央値を扱うため
その領域内の画素に対して非線形な演算を必要とするが、領域内の要素数を奇数にする
つまりnを奇数としてn*nの領域に対して処理を行うことで中央値を一意に求めることが可能になる

メディアンフィルタの使いところ

メディアンフィルタは雑音、つまりノイズが画像の内容と無関係でランダムな白色雑音の時に有効な手段としてつかえる
これはメディアンフィルタによる処理を行うことでスパイク状の雑音が取り除かれることとエッジがボケることが少ないためである

平均値を取る移動平均法のような手段では処理を重ねていくたびに周辺画素への影響が大きくなるが
メディアンフィルタのように中央値を選ぶことで極端な値の影響を非線形な演算が吸収しているためである

OpenCVPythonで実装してみる


いつもどおりPythonOpenCVメディアンフィルタをかけてみる

#coding:utf-8

import numpy as np
import cv2

#画像を読み込み
img = cv2.imread("lenna256.png")
#グレースケール化
gray = cv2.cvtColor(img,cv2.COLOR_RGB2GRAY)
#これは奇数にしておく必要がある
k = 3

res_img = cv2.medianBlur(gray,k)

cv2.imshow("median filter",res_img)
cv2.waitKey(0)
cv2.destroyAllWindows()


ここで使った画像はいつものlenna256.png
これをグレースケール化してlenna256_median.pngを生成する

lenna256.png
f:id:RabbitFoot141:20160403113730p:plain

そしてこれがメディアンフィルタを適用させた画像
lenna256_median.png
f:id:RabbitFoot141:20160403113807p:plain

他の画質改善手法に比べてスパイク雑音を取り除き、エッジに与える作用が小さいので
移動平均法などに比べて、連続した部分は滑らかになり
輪郭線もある程度はっきりしている

サイトをリニューアルしたから今回使ったものをまとめてみた

はじめに

今回自分のサイトをリニューアルしたので
kentsu-141.github.io
これに使ったものをざっくりまとめてみる

基本的なデザイン

まず基本的なデザインはこのホームページを見て思いついた
sard.website
ただ画像なんかを用意するのがめんどくさかったのと
なるべく最初の状態でスクロールする状況を作りたくなかったので

折りたたみ(後述)といつもどおりのカラーだけでゴリ押しのスタイルで作ると
今回のようなサイトになった

折りたためるあれ

今回使ったもので、表示部分を折りたためるあれは「アコーディオン」というもので
htmlタグのdl,dt,ddタグとjqueryを使って作った

参考にしたサイトはこれ
liginc.co.jp

これで表示部分を折りたためるようになった
しかしデフォルトでは折りたたまれず展開された状態になってしまうので

dd{
    display:none;
}

スタイルシートに追加しておいた
これでページ読み込み時で折りたたまれた状態になる

フォント

今回は今まで使ってこなかった方法でフォントを持ってきてみた

@import url(http://fonts.googleapis.com/css?family=Economica:400,700);

普段はダウンロードしたrobotoとかtahoma,impactあたりを使うんだけどこれもこれで良かった
こうして持ってきたフォントは以下のように使う(今回は全ページで統一なのでbodyタグに)

body{
    font-family:'Economica',sans-serif;
}

アニメーション

これが一番気になるだろうけど、特に独自実装したわけでなく
外部からwow.jsとanimate.cssを持ってきただけという

animate.cssは以下のサイトからいろいろ選べる
https://daneden.github.io/animate.css/

wow.jsはanimate.cssと併用するため必要
以下のレポジトリからダウンロードした
github.com

ただ注意すべきは、wow.jsをググればわかるが
ページがスクロールされ表示範囲に入った時にアニメーションに入ることや
サイズの大きい要素や、いくつも要素を同時に動かすと特にスマホなどで見た時にラグがでてかくかくになるので注意

最後に

割といつもフラットデザインになるようにしているが今回もなかなか微妙な出来になってしまった

C言語 構造体

6章 構造体

構造体とは、操作しやすいように一つの名前でまとめられた
1つ以上の異なった型の集まりである

この構造体と呼ばれるものは大きなプログラムで特に複雑なデータを組織化するのに役立つ
それは関連したデータを実体としてでなく一つの単位として扱えるという性質から来ている

6.1 構造体の基本事項

まずは構造体を使って二次元平面上の点を構造体を使って表してみる

int x,y;

普通はこう表すかもしれないけど、構造体でひとつの単位としてまとめるとこうなる

struct point{
    int x;
    int y;
};

これでpointという平面上での座標を表す一つの単位が出来た
前者に比べて後者のほうが何を示しているかわかりやすくなる

ここで少し説明するとstructという予約語の後に続いている名前は構造体タグと言われるものである
この構造体タグなるものは宣言以後、カッコ内の宣言部分の略称としてしようすることができる

構造体の中で命名された変数はメンバーと言われる
そして構造体タグとメンバー名は同名であってもぶつかることはない

構造体を使いたいときは以下のような記述をする

struct point pt;

これによりptはstruct point型であると宣言される

また以下のように各メンバーに対する定数式による初期化式の並びを定義の後につけることで構造体を初期化することができる

struct point max_pt = {100,100};

さらに宣言だけしておいて以下のように別々に初期化することも出来る

struct point min_pt;
min_pt.x = -100;
min_pt.y = -100;

上の例でわかるように

構造体型の変数名.メンバー名
>||
でその構造体のメンバにアクセスできるため、値を代入するときだけじゃなくて
値を呼び出したいときにも使用することが出来る


構造体は入れ子にすることも出来て
以下のようなメンバーを持つ構造体を宣言することも出来る
>|c|
struct rect{
     struct point pt1;
     struct point pt2;
};

この構造体は2つのpoint構造体をメンバとして含んでいる
ここでscreenを以下のように宣言しすると

struct rect screen

これでxが呼び出せる

screen.pt1.x

ここで紹介した以外にも構造体は以下のようにも宣言できる

struct{}x,y;

実は構造体タグは省略可能

6.2 構造体と関数

構造体に対して許される唯一の演算はコピーすること
&でそのアドレスを求めること

コピーと代入には関数に渡して関数から値を返すことも含まれている
しかし構造体の比較はできない

ここでは関数の値として構造体を返すことを例にあげてみる

struct point makePoint(int x,int y){
    struct point tmp;
    tmp.x = x;
    tmp.y = y;
    return tmp;
}

この関数はstruct point型の構造体を返すもので、前に書いたようにメンバー名と引数名が衝突していないことにも注目したい
ここで作成したmakePoint関数は任意の構造体を動的に初期化することができる

次に示すのは関数の引数と戻り値が構造体の場合のとき

struct point addPoint(struct point pt1,struct point pt2){
    pt1.x += pt2.x;
    pt1.y += pt2.y;
    return pt1;
}

ここは明示的な一時変数を使わないでpt1のメンバに加算しているが
これは構造体のパラメータが他のものと同じように値によって渡されることを強調するためである

これまでは構造体をそのまま渡してきたが関数に対して大きな構造体を渡す場合は構造体全体をコピーするより
ポインタを渡すほうが一般に能率的である
構造体ポインタは普通の変数へのポインタと変わらず以下のように宣言できる

struct point *pp;

これは変数ppがstruct point型へのポインタであることを示す
この時構造体ポインタのメンバにアクセスする場合は以下のようにする

(*pp).x;
(*pp).y;

この時注意すべきことは*は.より優先順位が低いため()が必要になる

これを用いて以下のようなコードが書ける

struct point base;
struct point *pointer;

pointer = &base;
printf("%d,%d",(*pointer).x,(*pointer).y);

また構造体のポインタへの参照はこれ以外にも別の方法がある
上の例に続いて、pointerが構造体へのポインタだとすると

pointer -> メンバー

のようにすることでメンバーを参照出来る

そこで2つ上の例は以下のように出力処理を書き直せる

printf("%d %d",pointer->x,pointer->y);

6.3 構造体配列

構造体自体は配列にすることができる
ここまでのまとめに準ずるなら

#define NUM 10
struct point pt[NUM];

もしくは

#define NUM 10
struct point{
     int x;
     int y;
}pt[NUM];

更にいうのであれば

struct point{
    int x;
    int y;
}pt[] = {
    1,1,
    2,1,...
};

のようにも定義出来る

この時構造体は配列になっているので、構造体のメンバーにアクセスしたい場合は

//iを0以上の任意の整数として
point[i].x
point[i].y

見たいな風にすれば、その構造体のインデックスに対応したメンバーを参照することが出来る

6.4 typedef

ここで少し話は逸れるがtypedefという新しいデータ型を宣言するための機能を紹介する
ただこの説明ではわからないと思うので実際に例を上げる

typedef int Length;

これでLengthをint型と同意義のものとして定義する

なので以下のような構文が書ける

Length len = 10;
Length maxLen = 20;

ということは文字列関連でいつもめんどくさいなと思っていたことを解消できる
それは文字列である。文字列はC言語においてポインタや配列で実現するのが通例になっているが
JavaC++,C#といった言語を先に経験した人にとってはこれは煩わしいものであったと思う
それをtypedefを使って以下のようにすれば

typedef char *String;

他の言語と同じように文字列を宣言できる

String str = "hello,world!!";

ちなみに同意義のものとして新しい型を既存の型と結びつけるので
typedefで宣言した新しい型でキャストすることもできる

例えば

String str;
str = (String)malloc(100);

のように

6.5 共用体

共用体(union)と言われるものは構造体とすごく似ているが、異なる型を同一のメモリで扱うことが出来る
というのも共用体のすべてのメンバにおいてオフセットが0である構造体のようなものだからである
共用体のメンバは構造体と同じように参照できる
ただ共用体自体の宣言が少し違う

union SampleUnion{
};

これでひと通りの構造体、それに類似するもののまとめは終わり