らんたくいっくそーと

数学ガール4巻を読んでいたら乱択クイックソートが出てきたのでPythonで書いてみた。本には疑似コードまで載せてあって読みやすい。


・普通のクイックソート

・乱択クイックソート

main()内でソートするリストの要素をrandint()で100万生成してそれをクイックソートする。一応最後にPythonに元からあるリストのソートと同じ結果になっているかの確認をする。比較のためにseed値を固定しておく。

実行結果がこちら。

・ふつうの

start
time: 9.663000
True

・乱択

start
time: 11.829000
True

乱択クイックソートの方が少し遅い。何回か繰り返してみたけど大体同じ感じだった。

次にソート済みのリストに対してソートをかけてみる。

c = range(9000)
c = range(9000, 0, -1)

として実行してみた結果がこちら。

ソース クイックソート 乱択クイックソート
c = range(9000) 6.739000 0.071000
c = range(9000, 0, -1) 13.412000 0.074000
最初にやったやつ 9.663000 11.829000

だいたいこんな感じになった。
ソート済みのリストに対しては乱択クイックソートの方が圧倒的に速いことがわかる。
ソートする対象が、すでにソートされている部分があるかどうかわからないときは乱択クイックソートを使うのも良いかもと思った。

Fortranでの配列の要素へのアクセス順序について

Fortranの配列では列優先でメモリに格納されるそうだ。

Fortranの多次元配列は列優先 (Column Major) です。(C/C++言語では行優先)例えば3行4列の2次元整数配列は integer a(3,4) のように宣言され、メモリ上には以下の順番で数値が格納されます。

a(1,1)
a(2,1)
a(3,1)
a(1,2)
a(2,2)
a(3,2)
a(1,3)
a(2,3)
a(3,3)
a(1,4)
a(2,4)
a(3,4)
Fortran 入門: 配列

ということで以下のプログラムを使って実際に比べてみた。使用したマシンはちょっと昔のiMac

program ArrayTest
    implicit none
    integer :: a(1000, 1000) = 0
    integer :: n = 1000
    real :: s = 0
    integer i, j, k
    do k = 1, 5000
        do j = 1, n
            do i = 1, n
                a(i, j) = j - i
                s =  a(i, j)
            end do
        end do
    end do
    print *, s

end program ArrayTest
program ArrayTest2
    implicit none
    integer :: a(1000, 1000) = 0
    integer :: n = 1000
    real :: s = 0
    integer i, j, k
    do k = 1, 5000
        do i = 1, n
            do j = 1, n
                a(i, j) = j - i
                s =  a(i, j)
            end do
        end do
    end do
    print *, s

end program ArrayTest2

2つとも1000×1000の2次元配列の端から端までアクセスするのを5000回繰り返すだけのプログラム。上のほうは列優先でアクセスし、下のほうは行優先でアクセスする。最後のprintは特に意味はない。ループが終わったことの目印になればいいかと思ったけど別にいらないような…

timeコマンドで時間を測った結果がこちら。

・列優先のほう

 0.

real    0m30.697s
user    0m30.297s
sys     0m0.070s

・行優先のほう

 0.

real    0m42.868s
user    0m42.522s
sys     0m0.145s

なるほど確かに列優先だ。


じゃあ3次元配列も同じような感じなのかしら?ってことで調べてみた。

program ArrayTest3D
    implicit none
    integer :: a(100, 100, 100) = 0
    integer :: n = 100
    real :: s = 0
    integer i, j, k, iter
    do iter = 1, 2000
        do k = 1, n
            do j = 1, n
                do i = 1, n
                    a(i, j, k) = k - j - i
                    s =  a(i, j, k)
                end do
            end do
        end do
    end do
    print *, s

end program ArrayTest3D
program ArrayTest3D2
    implicit none
    integer :: a(100, 100, 100) = 0
    integer :: n = 100
    real :: s = 0
    integer i, j, k, iter
    do iter = 1, 2000
        do i = 1, n
            do j = 1, n
                do k = 1, n
                    a(i, j, k) = k - j - i
                    s =  a(i, j, k)
                end do
            end do
        end do
    end do
    print *, s

end program ArrayTest3D2

上のほうが列優先で、下のほうが行優先。i,j,kの組み合わせはほかにもあるけどとりあえずこの2つでやってみた。

結果はこちら。

・列優先のほう

 -100.

real    0m30.861s
user    0m30.845s
sys     0m0.012s

・行優先のほう

 -100.

real    0m34.517s
user    0m34.498s
sys     0m0.012s

2次元配列の時ほどではないけど、上のほうが速いことがわかる。
上のほうでのアクセス順序がi,j,kの組み合わせの中で一番速いのかな?

ということで、Fortranで配列をループで扱うときはアクセスする順番に気を付けましょうというお話でした…

お手製timeコマンドをつくってみたい

Windowsでもtimeコマンド使いたいなーなんてときがしばしばあるので試しに作ってみようってことで書いてみた。

Windowsだと別の役割を持つtimeコマンドがすでにあるのでtimeitという名前に。
WindowsAPIのCreateProcess関数とGetProcessTimes関数を用いた。
FILETIME構造体は、1601年1月1日から 100 ナノ秒間隔の数を表す二つの 32 ビットメンバを持つ 64 ビットの値で、

typedef struct _FILETIME {
	DWORD dwLowDateTime;
	DWORD dwHighDateTime;
} FILETIME; 

となっているので、ポインタのキャストで無理やりlong long int にして扱った。

ただ、これはexeファイルしかuserとkernelの時間が測れないという問題がある。内部コマンドやバッチファイルは受け付けてくれない。また、

cmd /c

を先頭に入れると内部コマンドやバッチファイルを走らせることができるが、これだと新しく開いたプロンプトの時間を監視することになるので正しい時間が測れない。

どうしたらいいんでしょう……

参考にしたところ

等々

VimでF5を押してコンパイル&実行する

Vimで編集中のファイルをコンパイルして実行する方法を紹介しているサイト(参考にしたところ1)を見つけて、「これは便利だ!」ってことで真似してみることにした。
参考にしたところに載せたWebサイトを読みながら以下をvimrcに追加した。

とりあえずC、FortranPythonの3つで動くようにしてみた。C、Fortranではコンパイルと実行、Pythonではそのまま実行する。
windowsだと.exe、それ以外だと.outを生成する。
F5キーのみでやりたかったのでRunコマンドを用意してファイルの拡張子で場合分けした。

callとかの後ろにある「s:」が何なのかよくわからないです・・・

数字→ローマ数字変換

なんとなく数字をローマ数字に変換するスクリプトを書いてみた。1から9999までの数字を変換することができる。数字を受け取ったら1の位から順にチェックしていくってだけのもの。

実行例がこちら。

>>> 
153
CLIII
>>> 
48531
Error: input 1 ~ 9999
3587
MMMDLXXXVII

んー・・・もっと良い方法があるはず。

vim-indent-guidesを入れてみた

ある日vimテクニックバイブルをちらりと読んでみたらvim-indent-guidesというものを見つけた。Python使うしインデントが見えると助かるよなってことで導入することにした。ファイルは次のサイトでダウンロードできる。

GitHub - nathanaelkane/vim-indent-guides: A Vim plugin for visually displaying indent levels in code

zipを展開して出てきたautoload,doc,pluginをそれぞれvimのruntimeディレクトリにぶち込めばOKみたい。

で、vimrcに次の2行を追加した。

let g:indent_guides_enable_on_vim_startup=1
let g:indent_guides_guide_size=4

さてうまくいくかなとgvimを起動してみたら上手くインデントが色づけされててよかったのだが、コンソール版のvimを起動してみたら次のエラーが出た。

function <SNR>14_IndentGuidesEnable..indent_guides#enable..indent_guides#init_sc
ript_vars..indent_guides#capture_highlight の処理中にエラーが検出されました:
行    2:
E411: ハイライトグループがみつかりません: Normal

・・・ハイライトグループが見つからないってことは何か色の設定が見つからないってことなのかな・・・?なんて思いながら配布サイトの説明を読んでたら、colorscheme設定しとけ的なこと書いてあったので設定してみたら(というか今まで設定していなかったのかよ・・・)エラーが出なくなった。めでたしめでたし。

細かい設定についてはdocの中にあるテキストファイルに書いてあるっぽいから暇な時に眺めてみよう。

Processingを触ってみた

最近、Processingというものを知った。詳しくは以下のサイトで。ググるといろんなサイトが出てくる。
Processing.org
ポケットサーバー 格安レンタルサーバー

OpenProcessingというサイトに行くといろんな作品を見ることができて楽しい。ソースコードも一緒に公開されているので勉強にもなるかもしれない。
OpenProcessing - Creative Coding for the Curious Mind

今回はためしにグモウスキー・ミラの写像をパラメータをどんどん変えていってアニメーションさせてみた。グモウスキー・ミラの写像って何?って人はググってください。
コードはこちら。とりあえずハイライトはjavaにしておいた。

float a = 0.0;
float b = 0.06;
boolean f = true;

void setup(){
  size(400, 400);
  frameRate(10);
  background(255);
}

void draw(){
  translate(width/2,height/2);
  background(255);
  float x = 0.0;
  float y = 0.0;
  float newx = 0.0;
  float newy = 0.0;
  int i = 0;
  for(i = 0;i < 30000; i++){
    newx = y + a*(1 - 0.05*y*y) + b*x + 2*(1-b)*x*x/(1+x*x);
    newy = -x + b*newx + 2*(1-b)*newx*newx/(1+newx*newx);
    x = newx;
    y = newy;
    point(x*20,-y*20);
  }
  a += 0.005;
}

void mouseClicked(){
  if (f == true){
    noLoop();
    f = false;
  }
  else{
    loop();
    f = true;
  }
}

再生してみるとこんな感じになった。

元の動画は1分ほどの長さなのになぜか再生時間が2分ほどになってる…
動画の後半は何なんだろう…
もちろん、元々1分しかないんだから後半は動くわけがない…