『フカシギの数え方』の数え方

狂気を感じる動画として一部で話題になった『フカシギの数え方』

僕も数えてみます!

数え上げ問題なので、まずは定石通りに、問題の状態を数値で表現してみて、それぞれの状態から何通りあるかと考える方針でいきます。

この状態を表現するのに、どのような情報が必要でしょうか。

以下の情報があれば、この状態からゴールまでの通り方の数は一意に決まりそうです。

  • 現在地: (2,2)
  • もう通れない点: (0,0), (1,0), (2,0), (2,1), (1,1), (1,2)

ここからの行き先ですが、左と上はもう通っている点なので行けません。
残りは2通りです。

それぞれ、同じように「現在地」と「もう通れない点」から状態を表現できます。

また、この状態だとどうなるでしょう。

すでにゴールに到達してます。
この状態からゴールまでの通り方の数はもちろん1通りです。

以上から、各状態からのゴールまでの通り方は再帰的に求められます。

  1. 現在地がゴールなら、1通り
  2. そうでなければ、上下左右それぞれに(進める場合は)進んだ状態からのゴールまでの通り方を足し合わせる。

具体的なコードはたとえばこんな感じです。
コード例では、「もう通れない点」の集合を楽に表すためにビット演算を使っています。
座標(0,1)と(0,3)をそれぞれ2進数で0001, 0100などと表すと、(0,1)と(0,3)の集合を0101として1つの整数値に押し込められます。

[cpp]
#include <iostream>

const int DX[] = {0, 0, -1, 1};
const int DY[] = {-1, 1, 0, 0};
int W, H;

// 座標(x, y)をビットで表す
long long bit(int x, int y) {
return 1LL << (y * W + x);
}

// 現在の座標と通過済みの座標集合から、パターン数を返す
long long dfs(int x, int y, long long visited) {
// 既にゴールにいるなら、そこからのパターンは1通り
if (x == W-1 && y == H-1) return 1;

// 現在地(x, y)を通過済みとしてマークする
visited |= bit(x, y);

long long res = 0;
// 4つの隣接点でまだ通過してない点があれば、そこに進んだ場合の
// パターン数を合計に加える
for (int i = 0; i < 4; i++) {
int next_x = x + DX[i];
int next_y = y + DY[i];
if (next_x >= 0 && next_x < W && next_y >= 0 && next_y < H) {
if (!(visited & bit(next_x, next_y))) {
res += dfs(next_x, next_y, visited);
}
}
}
return res;
}

int main() {
std::cout << “横は何分割? : “;
std::cin >> W; W++;
std::cout << “縦は何分割? : “;
std::cin >> H; H++;

long long ways = dfs(0, 0, 0LL);

std::cout << ways << ” だってよ!” << std::endl;
}
[/cpp]

結果はこんな感じ。6×6に2分半かかったので、7×7は数百時間にはなりそうです。

[bash]
$ clang++ -O3 main.cpp && time ./a.out
横は何分割? : 6
縦は何分割? : 6
575780564 だってよ!

real 2m37.630s
user 2m37.058s
sys 0m0.035s
[/bash]

4分岐で再帰の深さは最大W*Hなので、ざっくり計算量はO(4^(W*H))でしょうか。
最先端アルゴリズムだとちょっぱやらしいので、しばらく考えてみたんですが何も思いつきませんでした!
(それを探して来た人ごめんなさい ><)

続きを読む

Skyrimで英語の勉強

Skyrim、PC版をちょっとずつ日本語音声で進めてたんだけど、ちょっとでも英語学習の足しになればなーと英語音声に切り替えました。 ただ、 英語音声+英語字幕:   よく話についていけなくなる。あと疲れるので息抜きになら … 続きを読む

しあわせの必要条件

17歳のころに学ぶべきこと/無遅刻・無欠席を礼賛する風潮はどうかと思います の、 しあわせの必要条件は「自由」であることだ。 に思うところあったので感じてることをメモメモ。 こんな事を書いて会社を辞めた僕がこれに共感した … 続きを読む

1月のまとめ

東京帰ってきてから3週間くらい。やってたことをメモメモ。 勉強した本 1対1対応の演習/数学I・数学A やり方は、例題は5分で分からんかったらヒントだけ見てもっかい考える、演習題は20分考えて分かんなかったら答え見る、っ … 続きを読む

ソニーを退職しました。

(社内で出した退職メールを一部削り、よく聞かれた質問への答えを追加したものです)

本日2011/12/28を最終出社日として、ソニーを退職しました。

振り返ると本当に多くの人の支えで恵まれた生活を送ってきました。一方で、望む生き方のために必要と認識しながら未熟な能力を出来る限り短期間で補完するため、もう一度勉強に専念する時間が欲しいという思いも持ち続けていました。そんな中いくつかのタイミングが重なり、今回わがままを言って退職させていただきました。

ソニーでの生活

コクーンで興味を持ち、設立趣意書に惚れてソニーに入りました。入って最初の印象は「研究室みたいだな」という感じで、予想していた学生から社会人へのギャップをほとんど感じなかったのを覚えてます。とにかく最初から居心地が良かったです。

最初の年、自分はまだ戦力になっていませんでしたが、少数精鋭のチームが全力をあげて無茶な目標を達成する様子を内側から見ることができました。次の年からは、製品に乗るコードを書くことがどういうことかを学びました。不具合で市場問題が起こるとどんな影響があるのか、品質を保証する難しさと方法を学びました。長期間広く再利用されるモジュール開発を通じて、API設計の考え方と重要さを学びました。UIアプリ開発では実行パフォーマンスが持つ力を認識し、改善手法を学びました。自分が出したアイデアが製品に乗って無数に出荷され、フィードバックが来る楽しさを知りました。サービス設計ではオフラインアプリ設計との様々な考え方の違いを学びました。たびたび海外にも行かせていただいて、出不精な僕でも少し各国の環境・文化・考え方の違いに触れることができました。

どれもこれもかけがえの無い経験です。

今振り返ってみると本当にたくさんの方の助けでやりたいことをやらせてもらい続けていたと思います。エンジニアとして非常に恵まれた環境で長い時間を過ごすことができました。

なぜ辞めたか

辞める決意が固かったわりに、その理由は少し漠然としてます。

30才を過ぎた頃、結局僕は残された時間何をして生きるのかをようやくまじめに考えはじめました。そこでしっくりきたのが、「40代になっても50代になっても、幅広く技術を使いこなしながら、自分が面白そうって思ったものを作っては壊し作っては壊しできたらめちゃくちゃ楽しく生きられそうだなあ」というイメージでした。図画工作や美術ばかり常に全力だった過去も、自分が創作活動で強い幸福感を得られる性質であることを裏付けているように感じました。

だけど、「作るの好き」だけでは生計は立てられません。やりたいことだけははっきりしたので、どうやったらそれで生活できるか考えました。それで出た答えは単純で「もっと作る力を高めよう」でした。

理由の一つは、自信を持って創作に集中し、価値を生む確率を高めるためです。採用面接でも使ったレースゲームへのたとえ話をしてみます。走り方(ライン取り・車種選択等)が同じ相手に対しては、走りこめば自分も同じタイムを出せる自信がありました。だからこそ僕は上位の人の走りやタイムをあまり気にすることなく、速い走り方を試行錯誤して探すことだけに集中できて、結果的に独特な走り方で世界記録を出せたりしていました。そんな学生時代の成功体験を引きずっている僕は、まわりに振り回されず本質的な創作に集中するために自信が必要と考えています。同じアイデア・コンセプトで作れば自分が負ける、と思っている限りきっと僕は動けない気がしてます。

また、作るものの規模が少し大きくなるとどれだけ頑張っても一人では作れなくなります。それに、作る人だけいれば良い訳ではなく、お金や価値を生み出すには色々な役割の人が組まないといけません。大きな力を持った人達とチームを組みたいのであれば、自分も最大限の「作る力」を提供する必要があると考えました。

そんなわけで、とにかく今はソフトウェアを作る力を全力で伸ばすのが望む生き方への近道だと考えています。

仕事しながら勉強する選択肢もありましたし、そのつもりで1年半ほど前には技術の幅を拡げるべく違う領域へ異動させていただきました。特に最初半年は毎週末技術書を買って土日で読む感じでしたが、めちゃくちゃ楽しかったのを覚えています。仕事をしながらも多くを学べたし、仕事を通じてしか学べない事も多くありました。それでも、勉強したいことの量とこなせるペースのギャップには焦りを感じていて、いったん仕事を止めて時間や気力を100%勉強に全振りした場合にどこまでやれるか確かめたい気持ちは強くなる一方でした。そうして、このタイミングで実行することを決心しました。

甘い状況判断から危ない選択をしたのではないかという不安はあります。だけど、世の中はもっと厳しい「はず」で冷静になるのも嫌なので、外に出て、全力でやってみて、それでも力不足なら早めに失敗しよう、どの考えが甘かったか実経験から反省してまた頑張ろう、とも思っています。

勉強したいこと

基礎力としては、「柔軟で実行効率の高いコードを、早く正確に書ける」とか、「新しい技術を正確に理解し、正しく使用できる」みたいなイメージを目指していて、数学やアルゴリズムとデータ構造なんかに始まってコンピュータサイエンスの基礎を体系的に学び直したり、言語への理解を含めたり、知識をちゃんと運用できるように訓練したいと思います。初見の技術を正しく捉えるためにもしっかりとした基礎が重要だと、仕事を通じて学びました。

また、「今どんな技術があって、何ができるかを幅広く具体的に知ってる」ようにもなりたいです。僕は何かアイデアを探るときに「あれをあれに応用するとどうなるかな」みたいにシーズベースで考える傾向があります。その意味で、僕の発想の幅は知ってるシーズに強く依存しそうです。であればたくさん知ることで強化したい。それらの技術を具体的に知るには、使って何か作ってみるのが一番と思ってます。それを可能な限り幅広い技術に対してやってみたい。とにかく幅広く。多くの技術をちゃんと理解することは、発想の種になるだけでなく、実現したいことに対して数多くの技術から最も筋のいい方法を選ぶ力にも繋がると思ってます。

フロントエンド・バックエンド両方をしっかり作るため、UIやデザインに関しても基本を勉強したいと思っています。

これから

力はついたけどその後の生活苦しいというパターンでは、きっと後悔しないと思います。後悔するとしたら、力がつかなかったパターン。モチベーションが続かないのが一番怖いです。今回の決断で迷惑をかける多くの方にも全く申し開きできません。でも、2年間勉強したい気持ちは高まるばかりだったのを確認してからなので、ある程度の勝算はあるんじゃないかと思っています。

というわけで、来月からは無職になり、多分人生最後(?)の「勉強専念タイム」を過ごします。

今までいろんな人に育ててもらい、助けてもらいながら、100%自己中心的な理由で突然離脱するにも関わらず、考えをじっくり聞いてくれて応援して送り出してくれる上司や同僚達には感謝してもしきれません。この会社で皆さんと仕事ができたことは本当に幸運でした。

これからの勉強時間が、まだ20年以上続けるつもりのエンジニア人生をより楽しくすると信じて、また、何らかの形で恩返しに繋がると信じて、思いっきり頑張ろうと思います。

今までありがとうございました。
そして、これからもよろしくお願いいたします。

続きを読む