Comment detail
必ず解ける迷路 (Nested Flatten)1024x1024を出力したら、Pen4 2.4GHzで 10秒弱でした。
棒倒し法も空間計算量がO(n)ですね。 すみません勘違いしてました。 私の考えたアルゴリズムと同じです。 棒倒し法の1行バッファ版と言えるようです。 以下、n=5, m=5の迷路の例でアルゴリズムを解説します。 ・最初の行 S■■■■■■■■■■■← まずバッファを用意して、全て壁にします。 ・1行目(奇数行) S■■■■■■■■■■■ 1■★■★■★■★■★■← ★の箇所に穴を開けます。 S■■■■■■■■■■■ 1■A■B■C■D■E■← 穴を開けた場所をA~Eとします。 S■■■■■■■■■■■ 1■A★B★C★D★E■← ★の箇所に穴を開けるかどうかを乱数で決定します。 S■■■■■■■■■■■ 1■A▲B★C★D★E■← たとえば▲に穴を開ける場合。 S■■■■■■■■■■■ 1■AAA★C★D★E■← AとBの領域が結合されるため、B領域をA領域に統合します。 仮にB領域に飛び地があっても、それもA領域に統合します。 また、★の左右が同じ領域だった場合は、穴を開けてはいけません。 ループ経路が出来てしまうからです。 S■■■■■■■■■■■ 1■AAAAAAA■E■← この調子で穴を開けていきます。 全ての★を乱数で決定したら1行確定です。 ・2行目(偶数行) S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A☆A☆A☆A★E■← ☆の箇所を壁にします。 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■A■A■A■E■← ★の箇所はすでに壁なので特に何もしません。 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■☆■☆■☆■☆■E■← ☆の箇所を壁にするかどうかを乱数で決定します。 ただし、残り1つになっているEの列は壁になってはいけません。 また、4つの☆は3つまでは壁にしてもよいですが、 最後のひとつは壁にしてしてはいけません。 4つとも壁にしてしまうと S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■■■■■■■■■E■← こうなって、Aの領域に侵入できなくなってしまうからです。 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■← 乱数を使って結局2箇所を壁にすることになりました。 全ての☆を乱数で決定したら1行確定です。 ・3行目(奇数行) S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■ 3■A■★■A■★■E■← 1行目と同じく、★の箇所に穴を開けてB,C領域と します。 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■ 3■A★B★A★C★E■← ★の箇所に穴を開けるかどうかを乱数で決定します。 あとは同じように、 偶数行→奇数行→偶数行→・・・ と繰り返します。 ・9行目(最後の一つ前の行&奇数行) S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■ 3■AAA■A■CCC■ 4■■■A■■■■■C■ 5■B■A■DDD■C■ 6■B■A■D■D■C■ 7■AAA■C■CCC■ 8■A■A■C■C■C■ 9■A■A■C■C■C■← 最後の一つ前の行は辻褄あわせが必要です。 具体的には、すべての領域を統合しなければなりません。 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■ 3■AAA■A■CCC■ 4■■■A■■■■■C■ 5■B■A■DDD■C■ 6■B■A■D■D■C■ 7■AAA■C■CCC■ 8■A■A■C■C■C■ 9■A■A★C■C■C■← 今回は、たまたまA領域とC領域しかないため、 ★の箇所の壁を取り除けばOKです。 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■ 3■AAA■A■CCC■ 4■■■A■■■■■C■ 5■B■A■DDD■C■ 6■B■A■D■D■C■ 7■AAA■C■CCC■ 8■A■A■C■C■C■ 9■A★A☆C★C★C■ ★☆の箇所に穴を開けるか乱数で決定します。 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■ 3■AAA■A■CCC■ 4■■■A■■■■■C■ 5■B■A■DDD■C■ 6■B■A■D■D■C■ 7■AAA■C■CCC■ 8■A■A■C■C■C■ 9■A■AAA★A★A■ ☆確定まで。 この例では、☆以外はたまたま左右が同じ領域なので、 選択肢がありません。 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■ 3■AAA■A■CCC■ 4■■■A■■■■■C■ 5■B■A■DDD■C■ 6■B■A■D■D■C■ 7■AAA■C■CCC■ 8■A■A■C■C■C■ 9■A■AAA■A■A■ A領域とC領域が統合されたため、 残りの★も穴をあけられなくなりました。 ・最後の行 S■■■■■■■■■■■ 1■AAAAAAA■E■ 2■A■■■A■■■E■ 3■AAA■A■CCC■ 4■■■A■■■■■C■ 5■B■A■DDD■C■ 6■B■A■D■D■C■ 7■AAA■C■CCC■ 8■A■A■C■C■C■ 9■A■AAA■A■A■ E■■■■■■■■■■■ 全て壁で埋めます。 これでおしまいです。 私の書いたJavaコードだと出力無しで1372ミリ秒でした。 PentiumM 2.1GHz java version "1.6.0_02" Java(TM) SE Runtime Environment (build 1.6.0_02-b05) Java HotSpot(TM) Server VM (build 1.6.0_02-b05, mixed mode)
このアルゴリズムで組んでみました。Pentium2 266MHzで30秒です。きっともっと最適化の余地があることでしょう。。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | #include <vector>
#include <fstream>
#include <algorithm>
#include <cstdlib> // random, srand
#include <cassert> // assert
#include <ctime> // time
#define REP(i, b, e) for (size_t i = b; i < e; ++i)
#define STEP(i, b, e, step) for (size_t i = b; i < e; i += step)
const size_t wall = static_cast<size_t>(-1);
bool random()
{
return std::rand() < RAND_MAX / 2;
}
void print(std::ostream& out, const std::vector<size_t>& map)
{
REP(i, 0, map.size())
{
out << (map[i] == wall ? "¡" : "@");
}
out << std::endl;
}
void create_maze(const char* path, size_t n, size_t m)
{
assert(n >= 1); assert(m >= 1);
std::ofstream out(path);
std::vector<size_t> areas(n); // identifers of areas
REP(i, 0, n) areas[i] = i;
std::vector<size_t> map(2 * n + 1, wall);
print(out, map);
for (--m; ; --m)
{
STEP(i, 1, 2 * n, 2) if (map[i] == wall)
{
assert(! areas.empty());
map[i] = areas.back();
areas.pop_back();
}
STEP(i, 2, 2 * n, 2) if (map[i - 1] != map[i + 1] && (m == 0 || random()))
{
const size_t old_area = map[i - 1];
const size_t new_area = map[i + 1];
areas.push_back(old_area);
std::replace(map.begin(), map.end(), old_area, new_area); // caution! passed by reference
map[i] = new_area;
}
print(out, map);
if (m == 0)
{
break;
}
STEP(i, 2, 2 * n, 2)
{
map[i] = wall;
}
std::vector<size_t> count(n, 0);
STEP(i, 1, 2 * n, 2)
{
++count[map[i]];
}
STEP(i, 1, 2 * n, 2) if (count[map[i]] >= 2 && random())
{
--count[map[i]]; map[i] = wall;
}
print(out, map);
}
print(out, std::vector<size_t>(2 * n + 1, wall));
}
int main()
{
std::srand(std::time(NULL));
create_maze("result.txt", 1024, 1024);
}
|
最適化の余地があるというのは、アルゴリズムにではなく、私のコードに、です。念のため(^^;
C++, Pentium2 266MHzで30秒ですか。 私の使ったPentiumM 2100MHzとの性能差を約10倍とすると。 (クロック差8倍とメモリとかキャッシュとかで+2倍) 13秒ぐらいで生成できそうなんですが・・・。 あ、そういえば速度を計ったときには出力コードをコメント にしてました。 純粋にバッファ操作の時間だけ計測すると何秒ぐらいになり ますか?
1 2 3 4 5 6 7 8 9 10 | // こんな感じで。
void print(std::ostream& out, const std::vector<size_t>& map)
{
REP(i, 0, map.size())
{
// out << (map[i] == wall ? "■" : " ");
}
// out << std::endl;
}
|
23秒でした。ていうか、私のコード、日本語部分が化けてますね。すみません(汗)
#日ごろ秀丸+欧文フォントで組んでるので、そのままコピーペーストするとこうなる
> 23秒でした。
(・3・)アルェー?
速くなったけど13秒には及ばないですね。
いくつもあるループを統合したら改善するのかも。
-------------
STEP(x) {
A
}
STEP(x) {
B
}
-------------
↓
-------------
STEP(x) {
A
B
}
-------------
> #日ごろ秀丸+欧文フォントで組んでるので、
> そのままコピーペーストするとこうなる
シブイ!
Courierでしょうか?
私はプロポーショナルフォント(英字Tahoma + 日本語MSUIGothic)で
プログラムしてます。
周りからは変態呼ばわりされてます(笑)
>速くなったけど13秒には及ばないですね。
うーん、コンパイラが違うとか。g++ -O2 でコンパイルしました。まあ、色々古いパソコンなので他にも原因があるかも。
>シブイ!
>Courierでしょうか?
Courier Newです(笑)
>私はプロポーショナルフォント(英字Tahoma + 日本語MSUIGothic)で
>プログラムしてます。
>周りからは変態呼ばわりされてます(笑)
・・・変態(ぼそ)(^^;
うーん、コンパイラが違うとか。g++ -O2 でコンパイルしました。まあ、色々古いパソコンなので他にも原因があるかも。
>シブイ!
>Courierでしょうか?
Courier Newです(笑)
>私はプロポーショナルフォント(英字Tahoma + 日本語MSUIGothic)で
>プログラムしてます。
>周りからは変態呼ばわりされてます(笑)
・・・変態(ぼそ)(^^;





horiuchi
#5294()
[
Java
]
Rating1/1=1.00
Rating1/1=1.00-0+
1 reply [ reply ]