2014年10月28日火曜日

エンドゲーム血族表

『エンドゲーム・コーリング』の血族名、プレーヤー名の英語表記および日本語表記の一覧表です。


Lines / 血族
Player / プレイヤー

Aksumite
アクスマイト
Hilal Ibn Isa alSalt
ヒラル・イブン・イサ・アル=サルト
第144血族
Cahokian
カホキアン
Sarah Alopay
セアラ・アロペイ
第233血族
Donghu
ドンフ
Baitsakhan
バイツァカーン
第13血族
Harappan
ハラッパン
Shari Chopra
シャリ・チョプラ
第55血族
Koori
クーリ
Alice Ulapala
アリス・ウラパラ
第34血族
La Tene
ケルト
Aisling Kopp
アイシュリン・コプ
第3血族
Minoan
ミノーン
Marcus Loxias Megalos
マーカス・ロクシアス・メガロス
第5血族
Mu
ムー
Chiyoko Takeda
竹田千代子
第2血族
Nabatean
ナバテアン
Maccabee Adlai
マカビー・アドライ
第8血族
Olmec
オルメク
Jago Tlaloc
ヤゴ・トラロク
第21血族
Shang
シャン
An Liu
アン・リュー
第377血族
Sumerian
シュメーリアン
Kala Mozami
カラ・モザミ
第89血族

血族についてのより詳しい説明は
http://www.ancientsocieties.com/pages/the-12-ancient-lines/
からどうぞ。

2014年7月19日土曜日

USにおけるReserveの用語法

アメリカのアクチュアリーフォーラムActuarial Outpostの1スレッドですが、参考になりました。

Loss reserve, claims reserve & policy reserve?
http://www.actuarialoutpost.com/actuarial_discussion_forum/showthread.php?t=279448


Loss reserve
Claim reserve
Benefit reserve
Policy reserve
IBNR
Unearned premium reserve
Active life reserve (ALR)
Disabled life reserve (DLR)
の用語についてQAが行われており、USにおけるこれらの用語の使い分けを見ることができます。

こちらでの回答によると、

Loss reserve = Claim reserve : 支払備金
Benefit reserve : FASで定められた、GAAPにおける責任準備金の一つ
Policy reserve : より一般的な意味での責任準備金
IBNR : 既発生未報告の保険金
Unearned premium reserve : 保険料の未経過部分として計算される、責任準備金の一つ
Active life reserve : 保障中の契約に対する責任準備金
Disabled life reserve : 就業不能等で給付状態にある契約への責任準備金

という感じでしょうか。

2014年3月6日木曜日

勉強用Google Apps Script (Quiz Script)

Googleドライブのスプレッドシートに問題と答のリストを作成しておくと、ランダムで問題を出題してくれるスクリプトを作成しました。

アクチュアリー試験の勉強用に作成しましたが、つまりはクイズを出してくれるスクリプトです。


2014年1月15日水曜日

2014年1月5日日曜日

Nexus5でカメラのフラッシュを懐中電灯として使う

Androidで、カメラ用のLEDフラッシュライトを懐中電灯として使う。

よくある機能でアプリもたくさん公開されていますが、なぜかインターネット接続のパーミッションを要求してくるものなどが多く、どれが安全なアプリかよく分からなかったため、「簡単な機能だし、悩むくらいなら作ってしまえ」と作ってみたところ、ちょっとハマってしまったので解決方法をメモしておきます。

2014年1月3日金曜日

生命保険会社の責任準備金

アクチュアリーの最も重要な業務は「保険料率の作成」と「責任準備金の算出」だと思っているのですが、この後者の「責任準備金」というものがどうもアクチュアリー以外にほとんど理解されていないような気がします。

そう思って調べてみると、責任準備金に関する説明というものは、アクチュアリー向け以外にはあまり充実していないということに気がつきました。結構様々な目的や異なる方法で計算された多義的な概念だから一口に説明するのは難しいけれど、だからこそ相応のボリュームの情報が、もう少しパブリックな場所にあってもいいのになあ。


以下、生命保険の分野において「責任準備金」がどのように説明されているか、生命保険会社等のWEBサイトにある「用語集」の内容を調べてみたまとめです。
損保分野や年金分野での説明はもう少し違うと思いますが。


2013年8月31日土曜日

小さい位数の有限群の分類

割と小さな自然数nに対し、位数nの有限群が同型を除いて何種類存在するか、という問いに対する有益な資料へのリンク集。

どの資料についても、自分で検証はしていない(というかする能力がない・・・)のでどれくらい信頼できる資料かは分かりませんが。


位数119までの群の分類
http://www.akanekodou.mydns.jp/math/mathmenu.html
119以下のほとんどの位数について、群の具体的な表示と、分類の証明まで行っているすごいテキストです。これを読みこなせれば随分とテクニックが身につくんだろうなあと思う、当面の自分の目標。


位数30以下の群の分類
http://www.math.meiji.ac.jp/~kurano/soturon/soturon.htm
明治大学の学生の卒論。位数30以下の群の分類について、基本的な手法の証明や用語の定義も含め丁寧に書いてあります。


The list of all discrete groups of order less than or equal to 100
http://lpsc.in2p3.fr/wingerter/smallgroups.html
位数100以下の群一覧表。とても便利です。


numbers of groups of orders 1 to 2015
http://orion.math.iastate.edu/maddux/504-Fall-2009/
2015以下のnに対し、位数nの群が何種類存在するかの個数一覧表。すごい・・・。

これによると、位数2015以下の群は全部で49,910,529,553種類存在するとのこと。
その内49,487,365,422個(99.2%)は位数1024の群、残った423,164,131個の内408,641,062個(96.6%)は位数1536の群という豆知識が得られました。

また、このpdfの内容を、Google Spread Sheetに貼り付けました。
http://goo.gl/lovBnl


どれも根気勝負みたいな資料で、作成された方々にはほんとうに頭が下がります。


2013/9/4 追記
「numbers of groups of orders 1 to 2015」について。

日本数学会 代数学分科会 シンポジウム
http://mathsoc.jp/section/algebra/algsymp_past/algsymp04.html
8月4日 原田耕一郎氏の報告資料に、

>> 紀元2000 年を記念する意味もあったろうか、Besche/Eick/O’Brien は位数2000 以下の
群の同型類をすべて定めた(2000).

とあり、上記リストはその論文の結果の一部かもしれません。
原論文がどこかでみれないかなあ。

2013年8月10日土曜日

Math Toolの素因数分解コード

Androidアプリ Math Tool ( https://play.google.com/store/apps/details?id=jp.blogspot.tsukinihinikeni.mathtool )で使用している素因数分解のコードです。

エラー処理、バックグラウンドプロセスに関する処理など、直接素因数分解に関係ない部分は省略しています。



TextView tv = (TextView) findViewById(R.id.***);
BigInteger n = new BigInteger(***);

if (n.abs().equals(BigInteger.ZERO)) {
tv.setText("0 = 0");
} else if (n.equals(BigInteger.ONE)) {
tv.setText("1 = 1");
} else if (n.negate().equals(BigInteger.ONE)) {
tv.setText("-1 = -1");
} else {
String factor = factorize(n);

String txt = "";
txt += n.toString() + " = ";
txt += factor;
tv.setText(txt);
}


private String factorize(BigInteger n) {

String f = "";
int factor_count = 0;

if (n.signum() == -1) {
f = "-1 * ";
n = n.negate();
}

ArrayList<String> factor_str = new ArrayList<String>();
ArrayList<Integer> index = new ArrayList<Integer>();

//primesは素数が小さい順に入っている配列(素数リスト)
//p_max_orderは素数リストの要素数
//p_maxは素数リストより大きい最小の自然数

for (int i = 0; i < p_max_order; i++) {

BigInteger p = new BigInteger(primes.get(i));
if (p.pow(2).compareTo(n) == 1) {
break;
}

int k = 0;

while (n.mod(p).equals(BigInteger.ZERO)) {
k++;
n = n.divide(p);
}

if (k > 0) {
factor_count++;
factor_str.add(p.toString());
index.add(k);
}
}

if (!n.equals(BigInteger.ONE)) {
factor_count++;
factor_str.add(n.toString());
index.add(1);
}

String notation = "";
if (n.compareTo((new BigInteger(p_max)).pow(2)) >= 0) {
notation = "\n  # " + n.toString() + " は " + p_max
+ " 以上の素数で割り切れる可能性があります.";
} else if (factor_count == 1 && index.get(0) == 1 && f == "") {
notation = " (素数)";
}

for (int i = 0; i < factor_count; i++) {
if (i > 0) {
f += " * ";
}

if (index.get(i) == 1) {
f += factor_str.get(i);
} else {
f += factor_str.get(i) + "^" + index.get(i);
}
}

f += notation;

return f;
}




アプリの使い方などは
http://tsukinihinikeni.blogspot.jp/2013/08/math-toolandroid.html
をご覧ください。

素数のリストについては、Excelで作成したCSVでアプリ内に保持しています。
作成方法は
http://tsukinihinikeni.blogspot.jp/2012/01/blog-post.html
をご覧ください。

Math Tool(Androidアプリ)

Androidアプリ:Math Tool
package:jp.blogspot.tsukinihinikeni.mathtool
対象:Android3.1+ ( HONEY COMB MR1以上)

Math Toolは手軽に剰余の計算(商と余りの計算)、既約分数の計算、素因数分解ができるツールです。
シンプルさと、どれだけ大きな数についてでも計算できるというのがウリです。(素因数分解には限界がありますが、それでもかなり大きな数まで計算出来ます。)

・アプリの概要

基本的な使い方
1.入力欄に数字を入力すると、自動的に素因数分解の結果が表示されます。



2.除数を入力すると、剰余計算の結果、既約分数が自動で表示されます。



3.C を押すと入力値がクリアされます。


入力値が大きな値の場合、素因数分解の結果が表示されるまでに若干の時間がかかる場合があります。


設定
Settings から、アプリの設定を変更できます。



・剰余計算 - 剰余計算表示
OFFにすると、剰余の計算・表示を行いません。

・剰余計算 - 負の数の剰余計算
初期設定では "余りは正値にする" になっています。
"余りは負値にする" を選んだ場合、負の数を入力した時の商と余りが、余りを0以下とするように計算されます。
具体的には
"余りは正値にする" → -7 ÷ 3 = -3 余り 2
"余りは負値にする" → -7 ÷ 3 = -2 余り -1
と計算されます。
どちらを選んでも、正の数の割り算については
7 ÷ 3 = 2 余り 1
になります。

・既約分数 - 既約分数表示
OFFにすると、既約分数の計算・表示を行いません。

・素因数分解 - 素因数分解表示
OFFにすると、素因数分解の計算・表示を行いません。

・素因数分解  - 素数リスト
素因数分解に使用する素数の範囲を指定します。
初期設定では1,229となっており、この時、小さい方から1,229番目までの素数(つまり、1万以下の素数)を使用して素因数分解を行います。そのため、1万より大きい素数同士の積については、その数が素数であるか合成数であるか判定できません。具体的には、
n < 1万×1万 で、nは1万以下の素数で割れない → nは素数
n > 1万×1万 で、nは1万以下の素数で割れない → nは素数か合成数か判定できない
としています。従って、入力した数が大きい場合、正確に"素因数"に分解できない場合があります。
この使用する素数の数(1,229)を変更するのがこの設定項目になります。何番目までの素数を使用するか入力して下さい。使用する素数の範囲を大きくすると正確に素数の判定ができる範囲が広がりますが、計算に時間がかかる場合があります。
使用する素数の数の最大値は78,498(100万以下の素数)です。

・その他 - 計算タイトルの表示
"タイトルを表示する" → 計算結果に、[剰余計算]、[既約分数]、[素因数分解]のタイトルが表示されます。
"タイトルを表示しない" → 計算結果に上記のタイトルが表示されません。
タイトルを表示する

タイトルを表示しない

・その他 - テーマ
画面のカラーを変更できます。
Holoは黒を基調とした画面、Holo.Lightは白を基調とした画面になります。

Holo

Holo.Light


・仕様

入力可能な数字
入力可能な数字の桁数に制限はありません。
商の計算など、基本的な部分については、BigIntegerクラス(http://developer.android.com/intl/ja/reference/java/math/BigInteger.html)を使用しています。

素因数分解
素因数分解については上記の通り、予め用意した素数リストで実際に割ってみて計算しています。そのため、素数リストの上限を超える大きな素数どうしの積については、素数か合成数かの判定をすることができません。
しかし、入力する数値自体に制限はないので、小さな素数どうしの積であれば大きな数についても素因数分解が可能です。例えば、54桁の数 808017424794512875886459904961710757005754368000000000 = 2^46 * 3^20 * 5^9 * 7^6 * 11^2 * 13^3 * 17 * 19 * 23 * 29 * 31 * 41 * 47 * 59 * 71 などは殆ど処理時間なく計算可能です。
また、素数と判定できた場合は「素数である可能性が高い数字」ではなく、「素数であることが確認できた数字」となります。


・その他

アプリのダウンロード(Google play)
https://play.google.com/store/apps/details?id=jp.blogspot.tsukinihinikeni.mathtool


連絡先
何かありましたら、この記事のコメント欄、あるいはメール( d.kondo.android@gmail.com )でお問い合わせください。

2012年3月25日日曜日

AnotherBrowser(Androidアプリ)

Androidアプリ:AnotherBrowser
package:jp.blogspot.tsukinihinikeni.AnotherBrowser
対象:Android1.6+ (DONUT以上)

AnotherBrowser"はAndroid用ブラウザアプリの共有機能から、今見ているページを別のブラウザアプリで表示するアプリです。
また、ローカルに保存しているHTMLファイルをブラウザアプリで表示することができます。

・アプリの概要
1.ブラウザの"ページを共有"等からこのアプリを選択すると、今見ているページを別のブラウザアプリで表示することができます。
例えば、普段使用しているブラウザアプリはflashをサポートしていないので、
このページを他のブラウザで表示したい

"ページを共有"等のメニューから、"AnotherBrowser"を選択する

今見ているページを、どのブラウザで開くかの選択肢が表示される
※設定で、この選択を経ずに、常に特定のアプリで表示することもできます

2.ファイル管理ソフト等でローカルに保存しているHTMLファイルにアクセスした時、そのファイルをどのアプリで表示するかのダイアログが現れます。
しかし、標準のブラウザを含む多くのブラウザはローカルファイルの表示(file://へのアクセス)に対して候補に現れず、HTMLファイルを直接ブラウザアプリで開くことができません。
"AnotherBrowser"を経由することで、好きなブラウザアプリでHTMLファイルを表示することができるようになります。
"AnotherBrowser"を選択すると、ブラウザアプリの一覧が現れるので、ファイルを表示したいブラウザを選択して下さい。
また、設定で常に特定のブラウザで開くようにすることもできます。

3.設定画面
共有機能等からではなく、アプリ一覧の中からこのアプリを起動すると、設定画面が表示されます。

設定画面
Web Pages
http://、https://で接続するときの設定です。
「毎回ブラウザを選択する」のチェックを外すと、常に設定したアプリで開きます。どのアプリで開くかの設定は、「ブラウザの変更」から行なって下さい。

ブラウザの変更
Local Files
file://で接続するときの設定です。
Web Pagesとは異なるブラウザを設定することができます。


・使用時の要件
無料、広告なし、権限(permission)不要です。
その他も特別な要件は必要としませんが、以下のことに気をつけて下さい。

・"ページの共有"の仕様はブラウザアプリによって異なるため、このアプリがうまく機能しないアプリもあるかもしれません。
・ローカルファイルを開くときの仕様もファイル管理アプリ等によって異なるため、このアプリがうまく機能しないアプリもあるかもしれません。また、ローカルのHTMLファイルを読み込む際の挙動が、意図しないものとなるブラウザアプリもあるかもしれません。

これらについては、個別に対処できることもありますので、このアプリがうまく動作しない場合などは詳細をご連絡いただければ、なんとかできるようがんばろうと思っています。


・その他

アプリのダウンロード(Google play)
https://play.google.com/store/apps/details?id=jp.blogspot.tsukinihinikeni.AnotherBrowser

類似の既存のアプリ
ブラウザアプリの共有機能から、今見ているページを別のブラウザアプリで表示するアプリとしては、既に以下のようなものもマーケットに公開されています。

・Browser Switch ( https://play.google.com/store/apps/details?id=com.nkj.app.browserswitch )
クリップボードの内容からブラウザを開いたり、ステータスバーにアイコンを置いたりもできます。

・Choose Browser ( https://play.google.com/store/apps/details?id=com.benhirashima.choosebrowser )
共有機能で受けたURLを暗黙的インテントで他のブラウザに飛ばす、この発想のアプリとしては最も基本的なロジックになっています。

2者とも、ローカルファイルを開くということについては特に意識して作られていないようですので、「ローカルのHTMLファイルをブラウザで開きたい」というややマイナーな趣向をお持ちの方については、"AnotherBrowser"が一番お役に立てる気がします。

更新
(2012/7/14 ver1.1)
設定したブラウザが見つからない時のエラー処理の方法を変更しました。
(旧:標準のブラウザで開く→新:ブラウザ設定画面)

連絡先
何かありましたら、この記事のコメント欄、あるいはメール( d.kondo.android@gmail.com )でお問い合わせください。

2012年3月16日金曜日

PlusOne(Androidアプリ)

Androidアプリ:PlusOne
package:com.blogspot.tsukinihinikeni.plusone
対象:Android1.6+ (DONUT以上)

"PlusOne"はAndroid用ブラウザアプリの共有機能から、今見ているページを+1することができるようになるアプリです。

・アプリの概要
ブラウザの"ページを共有"等からこのアプリを選択すると、今見ているページを+1するボタンをブラウザで表示します。

このページを+1したいが、+1ボタンが設置されていない

"ページを共有"を選択

"PlusOne"(このアプリ)を選択

先ほどのページを+1するためのボタンが表示されます。
ボタンの上に書かれているアドレスが、いま+1しようとしているアドレスです。

ボタンをクリックすることで+1ができます。
もう一度クリックすれば+1を取り消すこともできます。

・使用時の要件
+1を行うためには、Google+のアカウントが必要です。また、+1ボタンを表示するブラウザでGoogleアカウントにログインしている必要があります。

+1ボタンはJavaScriptで動いているため、ブラウザのJavaScriptがオフになっているとボタンが使用できません。

・その他

アプリのダウンロード(Google play)
https://play.google.com/store/apps/details?id=com.blogspot.tsukinihinikeni.plusone

セキュリティ
"PlusOne"は、権限やGoogleアカウントの情報を必要としません。Googleアカウントへのアクセスなどはブラウザアプリ任せのため、このアプリはユーザーの個人データには一切アクセスしません。

+1ボタンが表示されない
このアプリにもブラウザアプリにもアップデートが行われていないにもかかわらず、+1ボタンが表示されなくなった時期がありました。原因は私には分かりませんでした。また同様のことが起こるかもしれません。
表示されなくなった時期には、マーケットからこのアプリを削除するつもりでいましたが、原因は分かりませんがまた機能するようになったので、当分は機能する/しないに関わらずとりあえずマーケットに公開しておくことにします。ご迷惑をおかけすることもあるかと思いますが宜しくお願いいたします。

更新
(2012/7/14 ver1.3)
設定したブラウザが見つからない時のエラー処理の方法を変更しました。
(旧:標準のブラウザで開く→新:ブラウザの選択ダイアログ)

(2012/4/12 ver1.2)
標準のブラウザ以外でも+1ボタンを表示できるようにしました。
+1ボタン表示用のHTMLを若干変更しました。

(ver1.1)
+1ボタン表示用のHTMLを若干変更しましたが、このバージョンはマーケットに公開しませんでした。

連絡先
何かありましたら、この記事のコメント欄、あるいはメール( d.kondo.android@gmail.com )でお問い合わせください。

2012年2月25日土曜日

入力ボックス(Androidアプリ)

Androidアプリ:入力ボックス
package:com.blogspot.tsukinihinikeni.InputBox
対象:Android2.2+ (FROYO以上)

"入力ボックス"はマッシュルームアプリです。
Simejiのマッシュルーム呼び出し機能、ATOKのATOKダイレクトなどから呼び出してご使用下さい。

・アプリの概要
"入力ボックス"をマッシュルームとして呼び出すと、文字入力用のボックスが現れます。

・どんな時に役に立つか
1.Gmailアプリの宛先入力時

現在、Galaxy NEXUSでは、標準のGmailアプリの宛先欄にうまく日本語が入力できません。
2012年4月8日追記:2012年4月4日のGmailアプリのアップデートで、この件の不具合は解消されたようです。


(ATOKの場合1)一文字ごとに確定してしまう (ATOKの場合2)文字が2つ入力されてしまう

入力一文字ごとに電話帳の検索が入るためのバグと思いますが、漢字変換等もできないので、日本人名の入力ができない状態です。


このアプリを使えば、漢字変換等を行った後の文字を宛先欄に入力することができます。

入力ボックスに文字を入力

宛先欄に反映



 2.入力文字が隠されてしまう時

パスワードの入力フォームなど、入力文字が***や・・・のようにマスクされてしまい、何を入力したか分からなくなる時があります。
また、入力フォームのフォントが小さすぎて読みづらい、入力フォームが1行分しか表示されなくて入力した文字が表示されきらない、といったこともあるかもしれません。

このアプリを使えば、入力文字を確認後にフォームに反映させることができます。


パスワードを入力

反映
スクリーンショットをとっておけば、パスワードとして何を設定したかの簡易なメモにもなります。


3.アクションボタン

ver.2.0(2012/8/5-)にて、アクションボタンの機能を追加しました。


ボリュームダウンキーを押すと、入力ボックスの下に4つのアイコンが表示されます。

ボタンの機能は、左から、
・入力した番号に電話をかけます
・入力したアドレス宛にメールを作成します
・入力した内容を共有します
・入力ボックスの表示位置を移動します

ボリュームアップキーを押すとアクションボタンの表示をやめます。
また、メニューキーによってもアクションボタンの表示/非表示を切り替えることができます。

この機能によって、(マッシュルームの本来の使い方からは外れますが、)ブラウジング中でもメール作成中でも、IMEさえ立ち上げられる(あるいはマッシュルームを起動できる)状態であれば、画面を見ながら電話番号やメールアドレスを入力することができます。


・その他
アプリのダウンロード(Google play)
https://play.google.com/store/apps/details?id=com.blogspot.tsukinihinikeni.InputBox

更新
(2012/8/5 ver.2.0)
アクションボタン機能を追加しました。


(2012/2/27 ver.1.1)
対象OSを「Android4.0+(Ice Cream Sandwich以上)」から「Android2.2以上(FROYO以上)」に広げました。

(2012/2/29 ver.1.2)
入力ボックスが複数行を表示するように修正しました。


連絡先
何かありましたら、この記事のコメント欄、あるいはメール( d.kondo.android@gmail.com )でお問い合わせください。


2012年2月9日木曜日

BouncingBalls

API Demosに入っているBouncingBalls (http://goo.gl/HuQSf)を自分でも書いてみた。

ただし、勉強のため

・背景色は静的なものとし、AnimatorUpdateListenerによってinvalidateの処理を行うこととした。(オリジナルのBouncingBallsは背景色を変化させることで自動的にinvalidateの処理をおこなっている)

・オリジナルにあるShapeHolderクラスを使用しない。つまり、メインのBouncingBallsクラスの中で全て完結させる。また、ObjectAnimatorは使用せず、全てValueAnimatorで処理する。

という制限をつけて行った。

上手いコードにはなっていないが、とりあえずできたので貼りつけておく。



import java.util.ArrayList;

import android.animation.ValueAnimator;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator.AnimatorUpdateListener;

public class BouncingBallsActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
((LinearLayout) findViewById(R.id.container)).addView(new MyView(this));
}

public class MyView extends View implements AnimatorUpdateListener {

int i = 0;
ArrayList<Integer> rgb = new ArrayList<Integer>();
ArrayList<AnimatorSet> animatorSet = new ArrayList<AnimatorSet>();

ArrayList<ValueAnimator> animLeft1 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animTop1 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animRight1 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animBottom1 = new ArrayList<ValueAnimator>();
ArrayList<AnimatorSet> animatorSet1 = new ArrayList<AnimatorSet>();

ArrayList<ValueAnimator> animLeft2 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animTop2 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animRight2 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animBottom2 = new ArrayList<ValueAnimator>();
ArrayList<AnimatorSet> animatorSet2 = new ArrayList<AnimatorSet>();

ArrayList<ValueAnimator> animLeft3 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animTop3 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animRight3 = new ArrayList<ValueAnimator>();
ArrayList<ValueAnimator> animBottom3 = new ArrayList<ValueAnimator>();
ArrayList<AnimatorSet> animatorSet3 = new ArrayList<AnimatorSet>();

public MyView(Context context) {
super(context);
}

@Override
public void onAnimationUpdate(ValueAnimator anim) {
invalidate();
}

@Override
public boolean onTouchEvent(MotionEvent event) {

if (event.getAction() != MotionEvent.ACTION_DOWN
&& event.getAction() != MotionEvent.ACTION_MOVE) {
return false;
}

float h = (float) getHeight();
float endY = h - 50f;
float X = event.getX();
float startY = event.getY();
int duration = (int) (500 * ((h - startY) / h));

// color
rgb.add(i, Color.rgb((int) (Math.random() * 255),
(int) (Math.random() * 255), (int) (Math.random() * 255)));

// down
animLeft1.add(i, ValueAnimator.ofFloat(X - 25f, X - 25f));
animLeft1.get(i).setDuration(duration);

animRight1.add(i, ValueAnimator.ofFloat(X + 25f, X + 25f));
animRight1.get(i).setDuration(duration);

animTop1.add(i, ValueAnimator.ofFloat(startY - 25f, endY - 25f));
animTop1.get(i).setDuration(duration);
animTop1.get(i).setInterpolator(new AccelerateInterpolator());
animTop1.get(i).addUpdateListener(this);

animBottom1.add(i, ValueAnimator.ofFloat(startY + 25f, endY + 25f));
animBottom1.get(i).setDuration(duration);
animBottom1.get(i).setInterpolator(new AccelerateInterpolator());

animatorSet1.add(i, new AnimatorSet());
animatorSet1.get(i).play(animTop1.get(i)).with(animBottom1.get(i));
animatorSet1.get(i).play(animTop1.get(i)).with(animLeft1.get(i));
animatorSet1.get(i).play(animTop1.get(i)).with(animRight1.get(i));

// skew
animLeft2.add(i, ValueAnimator.ofFloat(X - 25f, X - 25f * 2f));
animLeft2.get(i).setDuration(duration / 4);
animLeft2.get(i).setInterpolator(new DecelerateInterpolator());
animLeft2.get(i).setRepeatCount(1);
animLeft2.get(i).setRepeatMode(ValueAnimator.REVERSE);

animRight2.add(i, ValueAnimator.ofFloat(X + 25f, X + 25f * 2f));
animRight2.get(i).setDuration(duration / 4);
animRight2.get(i).setInterpolator(new DecelerateInterpolator());
animRight2.get(i).setRepeatCount(1);
animRight2.get(i).setRepeatMode(ValueAnimator.REVERSE);

animTop2.add(i, ValueAnimator.ofFloat(endY - 25f, endY - 25f * 0f));
animTop2.get(i).setDuration(duration / 4);
animTop2.get(i).setInterpolator(new DecelerateInterpolator());
animTop2.get(i).setRepeatCount(1);
animTop2.get(i).setRepeatMode(ValueAnimator.REVERSE);
animTop2.get(i).addUpdateListener(this);

animBottom2.add(i, ValueAnimator.ofFloat(endY + 25f, endY + 25f));
animBottom2.get(i).setDuration(duration / 4);
animBottom2.get(i).setRepeatCount(1);
animBottom2.get(i).setRepeatMode(ValueAnimator.REVERSE);

animatorSet2.add(i, new AnimatorSet());
animatorSet2.get(i).play(animTop2.get(i)).with(animBottom2.get(i));
animatorSet2.get(i).play(animTop2.get(i)).with(animLeft2.get(i));
animatorSet2.get(i).play(animTop2.get(i)).with(animRight2.get(i));

// up
animLeft3.add(i, ValueAnimator.ofFloat(X - 25f, X - 25f));
animLeft3.get(i).setDuration(duration);

animRight3.add(i, ValueAnimator.ofFloat(X + 25f, X + 25f));
animRight3.get(i).setDuration(duration);

animTop3.add(i, ValueAnimator.ofFloat(endY - 25f, startY - 25f));
animTop3.get(i).setDuration(duration);
animTop3.get(i).setInterpolator(new DecelerateInterpolator());
animTop3.get(i).addUpdateListener(this);

animBottom3.add(i, ValueAnimator.ofFloat(endY + 25f, startY + 25f));
animBottom3.get(i).setDuration(duration);
animBottom3.get(i).setInterpolator(new DecelerateInterpolator());

animatorSet3.add(i, new AnimatorSet());
animatorSet3.get(i).play(animTop3.get(i)).with(animBottom3.get(i));
animatorSet3.get(i).play(animTop3.get(i)).with(animLeft3.get(i));
animatorSet3.get(i).play(animTop3.get(i)).with(animRight3.get(i));

animatorSet.add(i, new AnimatorSet());
animatorSet.get(i).play(animatorSet2.get(i))
.after(animatorSet1.get(i));
animatorSet.get(i).play(animatorSet3.get(i))
.after(animatorSet2.get(i));
animatorSet.get(i).start();

i++;

return true;
}

@Override
public void onDraw(Canvas canvas) {
if (i > 0) {
for (int j = 0; j < animatorSet.size(); ++j) {
if (animatorSet1.get(j) != null
&& animatorSet1.get(j).isRunning()) {
Paint paint = new Paint();
paint.setColor(rgb.get(j));
paint.setStyle(Paint.Style.FILL);
RectF oval = new RectF((Float) animLeft1.get(j)
.getAnimatedValue(), (Float) animTop1.get(j)
.getAnimatedValue(), (Float) animRight1.get(j)
.getAnimatedValue(), (Float) animBottom1.get(j)
.getAnimatedValue());
canvas.drawOval(oval, paint);
} else if (animatorSet2.get(j) != null
&& animatorSet2.get(j).isRunning()) {
Paint paint = new Paint();
paint.setColor(rgb.get(j));
paint.setStyle(Paint.Style.FILL);
RectF oval = new RectF((Float) animLeft2.get(j)
.getAnimatedValue(), (Float) animTop2.get(j)
.getAnimatedValue(), (Float) animRight2.get(j)
.getAnimatedValue(), (Float) animBottom2.get(j)
.getAnimatedValue());
canvas.drawOval(oval, paint);
} else if (animatorSet3.get(j) != null
&& animatorSet3.get(j).isRunning()) {
Paint paint = new Paint();
paint.setColor(rgb.get(j));
paint.setStyle(Paint.Style.FILL);
RectF oval = new RectF((Float) animLeft3.get(j)
.getAnimatedValue(), (Float) animTop3.get(j)
.getAnimatedValue(), (Float) animRight3.get(j)
.getAnimatedValue(), (Float) animBottom3.get(j)
.getAnimatedValue());
canvas.drawOval(oval, paint);
}
}
}
}
}
}



なお、R.layout.mainは以下のようになっている。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/container"
    android:background="#ffffffff">
</LinearLayout>

android:id="@+id/container"を定義している以外は、特に何もしていない。