ウォーリーをさがすとはどういうことか


ウォーリーを(アルゴリズム組んでプログラムの力で)探せ!

という祭りがあったそうです。

Mathematicaの強さを見せつけるものになった優勝作品を試してみました。

waldo = Import["http://shopping.c.yimg.jp/lib/cast-shop/wally3.gif"];
red = Fold[ImageSubtract, #[[1]], Rest[#]] &@ColorSeparate[waldo];
corr = ImageCorrelate[red,
   Image@Join[ConstantArray[1, {2, 4}], ConstantArray[0, {2, 4}]],
   NormalizedSquaredEuclideanDistance];
pos = Dilation[ColorNegate[Binarize[corr, .12]], DiskMatrix[30]];
found = ImageMultiply[waldo,
  ImageAdd[ColorConvert[pos, "GrayLevel"], .5]]

結果

確かにさがせてはいるようですが(すごい!)、ウォーリーをさがすというのがどういうことなのかはさがせていないみたいです。

祝・卒業


勤務する大学の、私にとっては最後の卒業式でした。式後、歴代の卒業生の半数近くが集まった、華やかなパーティーがありました(総勢49名)。「学生時代が人生最良の時であってはいけない」という考え方もありますが、もし、教師が学生を大切に守り育て、学生がそれをすばらしい思い出として記憶するなら、学生はもちろん教師も、いつかはその思い出に救われるのではないかと思います。カラマーゾフ万歳! 卒業おめでとう。

コンピュータが囲碁トッププロに四子局で勝ってしまった


2008年にスパコンと(トップではない)プロが対戦したときは、9路盤で引き分けたものの、19路盤ではハンデを最大(9子局)にしても勝負になりませんでした(当時の記事)。

それから4年経ち、ハンデを大幅に減らした対局(19路盤4子局)で、コンピュータがトッププロ(武宮正樹9段)に勝ってしまいました。対局の様子はニコニコ生放送で配信されましたが、視聴者は6万人を超えていたようです。私もその一人でした。

特別イベント『コンピュータ囲碁がプロ棋士に挑戦!』

モンテカルロ法という、ランダム(もちろん枝刈りは必須ですが)な着手で対局をシミュレートし、勝率の高かった手を選ぶ方法を発明し、その使い方がうまくなってきたことが勝因でしょう。

囲碁プログラムもはや、アマチュアの全国大会クラスになっているということなので、最近私がコンピュータに勝てないのも納得です。

B005KDG31M今回対戦したプログラムZen(の祖先)は、すでに「天頂の囲碁」という製品に搭載されて販売されているので、興味のある人は試してみるといいでしょう。最新版はアマ4段相当の「天頂の囲碁3」ですが、アマ2段相当の初代「天頂の囲碁」なら、安いパッケージもありますね。アマ3段相当のPS3版もあります。

「最善手を知る」のはおそらく無理なので、確率的な手法しかないだろうとは思いますが、ハードウェアげ進歩すれば現行のモンテカルロ法でトッププロにハンデ無しで勝てるのか、それでは足りない何かがあるのか。その答えを知るのは、そんなに遠い未来ではないのかもしれません。

将棋の人間側は日本将棋連盟、コンピュータ側は情報処理学会、囲碁の人間側は日本棋院、コンピュータ側は人工知能学会でした。囲碁には関西棋院もあるのですが、何か思うところはあるのでしょうか。学会はたくさん残っています。

jOOXを使えば、JavaでもjQueryのようにXMLを操作できる


4627847327拙著『Webアプリケーション構築入門』では、「Webアプリを作れるようになる前に、使えるようになろう」という考えから、いくつかのWeb APIの利用方法を早い段階で紹介しています。多くの場合、Web APIを使うためには、XMLやJSONなどのデータをプログラムから操作できなければなりません。本書では、Twitterのパブリックタイムラインを題材にして、JavaでXMLを処理する方法やPHPやJavaScript(jQuery)でJSONを処理する方法を紹介しています。

しかし、jOOXのようなライブラリを使うと、拙著で紹介したよりも簡単に、JavaでもjQuery風にXMLを操作できます。というのが、今日のお話。

p.72で紹介している、つぶやきの本文だけを取り出して表示するコードは次のようになります。(p.86などを参考に、joox-バージョン番号.jarを使えるようにしてから試してください。)

import java.net.*;
import org.joox.*;
import static org.joox.JOOX.*;

public class JooxPublicTimeline {

  public static void main(String[] args) throws Exception {
    URL url = new URL("http://api.twitter.com/1/statuses/public_timeline.xml");
    $(url.openStream()).find("text").each(new Each() {

      @Override
      public void each(Context cntxt) {
        System.out.println($(cntxt.element()).text());
      }
    });
  }
}

.each()の中にラムダ式を書けないのがつらいところで、この例のように無名インナークラス(p.181)を使わなければなりません。.find()のあとに.each()とすればListが返るので、それをfor文で回して・・・、としたくなるところですが、jQueryらしく書くとこんな感じでしょう。

p.73で紹介している、「名前: つぶやきの本文」という形式で表示するコードは次のように書き換えられます。

import java.net.*;
import org.joox.*;
import static org.joox.JOOX.*;

public class JooxPublicTimeline2 {

  public static void main(String[] args) throws Exception {
    URL url = new URL("http://api.twitter.com/1/statuses/public_timeline.xml");
    $(url.openStream()).find("status").each(new Each() {

      @Override
      public void each(Context cntxt) {
        Match status = $(cntxt.element());
        String text = status.child("text").text();
        String name = status.find("user>name").text();
        System.out.println(name + ": " + text);
      }
    });
  }
}

拙著で紹介しているJavaでXMLを処理する方法は標準的なものではありますが、よく言えばJavaらしい、悪く言えば冗長なコードです。「標準的」ということにこだわらなければ、ここで紹介したような、もっと簡単な方法があるのです。

『「知」の欺瞞』文庫化


4413019180宗教やブログ炎上、ドラッグなど、大学生が気をつけるべきことをいろいろ紹介した『大学生がダマされる50の危険』という本がありました。オウム真理教の全盛期(?)に大学生になった私にとっては、新興宗教がそれほど危険なものであり続けているのかどうか、実はよくわからないのですが(最近、私のために祈ってくれる人に出会わないし)、まあ、さらっと見ておいて損はないのかもしれません。

これさえ読めば安全・安心とダマされる人もいないでしょうが、大学生、つまり学問の場にいる学生に対して、「学問的な危険」を一つも紹介していないのは、ちょっと困ったことなので、ここで一つ紹介しておきましょう。

4006002610「学問的な危険」にもいろいろありますが、「知」の欺瞞は、文理を問わず、すべての学生が知っておくべき危険です。その基本文献であるソーカル・ブリクモン『「知」の欺瞞』が文庫化されました。文庫の割に価格が高い気がしますが、飲み会に行ってお酒を2,3杯飲む程度だと思えば我慢できます。それだけの価値は十分にあります。

「難しくて読んでもよくわからない本」に出会うことがよくあります。そのとき、「知」の欺瞞について知らない学生は、「わからないのは自分がバカあるいは無知だから」だと思ってしまうかもしれません。同じ著者の別の本を買って読んだりして、なんとか理解しようと努めるマジメな学生もいるでしょう。

しかし、「わからないのは著者がバカだから」ということが意外に多くあるのです。難しそうな言葉を散りばめただけの、学問的には何の価値もない文章が、学術雑誌に掲載されたり、書籍として出版されることがあることを、大学生は知っておくべきです(大学生に限らず、ではありますが)。『「知」の欺瞞』で紹介されているのは、そのもっとも有名な例である、ソーカル事件です。事件自体についてはGoogle先生に聞いてもらってもいいのですが、『「知」の欺瞞』はその基本文献なので、この機会に読んだふりはできるようにしておきましょう(「読んだふり」も危険の一つです)。

4396207239あわせて読みたい

訂正が出ています。

カレシの元カノの元カレ・・・をFacebookは知ってますよね


「カレシ、カレシの元カノ、カレシの元カノの元カレ・・・」というちょっと怖い広告がかつてありました。

あの広告が警告していたエイズは怖い病気ですが、もし、もっと怖い、たとえば、潜伏期間は人それぞれ、発症前に治療すれば100%治るが、発症すれば3日で死ぬ、そんな性感染症が発見されたら、すぐに検査を受けたくなりますよね。でも、(未だ収束していない)福島原発事故発生当時のような、「パニックを恐れる」エリートパニックによって、その事実が市民に知らされたときにはもう手遅れ、ということは十分あり得ます。

患者にインタビューしながら感染経路を調べて・・・なんてことをしてもやはり手遅れになるでしょう。

そこで、Facebookを使えないかなあ、とか思うわけです。Facebookでは、ユーザが自分で編集できる基本データに「恋愛ステータス」なる項目があって、交際相手を自己申告できるようになっています。自分の友人の登録状況は、次のアプリで一覧表示できます。

Friends Partner

表示させるには、交際ステータスへのアクセス許可が必要です。下のようなページが表示されたら、「インストール」をクリックしてください(http://www.facebook.com/settings/?tab=applicationsでアンインストールできます)。

私の友人たちの登録状況はこんな感じです。あまり登録していませんね(ウェブブラウザでなら友人以外の情報も見られるのですが、このアプリが利用しているAPIでは自分と友人の情報しか見られません)。

この交際相手、もちろん他人から見られるのは現在の相手だけですが、Facebookのサーバに、過去の相手が保存されていたら面白いですね(情報漏洩を恐れてちゃんと消しておくのが正しいポリシーでしょうが)。

最初に述べたような怖い病気が発生したときに、Facebookはその情報を公開するのだろうか、と思うのです。「公開する」と宣言すれば、人は将来の保険と思って登録するようになるでしょうか。「公開しない」と宣言すれば、情報漏洩を恐れる人も安心して登録するようになるでしょうか。あるいは逆の結果に?

「そういう病気が見つかって初めて過去の恋人たちを登録できるようなフォームが作られるだろうから今は何もしない」という人はまったく冷静ですばらしいです。そのペースで対応できる病気ならよいのですが。

Googleはそのミッションを変更する時期に来ているのでは?


Googleの現在のミッションは、

世界中の情報をアクセス可能にし、人々の役に立てる

だとされています。しかし、この「世界中の情報」に「Googleの検索アルゴリズムの詳細」は含まれていません。「Googleで検索して出てこないものは存在していないに等しい」と言われる時代、Googleのアルゴリズムは世界の成り立ちを決めていると言ってもいい時代です。検索結果に「Googleのアルゴリズム」が含まれていないのなら、それは「世界中の情報」とは呼べないでしょう。近いうちに出てくるということもないでしょう。ミッションはインポッシブルなのです。

Googleも営利企業ですから、自社の優位を保つために秘密を持つのはかまいません。そのこと自体を邪悪だとも思いません。でも、実現する気のないミッションを掲げたままにしておくのはいけません。邪悪です。

ついでに言うと、「アルゴリズムの詳細を公開するとチートされる」という言い方もどうかと思います。単純に「自社の優位を保つため」でいいと思いますが、それでは足りないというなら、せめて「世界中から優秀な人材を集めているが、公開してもチートされないようなアルゴリズムはまだ思いついていない」くらいにしておいてほしいものです。アルゴリズムとデータが分離できないような方式とか、いろいろやりようはあると思うのでがんばってください。

4484111160こんなことを考えたのは、レヴィ『グーグル ネット覇者の真実』(阪急コミュニケーションズ, 2011)を読んだからです。そこで紹介されていたジャック・ロマノス(サイモン&シェスターの元CEO)の発言が、最近のGoogleをよく表していると思います。

「ひたすら理想を追求しているかのような態度を取り、世界の知を拡大することだけが目的なのだと言うくせに、次の瞬間には、自分たちの流儀を受け入れなければ、今回の話はなかったことにすると言う」(p.565)

こんなことを言われない古き良きGoogleが私は好きでした。

http://rickwebb.tumblr.com/post/14467269283/god-i-am-getting-so-fed-up-with-google-this-is

グーグル検索の変化–問われる検索結果の関連性

関数の値の補間方法


学生向け

「関数の値を補間するプログラムで遊ばせたら、補間のアルゴリズムが複雑だったせいか、伝えたかったことがまったく伝わらなかった」という話を聞いたので、ちょっとやってみました。

Manipulate[
 f = Function[{x}, 1 + Sin[a x]];
 start = -Pi;
 end = Pi;

 (*サンプリング*)
 samples = Table[{x, f[x]}, {x, start, end, 2 (end - start)/n}];

 (*補間*)
 g = Interpolation@samples;

 (*描画*)
 plot1 = Plot[f[ x], {x, start, end}, PlotLabel -> "Original"];
 plot2 = ListPlot[samples, PlotLabel -> "Samples"];
 plot3 = Plot[g[x], {x, start, end}, PlotLabel -> "Interpolating Function"];
 GraphicsGrid[{{plot1}, {plot2}, {plot3}}],
 {a, 1, 32, 1},
 {{n, 32}, 1, 64, 1}]

CDF Playerがインストールされていれば実際に動かして試せます。UMMでも動きます。

上から、オリジナルの関数・サンプリングデータ・補間結果です。

要は「サンプルをたくさん取ればいいってもんじゃないよ」という話で、標本化定理とかにつなげたかったのでしょう。そういう場合はMathematicaでやるのが簡単です。「補間」自体について学ばせたいときは、Javaのような低レベルな言語を使ってもいいでしょう。

「ポスドクからポストポスドクへ」


私は芥川賞を純文学の新人賞だと思っているので、すでにSF作家として広く認知されている円城塔さんが受賞したのにはちょっと不思議な感じがしました。まあ、円城塔さんの書きたいことを表現する方法として「SF」が最適かどうかはちょっと疑問があるので(SFの枠組みを広げているという言い方もできますが)、この受賞を機に読者層が広がって、もっとぴったりした方法をが見つかるかも、という期待はしています(僭越ながら)。

不可解な落選が過去にあったので、この受賞でみんなスッキリという効果もあるでしょうか(村上龍さんは選考会を欠席したそうですが)。

4150503648「円城塔」という名前の由来である、金子邦彦『カオスの紡ぐ夢の中で』が読まれて、金子さんの研究分野の周辺領域に興味を持つ人が増えると楽しいかも、と思っていたら、先に注目を浴びたのは、日本物理學會誌にかつて掲載された「ポスドクからポストポスドクへ」でした。あらあら。

アカデミズム版「政策より政局」みたいな話ではありますが、単純に言えば、

  • 若手の大学教員や研究者の一部、ポスドクと呼ばれる立場の人は「ワーキングプア」になっている。助手や助教はプアではないが、数年という短い任期が過ぎれば追い払われる立場にいる
  • (准)教授になってから時間が経った人の一部は、まともに教育・研究できないのはもちろん、もし首になったら再就職なんてとてもできない「使えないヤツ」になっている

ということなのですが、このような現実をこの芥川賞騒ぎの中で初めて知ったという大学生ももしかしたらいるかもしれません。大学院生がそうだととてもまずいでしょうね(あとで「自己責任」と言われるから気をつけて!)。

「ふだん偉そうにしている先生が実は・・・」ということを、時間的金銭的投資をしている学生たちはちゃんと知っておくといいでしょう。もちろん、それによって大学教員の権威は揺らぐわけですが、それくらいのことで話を聞いてもらえなくなる教員は、どうせたいしたことはないのです(ブーメランってやつです)。

『カオスの紡ぐ夢の中で』のほうが、やっぱり面白いですね。

はてなブックマークAtomAPIをOAuthで利用する方法(Java, scribe-java版)


はてなブックマークAtomAPIの使い方を紹介します。2010年末にOAuthをサポートしたので、ここでもそれを試します。言語はJava、ライブラリはscribe-javaを使います。前にoauth-signpostを使う方法を紹介しましたが、こちらのほうがおそらく簡単です(準備のためにクラスを1つ余計に作らなければなりませんが)。

アプリケーションの登録

アプリケーション登録ページでアプリケーションを登録し、Consumer keyとConsumer secretを取得します。

ライブラリの準備

4627847327scribe-バージョン番号.jarとcommons-codec-バージョン番号.jarを使えるようにします(このあたりの詳細については、拙著『Webアプリケーション』などを参照してください)。

APIを定義するクラス

TwitterのようなメジャーなAPIはあらかじめscribe-javaで定義されているので(一覧)、後のコードで「TwitterApi.class」などと書くだけでいいのですが、はてなはそこまでメジャーでもないようで、APIを定義するクラスを自分で作らなければなりません。

import org.scribe.builder.api.*;
import org.scribe.model.*;
import org.scribe.utils.OAuthEncoder;

public class HatenaApi extends DefaultApi10a {

  @Override
  public String getAccessTokenEndpoint() {
    return "https://www.hatena.com/oauth/token";
  }

  @Override
  public String getRequestTokenEndpoint() {
    String scope = "?scope=read_public%2Cwrite_public%2Cread_private%2Cwrite_private";
    return "https://www.hatena.com/oauth/initiate" + scope;
  }

  @Override
  public String getAuthorizationUrl(Token requestToken) {
    return String.format("https://www.hatena.ne.jp/oauth/authorize?oauth_token=%s",
            OAuthEncoder.encode(requestToken.getToken()));
  }
}

TwitterのOAuthと違うのは、どのような権利を求めるかを「score=...」という形で書いておくところです。

Access tokenとToken secretの取得

OAuthでユーザの権利を譲り受けるためのAccess tokenとToken secretを取得します。ここで紹介するコンソール上で行う方法の他に、ブラウザ上でリダイレクトを使う方法もありますが、両者の違いはTwitterの場合などと同じなので、Twitterでの方法を見れば、リダイレクトを使う方法も実現できるでしょう。

先に取得したConsumer keyとConsumer secretを下のコードに埋め込んでください。

import java.io.*;
import org.scribe.builder.*;
import org.scribe.model.*;
import org.scribe.oauth.*;

public class ScribeTokenCreator {

  public static void main(String[] args) throws Exception {
    // プロキシサーバの設定
    //System.setProperty("http.proxyHost", "proxy.example.net");
    //System.setProperty("http.proxyPort", "3128");
    //System.setProperty("https.proxyHost", "proxy.example.net");
    //System.setProperty("https.proxyPort", "3128");

    String consumerKey = ***** Consumer key *****;
    String consumerSecret = ***** Consumer secret *****;
    OAuthService service = new ServiceBuilder().provider(HatenaApi.class).apiKey(consumerKey).apiSecret(consumerSecret).build();

    Token requestToken = service.getRequestToken();
    //System.out.println(requestToken.getRawResponse());
    String authUrl = service.getAuthorizationUrl(requestToken);
    System.out.println("このURLにアクセスし、表示されるPINを入力してください。");
    System.out.println(authUrl);
    System.out.print("PIN:");

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String pin = br.readLine();

    Token accessToken = service.getAccessToken(requestToken, new Verifier(pin));
    System.out.println("Access token: " + accessToken.getToken());
    System.out.println("Token secret: " + accessToken.getSecret());
  }
}

上のコード実行するとURLが表示されるので、そのURLにブラウザでアクセスしてください。下のようなページが表示されます。scoreの記述によって内容は変わります。この例では、公開・非公開の両方の情報への読み書き権限を要求しています。

OAuthで許可を求められているところ

「許可」をクリックすると下のようなページが表示されるので、表示された文字列を先のコードからのプロンプトで入力してください。Access tokenとToken secretが得られます。

「許可」をクリックした結果

ブックマーク

譲り受けた権利を使って「http://b.hatena.ne.jp/atom/post」にURLをPOSTすれば、そのURLをはてなブックマークに登録できます。例として「http://www.google.com/」を登録するコードは次のようになります。先に取得したConsumer keyとConsumer secret、Access token、Token secretを埋め込んで実行してください。

import org.scribe.builder.*;
import org.scribe.model.*;
import org.scribe.oauth.*;

public class ScribeBookmark {

  public static void main(String[] args) throws Exception {
    // プロキシサーバの設定
    //System.setProperty("http.proxyHost", "proxy.example.net");
    //System.setProperty("http.proxyPort", "3128");

    // これはユーザによらない
    String consumerKey = ***** Consumer key *****;
    String consumerSecret = ***** Consumer secret *****;
    OAuthService service = new ServiceBuilder().provider(HatenaApi.class).apiKey(consumerKey).apiSecret(consumerSecret).build();

    // これはユーザごとに異なる
    Token accessToken = new Token(
            ***** Access token *****,
            ***** Token secret *****);

    // ターゲット
    String target = "http://www.google.com/";
    String xml = String.format("<entry xmlns='http://purl.org/atom/ns#'>"
            + "<title>dummy</title>"
            + "<link rel='related' type='text/html' href='%s' />"
            + "<summary type='text/plain'></summary>"
            + "</entry>",
            target);

    // HTTPリクエスト(POST)
    OAuthRequest request = new OAuthRequest(Verb.POST, "http://b.hatena.ne.jp/atom/post");
    request.addHeader("Content-Type", "application/octed-stream");
    service.signRequest(accessToken, request);
    request.addPayload(xml);
    Response response = request.send();

    // 結果の表示
    System.out.println(response.getCode());
    System.out.println(response.getBody());
  }
}

Twitterの場合との違うのは、リクエストボディにXMLを書かなければならないこと、それを送信するためにContent-Typeをapplication/octed-streamにしておくことです。

ブックマークの編集

ブックマークを編集したいときは、上記のPOSTのレスポンス・ヘッダ (Location) に対して、GETで現状を取得し、PUTで新しいデータを送信します。例として、現在のコメントに「♡」を追記するコードは次のようになります(javax.xml.parsers.*とorg.w3c.dom.*、org.xml.sax.*をインポートしておく必要があります)。タイトルははてな全体で共有されるので、よほどの理由がない限りは変更しない方がいいでしょう。

// 編集のためのURL
String editUrl = response.getHeader("Location");

// 現コメントの取得
// HTTPリクエスト(GET)
request = new OAuthRequest(Verb.GET, editUrl);
service.signRequest(accessToken, request);
response = request.send();
System.out.println(response.getCode());

// 結果(XML)の処理
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(response.getStream()));
String comment = doc.getElementsByTagName("summary").item(0).getTextContent();

// コメントの更新(「♡」を付加する)
String newComment = comment + "♡";
xml = String.format("<entry xmlns='http://purl.org/atom/ns#'>"
        //+ "<title>Google 2</title>"
        + "<summary type='text/plain'>%s</summary>"
        + "</entry>",
        newComment);

// HTTPリクエスト(PUT)
request = new OAuthRequest(Verb.PUT, editUrl);
request.addHeader("Content-Type", "application/octed-stream");
service.signRequest(accessToken, request);
request.addPayload(xml);
response = request.send();

// 結果の表示
System.out.println(response.getCode());
System.out.println(response.getBody());

補足

アプリケーションに与えた許可を取り消したいときは、「Myはてな」→「ユーザ設定」→「外部アプリケーション認証」→「外部のアプリケーションから、はてなのサービスを利用する」の順にクリックしてください。「https://www.hatena.ne.jp/はてなID/config/auth/provider」にアクセスしてもいいでしょう。