Hatena::Grouptumblr

saitamanodoruji

くこかー
saitamanodoruji.tumblr.com
 | 

2012-06-04

指数関数フィッティングでもう少しランダムに Endless Summer

11:17 | 指数関数フィッティングでもう少しランダムに Endless Summer - saitamanodoruji を含むブックマーク はてなブックマーク - 指数関数フィッティングでもう少しランダムに Endless Summer - saitamanodoruji



(2013-07-25 追記: 最新版は version 2013 です. Exponential Endless Summer を改訂しました (version 2013) - saitamanodoruji - たんぶら部 - Tumblove - )



Tumblr Dashboard のシャッフルをポスト ID のランダム選択でやると最近のばっかり出てくる。この問題への対策として taizooo の方法 があり、ReblogMachine に採用されている*1。taizooo の方法を使えばどの年からポストが選ばれる確率も全て等しくなる。ただ Tumblr の単位時間あたりのポスト数は常に増えているので年末に近いポストほど選ばれる確率が高い。そこで、もう少し時刻が偏らないようにポスト ID を引ける新しい方法を考えた。

方法

まず taizooo にならって http://kagurazakaundergroundresistance.tumblr.com/archive から半年ごとの Tumblr の総ポスト数 (= ポスト ID) のデータを取った。さらに 2007 年の 7 月以前については http://www.davidslog.com/archive を見て補完した。

プロット*2

Fig. 1
f:id:saitamanodoruji:20120604011642j:image

縦軸対数にする。

Fig. 2
f:id:saitamanodoruji:20120604013317j:image

直線っぽい。t の範囲を 2 つにわけて指数関数でフィッティング。

Fig. 3
f:id:saitamanodoruji:20120604014940p:image

Fig. 4
f:id:saitamanodoruji:20120604014941p:image

Fig. 5
f:id:saitamanodoruji:20120604021556p:image

関数を A\exp\[Bt\] の形になおして、

  N(t) = f(t) = \{{(3.120 \times 10^{-244}) \exp{\[(4.865 \times 10^{-10})t\]}\text{   \rm{if} \it{t} < 1183377600000}}\\{(7.768 \times 10^{-22}) \exp{\[(5.423 \times 10^{-11})t\]}\text{   \rm{if} 1183377600000 \leq t}}\.

ここで t \[\rm{msec}\]Unix time、N(t)t における総ポスト数。


f(t) は「この日、この時刻の Dashboard が見たい」という用途で使う場合には誤差が大きすぎて (2 ヶ月くらいずれたりする) あまり役に立たないが、Unix time t をランダムに選んだ上で f(t) を使ってポスト ID N を得る次のようなスクリプトでは十分使える。

function draw(oldestID, newestID) {
  var oldestUTime = getUnixTimeFromPostID(oldestID);
  var newestUTime = getUnixTimeFromPostID(newestID);
  var drawnUTime = oldestUTime + Math.floor((newestUTime - oldestUTime) * Math.random());
  return getPostIDFromUnixTime(drawnUTime);
}

function getPostIDFromUnixTime(utime) {
  if ( utime < 1149465600000) {
    return;
  } else if ( utime < 1183377600000 ) {
    return Math.ceil(3.12 * Math.pow(10, -244) * Math.exp(4.865 * Math.pow(10, -10) * utime));
  } else if ( utime >= 1183377600000 ) {
    return Math.ceil(7.768 * Math.pow(10, -22) * Math.exp(5.423 * Math.pow(10, -11) * utime));
  }
}

function getUnixTimeFromPostID(postID) {
  if ( postID < 1 ) {
    return;
  } else if ( postID < 4600108 ) {
    return Math.floor(Math.log(postID/(3.12 * Math.pow(10, -244)))/(4.865 * Math.pow(10, -10)));
  } else if ( postID >= 4600108) {
    return Math.floor(Math.log(postID/(7.768 * Math.pow(10, -22)))/(5.423 * Math.pow(10, -11)));
  }
}

draw() にポスト ID の下限と上限をわたすとその間からランダムにポスト ID をひとつ返してくれる。やっていることは

  1. f^{-1}(N) *3 で下限と上限の ポスト ID N_{oldest}, N_{newest}Unix time t_{oldest} \[\rm{msec}\], t_{newest} \[\rm{msec}\] に変換。
  2. Math.random()Unix time t \[\rm{msec}\] (t_{oldest} \leq t < t_{newest}) をランダムに選ぶ。
  3. f(t)t をポスト ID N に変換。

テストページを http://saitamanodoruji.tumblr.com/exponentialEndlessSummer においた。

Tumblr の総ポスト数が f(t) に完全に一致すると仮定した場合には、taizooo の方法では 1 月のポストに比べて 12 月のポストは 4.8 倍くらい選ばれやすくて、今回の方法ではその点が改良されたはず。ちゃんと評価するにはそれぞれの方法で得たポスト ID の Dashboard を実際に取得して時刻のデータをヒストグラムにするとかして確率分布を求めたらいいと思う。Endless Summer on Dashboard for Greasemonkey を改造して AutoPagerize が継ぎ足すたびに GM_setValue() で Preference に Dashboard の時刻を記録するようにすればリブログしながらデータを貯められそう。

pp2012/06/07 21:59pixitailで縦長の漫画がぼやけて見れないのをなんとかしろください

 |