Hatena::ブログ(Diary)

merom686の日記 このページをアンテナに追加 RSSフィード

2016-11-29

少しずれる

f:id:merom686:20161129152519p:image

上の図が何を表しているかというと、

  • 横軸
    • サンドイッチに入れるレタスの量
  • 縦軸
    • 明るい青 : レタスによるおいしさアップ
    • 赤 : レタスを買うのに必要なお金
    • 暗い青 : レタスによる総合的な利益(明るい青 - 赤)

ハムとかタマゴとかの大量生産するサンドイッチを想像してほしい。レタスが入っていないよりは少しでも入っていたほうがいいが、1つのサンドイッチにレタス1kgが入っていたら明らかにおいしくない。ちょうどいい量は、明るい青のグラフのてっぺんの横位置に現れている。

暗い青のグラフは、レタスにかかるお金も計算したときのもの。当然ながら、明るい青のグラフよりは下になっている。ここで注目してほしいのは、グラフのてっぺんの位置が左に移動していることだ。つまり、レタスの値段が高くなるとサンドイッチに入れる最適なレタスの量が減る。

このグラフはものすごく大雑把だけど、世の中のわりと多くのものがこういう原理で動いている。何かを足して(引いて)最適解の位置が少しずれるという感覚は役に立つ。

灼熱の卓球娘 #8,9

8話は音楽がよかった。このアニメは音楽がかっこいい。

9話は上矢さんの表情がよかった。このアニメは追い込まれたときの表情がいい。例えば、1話でムネムネ先輩が1点目を取られたとき。

あがりさんまじかっこよかった。

2016-11-26

デスクトップに置いたファイルたちを削除してしまった話

Firefoxの新規プロファイルで試したいことがあって、デフォルトのフォルダだと普段行かないような場所になるので、何か嫌で手近なデスクトップにした。今思えば、どうせすぐ消すのだからどこでも同じだったし、そもそもわざわざフォルダを変えたのは気分の問題だと思う。

firefox -pで新規プロファイルを作ると、デスクトップにファイルがばらまかれ「しまった、フォルダを作り忘れた」と思った。数分後、用が済んだのでプロファイルを消した。

ここで、ファイルも消去するか訊かれる。ばらまいてしまったので消すのが面倒だという思いがあった。自分が面倒だと感じていること自体はこの世界に何の影響も及ぼさないが、自分の(とっさの)判断には大きく影響を与える。ファイルも消去してもらうことにした。プロファイルの作成・削除は、何回も経験があって慣れていた。

デスクトップのアイコンがいくつか消えていく。0.5秒くらい考えて、Firefoxと関係ないファイルが消えている可能性のようなものが襲ってくる。タスクマネージャー(普段から起動してある)をクリックしてFirefoxを探す。ここで少し考えてから、Firefoxを終了させた。結果的には何も考えず終了させたほうがよかったが、焦って終了させて取り返しがつかなくなるケースもある。ここはいい判断だったと思う。

ごみ箱を確認したが、ファイルはない。油断したというわけではないが、酷いミスをしていくつかのファイルが消えた。やらかした直後は、「未知のファイルはごみ箱に入れるだろー」という気分だった(我ながらおかしいが、そういう気分じゃなかったらあのボタンを押さない)。後から考えれば、インストールしたプログラムじゃなくて自分で作ったプロファイルだから、狙って消せるとは期待できない。

とりあえずPCをスリープさせ、何が失われたかわからないまま夕食をとった。これからすることは、ファイルの復元。してはならないことは、PCのHDDに書き込みを行うこと(しかしPCを起動しながらデータを取り出す以上、多少は仕方ない(Windowsを起動せずに削除済みファイルを取り出せるようなスキルはない))。幸い、別のPCと外付けHDDがある。別のPCでいくつかのファイル復元ソフトを探してUSBメモリに入れ、メインPCに接続した外付けHDDへコピーし、外付けHDD上へ復元する。いくつか試した結果、Recuvaを使って比較的短時間で復元できた(ポータブル版もあったけど、そんなものを探す余裕はないので別PCにインストールしてそのファイルをコピー)。ここまで、普通にエクスプローラーとかを使って操作していたから、けっこうな数のファイルが壊れているが、おおむね問題ない。

Firefoxがファイルを消す順番は、名前順だったようだ。1119などの日付をフォルダ名にしたものがまず消された。ここにはダウンロードしたファイルが1か月分くらい入っている。壊れていてもダウンロードし直せるものが多く、多少消えてもダメージは小さい。HDD機であり、無駄に多くのファイル(Visual Studio Image Libraryとか)を保存していたため時間が稼げて、他のフォルダへの被害は少なかった。そもそも大事なものはバックアップを取ってある、と言いたいところだがそうでもなかった。自分が書いた文章やソースコードなど、本当に大事なものはDropboxに入れてあるが、これも操作ミスで消してしまう可能性があると思わされた。

それにしても酷いミスだった。自分以外の人がこういうやらかしをしたと聞いたら、「俺ならさすがにそんなミスはしない」と思ったのではないか(今となってはどう思ったかわからないけど)。とにかく、こういう想定外のミスは滅多にないことで血の気が引いた。やるときは魅入られたようにそちらへ歩いてしまうものだ。

2016-11-01

秋アニメ第1話

もう第1話という時代ではないが。いやさ、試したい作品の第1話を試すまで待ってたら1か月経っちゃったのよ。しかもまだ試してないアニメけっこうあるし。

例によってよかった順。本命は卓球娘。アーススター枠が安定して楽しめる。

灼熱の卓球娘

1〜3話をまとめて見た。これはいい。タイトルがぴったり。キャラデザが好み。全体的にかわいい。このブルマは好き。顔も服も体つきもいい。OP曲のちょっとひねってるところが好き。

キービジュアルで赤い子の見た目は知っていたけど、内容的にはあがりちゃんが主人公に見える。こよりが主人公でないという意味ではなくて、のび太(あがり)とドラえもん(こより)のような関係。

てーきゅう 8期

最初から絶好調。ただ、アニメがブランクを感じさせない出来でも、見てるこっちにはブランクがある。その意味でこれから。

魔法少女なんてもういいですから。セカンドシーズン

嬉しい2期。短いけど、作画も内容もいい。初回OPの前にOP曲のCMを流すのはやめろ。

3月のライオン

原作を知っているからか、声がわからない。茅野愛衣はいいとして、花澤香菜の意図が見えない。久野美咲は好きだけど、それはモモなのか。慣れるのが楽しみなところ。

キャラデザはどうなんだろ。原作の絵は、自分は好きだけど、好みが分かれそう。アニメの絵がどうなのか。アニメとしてはいい感じなのかな。

将棋描写は、アニメになると違和感が出る。対局中あんなにしゃべらねーよと。漫画だと、心の声も含めデフォルメして表現していると受け取れる。まあその辺りも慣れかな。

舟を編む

飯間浩明(@IIMA_Hiroaki)さん | Twitter
ツイートを見て試してみた。さすがノイタミナという感じで、言葉に関する描写がしっかりしている。本を作るのも仕事でやってるんで糞みたいな描写も入る。自然ではあるが嫌いなのでなかなか見るハードルが高い。いいアニメだとは思う。

WWW.WORKING!!

初回だけしか見てないけど、けっこう面白そう。キャラの見た目に個性がなくてつらいと感じるが、見続ければその印象も変わるだろうか。

あにトレ!!XX

EXの次のXXは読みがペケペケ。EXの内容が思い出せない。こんなだったっけ。そういう方向性ではあったけど、こんなだったっけ。かわいきゃそれでいいんだけど、設定が不自然だったりあざとすぎたりするとかわいくなくなる。

バーナード嬢曰く。

こんな内容だったのか(元のイメージを思い出せない)。キービジュアルは明らかにダメだが、そこだけで判断してはいけないことも知っている。実際に動く絵を見てみると、SD画は悪くない感じで、あとは作画がよければというところ。作画はけっこう期待しているが、内容がやや不安。

ステラのまほう

原作の人の絵はやや苦手な面もあるが、アニメになってとっつきやすくなった。内容もけっこうよさそう。

にゃんぼー!

声が異様に豪華。内容も悪くない。EDでよつばが動くのが衝撃だった。それでいて、EDとして正統派の内容でもある。

信長の忍び

かわいいからありなんじゃないかと。

SHOW BY ROCK!!#

かわいいけどなぜかそりが合わない。

その他

ブレイブウィッチーズ魔法少女育成計画といったあたりを試したい。

継続

ここたまは5クール目に入ってから好調。NARUTOは2週ためてるけどいいイメージを持っている。

2016-10-20

倍精度の更に倍の精度でマンデルブロ集合

MULXを使うと、多倍長乗算を書くのがかなり楽になる(もちろん速くもなる)。今回は、64bitの汎用レジスタ2個で128bitの符号付き固定小数点数を表し、その加減算と乗算をXbyakで実装した(これは多倍長なのか?)。

これまでは速さを求めていたけど、速いとどんどん拡大させてしまい、すぐ計算精度の限界に達してしまう。そこで、ずっとやりたかった精度アップだ(64bitのCPUOS、それにできればMULXが欲しかった)。精度を大幅に上げると計算時間がかかりすぎるので、最低限の桁数にした。精度の桁数が2倍になった代わりに、前回の20倍くらい遅くなった。

Xbyakを使っているといっても、実行時にコードを生成している意味はほとんどなくて、単に高級アセンブラとして使った感じ。いつものマンデルブロ集合用コードより複雑なので、速度はあまり追求せず(ていうかできず)気持ちよく書けた。

Fixed128は128bitの符号付き固定小数点数を表す構造体。符号を別に持つ方法もあったが、16byteに収まったほうが楽だと思い、単に符号付き整数として格納している。乗算では、負数だったら符号反転して非負整数同士の掛け算にしている。速度的にどちらが有利なのかは知らん。

加算は小数点の位置を気にすることなく普通にキャリー付き加算をすればいいから簡単(符号を別に持つとここがややこしくなる)。減算も同様。符号反転は、ビット否定してから1を足せばよい。

どこを描画するかの変数も、doubleではなくFixed128で保持する必要があるので、C++から簡単に加減乗算が使えるようにしている。除算で精度が必要になることはないので、割り算をしたければ逆数をdoubleからキャストして掛ければよい。拡大率はdoubleでいい(ここは浮動小数点の強みが出ている)。

役立つ命令が用意されていて、しかも最近は所要クロック数も減ってきてるのでありがたい。MULXはフラグを変更しない乗算。結果を出力するレジスタも選べるようになっている。SHRDは、まさに固定小数点のためにあるような命令で、これがまた速い(instlatx64で見ただけで自分で測ったわけではないが)。

慣れないことするからバグ取りもけっこう大変だった。rbpを保存してるつもりで使ってたとか、絶対値の大きい値は扱えないのにピクセル数をFixed128にキャストしてたり。しかし低級(アセンブラ)なことを高級(C++)に書けるのは気持ちがいい。

コンパイルするときは、xbyak.hの他にxbyak_mnemonic.h(とxbyak_bin2hex.h?)が必要。ヘッダだけで使えてこんな快適に書けるのはすごい。

#include "xbyak.h"

struct Fixed128 {
    static constexpr int P = 7; // 固定小数点の位置

    using op = void (*)(Fixed128 *, const Fixed128 *, const Fixed128 *);
    static op add, mul; // 速度は不要だがどうせ下で書くので流用する

    uint64_t u[2];

    Fixed128& operator+=(Fixed128& t) {
        return *this = *this + t;
    }
    Fixed128& operator*=(Fixed128& t) {
        return *this = *this * t;
    }
    Fixed128 operator+(Fixed128& t) const {
        Fixed128 ret;
        add(&ret, this, &t);
        return ret;
    }
    Fixed128 operator*(Fixed128& t) const {
        Fixed128 ret;
        mul(&ret, this, &t);
        return ret;
    }

    Fixed128 operator-(Fixed128& t) const {
        return *this + (-t);
    }
    Fixed128 operator-() const {
        Fixed128 t;
        t.u[0] = ~u[0];
        t.u[1] = ~u[1];
        if (++t.u[0] == 0) t.u[1]++;
        return t;
    }

    explicit operator double() const {
        const bool p = (u[1] & 1ULL << 63) == 0;
        const Fixed128 t = p ? *this : -*this;
        return (t.u[0] * std::pow(0.5, 128 - P) + t.u[1] * std::pow(0.5, 64 - P)) * (p ? 1 : -1);
    }
    explicit Fixed128(const double s) {
        double i, d;
        d = std::modf(std::fabs(s) * std::pow(2.0, 64 - P), &i);
        u[0] = (uint64_t)(d * std::pow(2.0, 64));
        u[1] = (uint64_t)i;
        if (s < 0) *this = -*this;
    }
    Fixed128() {}
};

class Mandel128 : public Xbyak::CodeGenerator {
    const Xbyak::Reg64& r(int i, int h) const {
        const Xbyak::Reg64 *const reg[] = { /*&rax, &rcx, &rdx, &rbx, &rsp,*/ &rbp, &rsi, &rdi,/* &r8, &r9, &r10,*/ &r11, &r12, &r13, &r14, &r15 };
        return *reg[i * 2 + h];
    }
    void push_8() {
        push(rbp);
        push(rbx);
        push(rsi);
        push(rdi);
        push(r12);
        push(r13);
        push(r14);
        push(r15);
    }
    void pop_8() {
        pop(r15);
        pop(r14);
        pop(r13);
        pop(r12);
        pop(rdi);
        pop(rsi);
        pop(rbx);
        pop(rbp);
    }
    void f_mov(int i, int j) {
        mov(r(i, 0), r(j, 0));
        mov(r(i, 1), r(j, 1));
    }
    void f_mov(int i, Xbyak::RegExp e) {
        mov(r(i, 0), ptr[e]);
        mov(r(i, 1), ptr[e + 8]);
    }
    void f_mov(Xbyak::RegExp e, int i) {
        mov(ptr[e], r(i, 0));
        mov(ptr[e + 8], r(i, 1));
    }
    void f_add(int i, int j) {
        add(r(i, 0), r(j, 0));
        adc(r(i, 1), r(j, 1));
    }
    void f_add(int i, Xbyak::RegExp e) {
        add(r(i, 0), ptr[e]);
        adc(r(i, 1), ptr[e + 8]);
    }
    void f_sub(int i, int j) {
        sub(r(i, 0), r(j, 0));
        sbb(r(i, 1), r(j, 1));
    }
    void f_neg(int i) {
        not(r(i, 0));
        not(r(i, 1));
        add(r(i, 0), 1);
        adc(r(i, 1), 0);
    }
    void f_mul(int i, int j) {
        xor(r10, r10);

        if (i == j) {
            mov(r8, r(i, 1));
            add(r8, r8);
            jnc("@f");
            f_neg(i);
            L("@@");

            mov(rdx, r(i, 0)); // free r(i, 0)
            mulx(r(i, 0), r(i, 0), r(i, 0));
            mulx(r9, r8, r(i, 1));
            add(r8, r8);
            adc(r9, r9);
            adc(r10, r10);
            add(r(i, 0), r8);

            mov(rdx, r(i, 1)); // free r(i, 1)
            mulx(r8, r(i, 1), r(i, 1));
            adc(r(i, 1), r9);
            adc(r8, r10);

            shrd(r(i, 0), r(i, 1), 64 - Fixed128::P);
            shrd(r(i, 1),      r8, 64 - Fixed128::P);

        } else {
            mov(r8, r(i, 1));
            add(r8, r8);
            jnc("@f");
            f_neg(i);
            xor(r10, 1);
            L("@@");

            mov(r8, r(j, 1));
            add(r8, r8);
            jnc("@f");
            f_neg(j);
            xor(r10, 1);
            L("@@");

            mov(rdx, r(j, 0)); // free r(j, 0)
            mulx(r(j, 0), r(j, 0), r(i, 0));
            mulx(r9, r8, r(i, 1));
            add(r8, r(j, 0));

            mov(rdx, r(j, 1)); // free r(j, 1)
            mulx(r(j, 1), r(j, 0), r(i, 1)); // free r(i, 1)
            adc(r9, r(j, 0));
            adc(r(j, 1), 0);

            mulx(r(i, 1), r(i, 0), r(i, 0)); // free r(i, 0)
            add(r(i, 0), r8);
            adc(r(i, 1), r9);
            adc(r(j, 1), 0);

            shrd(r(i, 0), r(i, 1), 64 - Fixed128::P);
            shrd(r(i, 1), r(j, 1), 64 - Fixed128::P);

            test(r10, r10);
            jz("@f");
            f_neg(i);
            L("@@");
        }
    }

public:
    Mandel128(int m) {
        push_8();

        f_mov(0, rcx);
        f_mov(1, rcx + 16);
        mov(ebx, m);
        mov(rax, 4);
        shl(rax, 64 - Fixed128::P);

        L("loop");   // a b ? ?
        f_mov(2, 0); // a b a ?
        f_mul(0, 0); // aa b a ?
        f_mov(3, 1); // aa b a b
        f_mul(3, 3); // aa b a bb
        f_mul(1, 2); // aa ab ? bb
        f_mov(2, 0); // aa ab aa bb
        f_add(2, 3); // aa ab aa+bb bb

        cmp(r(2, 1), rax);
        jge("exit");

        f_sub(0, 3);        // aa-bb ab ? ?
        f_add(1, 1);        // aa-bb 2ab ? ?
        f_add(0, rcx);      // aa-bb+x 2ab ? ?
        f_add(1, rcx + 16); // aa-bb+x 2ab+y ? ?

        dec(ebx);
        jnz("loop");

        L("exit");
        mov(eax, m);
        sub(eax, ebx); // 戻り値(反復回数)

        pop_8();
        ret();
    }
    void init_Fixed128() {
        // 引数はrcx, rdx, r8に入っている
        Fixed128::add = (Fixed128::op)getCurr();
        push_8();

        f_mov(0, rdx);
        f_add(0, r8);
        f_mov(rcx, 0);

        pop_8();
        ret();

        Fixed128::mul = (Fixed128::op)getCurr();
        push_8();

        f_mov(0, rdx);
        f_mov(1, r8);
        f_mul(0, 1); // 最初f_addって書いてました すません
        f_mov(rcx, 0);

        pop_8();
        ret();
    }
};

Fixed128::op Fixed128::add, Fixed128::mul;

// // こんな感じで初期化する
// ma = new Mandel128(m);
// f = (int (*)(Fixed128 *))ma->getCode();
// ma->init_Fixed128();

2016-10-19

CGPには負けたくないと言った件

電王トーナメントの予選でニコ生に出たとき、「このソフトには負けたくないみたいなのないですか」とか聞かれて(記憶で書いてるので違う言い方だったらすみません)、「(対戦中の)このCGPには負けたくないですね」と答えた。

あとでNoviceさんとその辺りの話になって、なぜCGPなのか、おから饅頭は違うのか、みたいなことを聞かれた。そのときは「同世代で実力の近い人がいないんで…」とかよくわからないことを言った。

事実だけ言えば、同世代なのはおから饅頭だ(WCSC25が初参加)。ソフトの棋力は、WCSC25のときはおから饅頭が上、SDT3のときはshogi686が上、WCSC26のときはおから饅頭が圧倒的に上、今回(SDT4)はおから饅頭が少し上、という感じだろうか(正確なところはわからない)。

おから饅頭の開発者が予選に来てなかったこともあるけど、それよりもWCSC26のときの力の差の印象が大きかったのだろう。負けたくない相手と言われて、おから饅頭のことは全く浮かばなかった。

CGPは、SDT3が初参加。WCSC26では、shogi686の学習がダメダメだったため順当に負けたが、ダメなりに受けの力を見せてけっこう見られる勝負だった。SDT4ではshogi686が力を発揮して勝利

今回は、shogi686がライブラリで大量の教師局面を使っている点と、CGPが高速化に重きを置いている点で、shogi686に分がある戦いだと思っていた。この好条件で負けるわけにはいかない。いや、そこまで自分がダメだと思いたくない。それで、CGPには負けたくないと言ったのだと思う。

さて。この1年くらい、僕はコンピュータ将棋に対して全然やる気がなかった。大会直前になると、感情が無い感じでずっと開発していた。んで、「誰々には負けたくない」と発言することは、その相手を「ライバル視している」みたいな意味を少なからず含むと思われる。こんな状態で、誰に対してもそんなこと言えないわけ。まあ上に書いたような理由があるのでニコ生ではCGPさんの名前を出したわけだけど。CGPさんとはCPUの話ができるのでよい。

で、Noviceさんに対してちょっとモゴモゴした答え方になったはまた別の理由もあって。おから饅頭やCGPとは、成績は近いものの、ガチ勢っぽくて気後れする部分がある。いやそうじゃないな。あのときの僕は、寂しかったのだ。予選が終わったばかりで不安定だったのだろうけど。

開発者のタイプは色々だ。shogi686が棋力では圧倒しても、プログラマとしてはあちらのほうがずっと優れている、というのは普通のことだ。そんな中でそういう気分になったのかね。状況は他の人と変わらんのかもしれないけど。

つかニコ生と言えば、阿部光瑠氏とアニメの話したかったわ(たぶん見てるアニメは一つも被ってないけど)。