TopCoder Arena plugin moj

mojというTopCoder Arenaのプラグインの紹介です。日本語でこれについて書いてある情報が無かったので書きました。因みにこの記事の内容の殆どは次のフォーラムの引用です。
TopCoder Forums moj (TZTester modification, new features)

mojについて

SRMのソースコードのスケルトンを生成するときに賢いテストコードを挿入します。問題に提示されている全テストケースを瞬時に正確に判定でき、時間を節約できます。
mojはTZTesterの修正版です。TZTester他のプラグインについてはぐぐってください。
機能要約:

  • 出力がかわいくなる
  • doubleの誤差1e-9まで許容
  • 簡単にカスタムテストを作れる
  • Javaと切り替え可能
  • 実行時間測定

他に特筆すべきこと:

  • gcc,VCに対応
  • テストコードがclassの外にあり邪魔にならない

出力はこんな感じにかわいくなります。

Test Case #0... PASSED (relative error 5.44969e-014)
Test Case #1... PASSED (relative error 5.68462e-012)
Test Case #2... PASSED (relative error 4.05691e-008)
Test Case #3... PASSED

使い方

フォーラムの “Download moj” のリンクからzipを落とします。CodeProcessorとFileEditも同梱されています。
基本的にmoj_instructions.txtに説明が書かれていますが、周りのプラグインの入れ方は先程のようにぐぐったほうが詳しく画像付きで説明されていたりするので、その方が間違いもなく楽です。TZTesterとの差分は、次のように読み替えるだけです。

  • TZTester.jar → moj.jar
  • tangentz.TZTester → moj.moj

既に他のプラグインを入れている方は、次の箇所を更新してください。

  • Common ClassPath
  • CodeProcessor Configuration > CodeProcessor Script
  • CodeProcessor Configuration > FileEdit Configuration > Code Template

雛形はzipにcpp,javaファイルがあるので、それに習って書き換えます。

LV拡張とext3オンラインリサイズ

初めてLVの容量を増やしました。調べてみるとext3はオンラインリサイズが可能とのこと。でも頻りに言われるext2onlineというコマンドが無い、パッケージを探しても見当たりません。おかしいなと思っているとresize2fsでオンラインリサイズ出来るらしいです。

$ sudo lvextend -tvL +10G /dev/mapper/VG-home # 実行内容だけ確認
...
$ sudo lvextend -L +10G /dev/mapper/VG-home
$ sudo resize2fs /dev/mapper/VG-home
resize2fs 1.41.11 (14-Mar-2010)
Filesystem at /dev/mapper/VG-home is mounted on /home; on-line resizing required
old desc_blocks = 1, new_desc_blocks = 2
Performing an on-line resize of /dev/mapper/VG-home to 5242880 (4k) blocks.
The filesystem on /dev/mapper/VG-home is now 5242880 blocks long.

$ df -h
Filesystem            Size  Used Avail Use% マウント位置
...
/dev/mapper/VG-home    20G  3.0G   16G  17% /home
...

あんまり細目に拡張するとLVが断片的に配置されそうな気がします。何度も弄った後で再配置するとかできるんですかね。

余談ですが、最近のHDDは容量が非常にでかくてうちの鯖でも1TB積んでいます。そんなに一気に使う事もないのでLVMを使っていますが、それらしい活用はしていません。スナップショットとか使えるらしいけどまだ使ったことがないです。というか有効な活用が思いつきません。ユーザランドのバックアップならvfs下で取った方が都合がいいと思います。システムのスナップなら/homeとか以外全体の同期を取りたいんですが、生憎/etc,/bin,/sbinらへんはroot partitionとしてLVMの外にあります。だからといって今更物理パーティションの構成変えてinitrdを作るのもまた手間です。そもそもGentoo使うなって話になるかもしれませんが、portageの魔力に一度絡め取られると他のディストリビューションは非常に使い難く感じてしまうのです。

Go言語の並列性と実行速度

Go言語の並列性については次を参照。並列性

測定にはそれなりに時間がかかる処理で並列化出来る問題が必要です。ということで「任意の大きさのライツアウトの答えを求める」プログラムにしました。
入力は整数二つ横幅と縦幅、出力は0,1で押すボタン。範囲は矩形で初期状態は全点灯。 アルゴリズムは最上段を全通り試して二段目以降は上段から確定していって、全消灯できたときそれを解とします。全通りの解を全て求めてもいいけど、話の都合上見つかった1つの特解のみ出力します。
GoとC++/OpenMPで実装しました。両方取り敢えず並列化しています。

package main

import "fmt"
import "runtime"

const NCPU = 4 // 実行するスレッド数

func main() {
	runtime.GOMAXPROCS(NCPU) // 今後いずれ不要になる
	var w, h uint
	fmt.Scan(&w, &h)

	var m uint64 = 1 << w - 1
	cans := make(chan []uint64)
	go func() {
		for i := uint64(0); i <= m; i++ {
			go func(p uint64) { // 最上段の値毎の処理をgoroutineに
				d := make([]uint64, h)
				d[0] = p
				f := p << 1 ^ p ^ p >> 1
				for i := uint(1); i < h; i++ {
					d[i] = ^f & m
					f = d[i-1] ^ d[i] << 1 ^ d[i] ^ d[i] >> 1
				}
				if f & m == m {
					cans <- d // cansはクロージャ
				}
			}(i)
		}
	}()

	format := fmt.Sprintf("%%0%vb\n", w)
	for _, e := range <- cans {
		fmt.Printf(format, e)
	}
}
#include <iostream>
#include <vector>
#include <bitset>
#include <omp.h>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;

int main()
{
	size_t w, h;
	cin >> w >> h;

	vector<ull> ans;
	ull m = (1ull << w) - 1;
	#pragma omp parallel for schedule(dynamic, 1)
	for(ll p = 0; p <= m; ++p) // omp forの要求でsigned
	{
		if(!ans.empty()) continue; // omp parallel中はbreak不可
		vector<ull> d(h, 0);
		d[0] = p;
		ull f = p << 1 ^ p ^ p >> 1;
		for(size_t i = 1; i < h; ++i)
		{
			d[i] = ~f & m;
			f = d[i-1] ^ d[i] << 1 ^ d[i] ^ d[i] >> 1;
		}
		if((f & m) == m)
			#pragma omp critical
			ans = d;
	}

	for(size_t i = 0; i < h; ++i)
		cout << bitset<64>(ans[i]).to_string().substr(64ull - w) << endl;

	return 0;
}

goroutine間では共有変数は極力使用せず、チャネルを使って通信します。L14でcansは解を得るためのチャネルです。L33で解を求めて処理をブロックします。またL28でiをクロージャにしないのは参照型だからです→詳しく。 L15をgoする理由は、mが大きい時cansを要求するまでに時間が掛かるのを防ぐためです。もしgoしないと、L17のgoroutineがmだけ大量に生成されます。またgoしておくことでcansの要求に応じてL17の方が優先的に実行され、goroutineが減ったときに勝手に増やしてくれるようになります。実行中のメモリ使用量を見ると一目瞭然です。
mainが返るとgoroutineが残っていてもプログラムは終了します。このプログラムではcansは一回しか要求していませんが、もう一度要求すると次の解が出てきます。

2つのプログラムを実行時間を測定しました。共に4並列で、入力は20×20です。厳密な比較にはなりませんが、参考にはなると思います。因みにgcのスケジューリングは不完全だそうなので、今後改善されるかもしれません。C++のomp forでschedule(dynamic, 1)としているのは、Goのスケジュールと近似するためですが、実際はstaticの方が速いです。
Atom D510; C++:1.30[s] Go:6.88[s]
Core i7 860; C++:0.22[s] Go:2.16[s]
ざっと5~10倍程度の差でしょうか。まあ十分速い部類だと思いますが、どうでしょう?w
改善の余地もあるそうなので今後にも期待したいです。

Go言語使いたい

周知のGo言語、Google社員によって作られた変だけど実用的な言語。と思ってます。公式サイトに十分に情報が乗っているので参考書には困りません。英語が難しいなら日本語訳している情報サイトもあります。プログラミング言語Goの情報サイト
これからちょくちょく勉強して行くつもりです。Go言語はアルゴリズムの記述や速度が必要なWebシステムなんかに持って来いな気がします。因みにgcではC++とリンク不可で、Cのみ可だそうです(gccgoは両方可)。尚、GoからC++に、C++からGoに引っ張るのは”まだ”できないそうです。wktk

主観的に特徴を書くとこんな感じです。

  • ステートメントが少ない
  • 構文の制約が厳しい(表現の統一)
  • ビルトイン型/関数が必要十分
  • 動的言語のような柔軟な記述
  • 独特で便利な機構
  • コンパイルが超高速
  • 実行も速い
  • 並列処理が簡単
  • 豊富な標準パッケージ

正確に知りたい方は他を参照してください。

処理系はgo-windowsもあるけど何かうまい使い方が分からないから今はlinux上のgcで試してみました。

Codeforces #23 A. Youre Given a String…
文字列の中から2回以上出現する部分文字列の最大長を求める。

package main
import "fmt"
func main() {
	var s string
	fmt.Scanln(&s)
	g := 0
	for i := 0; i < len(s); i++ {
		for j := i+1; j < len(s); j++ {
			k := 0
			for j+k < len(s) && s[i+k] == s[j+k] { k++ }
			if g < k { g = k }
		}
	}
	fmt.Println(g)
}

特に変な機構も使ってないので構文しか分かりませんが。。次に比較用にC++で、

#include <iostream>
#include <string>
using namespace std;
int main()
{
	string s;
	cin >> s;
	int g = 0;
	for(int i = 0; i < s.size(); ++i)
		for(int j = i+1; j < s.size(); ++j)
		{
			int k = 0;
			while(j+k < s.size() && s[i+k] == s[j+k]) k++;
			g = max(g, k);
		}
	cout << g << endl;
	return 0;
}

IDEで書けると非常に楽しそうです。特にパースが速いなら快適な補完が利きそう。でもデバッガは難しそうですね。

PowerCommands for Visual Studio 2010

PowerCommands_Preview

PowerCommands for Visual Studio 2010というプラグインの紹介。何気に便利です。ソリューションエクスプローラから右クリックでコンソールやエクスプローラ,パスのコピーが出来るようになります。他にも色々機能がありそうだけどまあそれだけで十分便利。VS2010でしか使えないのが不思議。

使用しているとなぜかエディタで文字列を選択(反転)したときにVS自体のフォーカスが高速にチカチカと明滅(?)する謎の問題が発生。ウィンドウを強調しているわけでなく本当にフォーカスが当たったり外れたりを高速に繰り返します。もちろんエディタにマトモに文字が打てなくなります。

結果的にForumの方に解決法が載っていましたが英語でしか書いてなくてうまく検索にヒットせずちょっと間面倒でした。オプションからExtract Constantのチェックを外すと治ります。

Visual Studio Options