第1章の演習
演習の環境について
C++環境の準備にしたがって,手元でC++プログラムを作成,実行できる環境を準備する.
ソースのダウンロード
今後の演習では,ホームフォルダの下にmis2というフォルダを作って,
そこで作業することを前提として資料を作成している.macを使う場合は
mkdir ~/mis2
を実行する.
http://opendatastructures.orgのc++ edition, c++ sources ( C++ sources(講義独自版) ) からダウンロードしたファイルをホームフォルダの下のmis2に置く.ods.cpp.tgzというファイルができた時は,
cd ~/mis2
tar zxvf ods-cpp.tgz
ods-cpp.tarというファイルができた時は,
cd ~/mis2
tar xvf ods-cpp.tar
を実行するとcppというフォルダができる.
演習(C++プログラムの速度計測)
以下の関数fは整数(int)を要素にするvectorにn回要素を追加(push_back)してn回要素を削除する何もしない関数(何もしないが,コンパイラの最適化で削除されることはないように工夫して
いる)である.
void f(std::vector<int> &v, int n){
for (int i = 0; i < n; i++) v.push_back(i);
for (int i = 0; i < n; i++) v.pop_back();
}
さまざまな,nを与えた時の実行時間を測るためのプログラム
vector_speed.cppをコンパイル実行してみる.
これは配布プログラムに含まれている.最初の指示に従うとホームフォルダの下のmis2の下のcppというフォルダに含まれているはずである.
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <vector>
uint64_t cputime(){
unsigned int ax,dx;
asm volatile("rdtsc\nmovl %%eax,%0\nmovl %%edx,%1":"=g"(ax),"=g"(dx): :"eax","edx");
return (uint64_t(dx)<<32)+uint64_t(ax);
}
void f(std::vector<int> &v, int n){
for (int i = 0; i < n; i++) v.push_back(i);
for (int i = 0; i < n; i++) v.pop_back();
}
int main(int ac, char **ag){
int n = 1000;
if (ac > 1) n = atoi(ag[1]);
std::vector<int> v;
clock_t clk1 = clock();
uint64_t cpu1 = cputime();
f(v, n);
uint64_t cpu2 = cputime();
clock_t clk2 = clock();
std::cout << "cycle : " << cpu2 - cpu1 << std::endl;
std::cout << "clock : " << clk2 - clk1 << std::endl;
std::cout << "sec : " << (double)(clk2 - clk1)/CLOCKS_PER_SEC << std::endl;
}
g++ -O2 -o vector_speed vector_speed.cpp
を実行し,「コンパイル」する.「-O2」は最適化のオプションで,速度計測を目的とする場合は,こ
の程度の最適化レベルを用いる.コンパイルに成功したら,
./vector_speed 100000
を実行すると
cycle : 2296824
clock : 658
sec : 0.000658
のように,表示される.なお,プログラム中で,時間を計らなくても,
time ./vector_speed 100000
のようにしても,
real 0m0.011s
user 0m0.001s
sys 0m0.002s
のように表示されるが,計測の精度は落ちるし,プログラムの一部のみを計測することはできない.
プログラムの説明をする.
- atoi
「char *」型のCの文字列から,整数(int)に変換するC言語の関数.
- clock
OSの内部のclockの間隔(CLOCKS_PER_SEC, /usr/include/time.hに定義されている.MacOSのセンターに入っているバージョンでは1,000,000なので,1/1000000秒に1回増える)に基いたプロセス起動からの実行時間.OSによって計測の細かさが変わる.
- cputime()
アセンブリ言語命令 rdtsc(read time scamp counter) を用いて,CPU1サイクルにつき1カウントアップされるカウンタを読み出す.
自習問題
- 上のプログラムを自分の環境で実行してみて,fに与えるnの値をどう変化させるかによって,実行時間がどう変わるかを考えて見て下さい.
- 自分の環境で実行してみて,clockとcycleの関係から,CPUの速度が何GHzなのかを1秒あたりの実行サイクル数で推計してみてください.
- vectorに変えて,list, dequeを使うプログラムに書き換えてみて(プログラム中に出現する3つの"vector"を"list"あるいは"deque"に置き換える),同様に実行時間を計測してみてください.
- vector_speed.cpp を書き換えてしまうのは抵抗はあると思うので,
cp vector_speed.cpp list_speed.cpp
cp vector_speed.cpp deque_speed.cpp
などとコピーして,list_speed.cpp, deque_speed.cpp を好きなテキストエディタで編集したあとで(vector_speed.cpp をテキストエディタで開いて,別名で保存しても同じです),
g++ -O2 -o list_speed list_speed.cpp
./list_speed 100000
g++ -O2 -o deque_speed deque_speed.cpp
./deque_speed 100000
などと実行してみます.
[投票]Priority QueueがあればFIFO, LIFO を実装できるか?
- 両方実装できる
- FIFOは実装できるがLIFOは実装できない
- LIFOは実装できるがFIFOは実装できない
- 両方実装できない
[投票]queueの中でスタック(stack)と呼ばれるのは?
- FIFO (first-in-first-out) queue
- Priority queue
- LIFO (last-in-first-out) queue
[投票]次の中でUSet でなくSSet を使う必要があるのは?
- 0から263-1までの数の集合について,ある数が集合の要素に含まれるかどうかを調べる.
- 0から263-1までの数の集合について,最小の数を求める.
- 0から263-1までの数の集合について,要素数を求める.
[投票]以下のうち正しいのは?
- O(2n2) ⊆ O(n2)
- O(1.01n) ⊆ O(n100)
- O(n2.001) ⊆ O(n2 log n)
- O(n2 log2 n) ⊆ O(n2 log n)
[投票]次の関数 f(n)について最も適切なO-記法で表わせ
f(n) = 2 * n3 + 3 * n2 * log n
- O(n2)
- O(n2 log n)
- O(n3)
- O(n3 + n2 log n)
- O(n3 log n)
[投票]次の関数 f(n, m)について最も適切なO-記法で表わせ
f(n,m) = 2 * n2 log m + 2 * m + 3 * m log n
- O(n2)
- O(n2 log m)
- O(n2 log m + m)
- O(n2 log m + m + m log n)
- O(n2 log m + m log n)
[投票]確率変数X, Y に対してE[X * Y] = E[X] * E[Y] は成り立つ?
- 成り立つ
- 成り立たない
- 一般には成り立たないが X, Y が独立なら成り立つ
[投票]次の環境のうちw-bit RAMのw=64と仮定するのが適当なものを選べ(正解複数)
- iMac端末のWindows環境
- iMac端末のMac環境
- iPhone SE
- 任天堂Switch
- Apple Watch