第4回社内Hadoop勉強会が開催されました。
先週土曜は社内で不定期開催のHadoop勉強会が開催されました。講師はいつもの@choconeco。今回はHadoopからちょっと離れて「アルゴリズム」がテーマに扱われました。Hadoopは分散処理のアーキテクチャで、1CPUで全て処理するものと同じように実行しようとすると、処理できない、というケースが多発するとのこと。分散処理に向いたアルゴリズムを適切に選択し、Hadoopを様々なシーンで有効活用できるようにするのが狙いです。
最初は「Make10」。1〜9までの数字を1つずつ4つ使って四則計算で「10」になる計算式を探し出すプログラムを作る、というもの。例は以下。これをプログラムで自動でピックアップしていくわけです。
(1 + 3 - 2) * 5 = 10
これが相当難しい。FizzBuzzくらいの難易度で出来るかなぁと思ってたら、全然歯が立ちませんでした。一応その場でJavaで即興で書いてみたりもしたんですが、絶対にありえないと言われた1を4つ使う組み合わせが出てきちゃってアウト。
回答としては「逆ポーランド記法」という方法で書いていき、Stackを使いながら再帰を繰り返すことで全てを自動ではじき出すことが出来るそうです。
続いては「グラフ理論」。ソーシャルグラフの自動演算をHadoopにやらせるときに必要な考え方を解説してもらいました。
この日の例題は「高田馬場から池袋までを最短時間で移動するにはどうしたらよいか?」というもの。普通に考えれば「山手線で2駅でFA」なんですが、これをHadoopにやらせるには相応の考え方=アルゴリズムが必要です。…これが凄く難しかった。すみませんが、このアルゴリズムについてはまだ自分の中で消化しきれてないので解説できません。
今回はまったくHadoopに触れなかったんですが、今まであまり使って来なかった頭をしっかり使うことになり、良い刺激になったと思います。アルゴリズムに関してはまた別枠で勉強会を開催しても面白そうだなぁ。
あと、割込みでオレもプレゼンをやらせてもらいました。「Hadoopで遊ぶためのデータ集め」ってことで、Twitterから特定条件に見合うツイートを検索してCassandraに貯めておく、というサンプルを作りたかったんですが、どうしてもCassandraが動かず(何をやっても正常動作できなかった。1.0.8はバグってんじゃないかなぁ、とか勝手に思ってます。)、あえなくCassandraの部分は断念。Twitter4jを利用してsearch APIを呼び出すところまでをサラッと解説しました。以下そのソースコードです。
package jp.co.mforce.hadoopstudy; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.List; import twitter4j.Query; import twitter4j.QueryResult; import twitter4j.Tweet; import twitter4j.Twitter; import twitter4j.TwitterException; import twitter4j.TwitterFactory; public class TwitterSearch { public static final int WAIT_TIME = 1000; public static final String SEARCH_KEYWORD = "ミッションたぶんPossible"; public static void main(String[] X) { StringBuffer tweetData = new StringBuffer(); Twitter twitter = new TwitterFactory().getInstance(); Query query = new Query(SEARCH_KEYWORD); query.setRpp(100); query.setSince("2012-01-01"); query.setUntil("2012-04-07"); QueryResult result = null; int tweetCount = 0; int i = 1; while(true) { query.setPage(i); try { result = twitter.search(query); List<Tweet> list = result.getTweets(); // 検索結果0件の場合は処理終了 if(list.size() == 0) { System.out.println("================================================================================="); System.out.println("処理終了 件数:" + tweetCount); break; } else { tweetCount += list.size(); } for(Tweet tweet:list) { System.out.println(format(tweet)); tweetData.append(format(tweet)); } } catch(TwitterException ex) { ex.printStackTrace(); break; } try { Thread.sleep(WAIT_TIME); } catch (InterruptedException ex) { ex.printStackTrace(); } i++; } // 出力処理 try { File file = new File("export.txt"); PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file))); pw.println(tweetData.toString()); pw.close(); } catch(IOException ex) { ex.printStackTrace(); } } private static String format(Tweet tweet) { return String.format(">>\r\n<table><tr><td valign='top'><img src='%1$s'></td><td><span class='deco' style='font-weight:bold;'>%2$s</span></td></tr><tr><td colspan='2'><span class='deco' style='font-size:x-small;'>%3$s %4$s <a href='https://twitter.com/#!/%4$s/status/%5$s' target='_blank'>%6$s</a></span></td></tr></table>\r\n<<\r\n", tweet.getProfileImageUrl(), tweet.getText(), tweet.getSource(), tweet.getFromUser(), String.valueOf(tweet.getId()), tweet.getCreatedAt().toString() ); } }
終了後はいつものごとく打ち上げに。行きつけのフレンチ居酒屋で大騒ぎしてきました。…オレなんでこんな変な顔で写ってるんだろ?