ボムを作る

これは再帰の打ち切りの項でも説明しました.
a(X,Y):XXXXa(sX,Y-1)などとして,a(r,10)などと使うというものです.
「不規則な色んなところを回収する」というたぐいの問題でも
場所によってはこのボムで一気に一部の領域を片づけることが出来ます.

また,「灰色マスは回収しはじめるまでは何度通ってもいい」というのも
ポイントで,正方形状の領域の一部が灰色でも,灰色マスの外側だけを先に
処理するという使い方ができます.一般的に初期位置から遠くは回収が
面倒なので,この考え方が役に立つ場面は結構あります.


a(X):pXq型の関数の利用

経路が不規則になればなるほど,2倍,3倍関数などの利用場所は
限られてきます.関数は,Xをたくさん含むほど,(上手に使えたときの
利は大きくなりえますが)適用できる箇所が少なくなりやすいのです.

ここで紹介するのは,1変数関数でしかもXを1つしか含まないものの利用法です.
このような関数は,a(X):pXqの形だけです.


いきなりですが,次のコードを少ないbyte数に書き換えることを考えましょう:

abcpqabcuvabcpqwvupqwabcupqvabcpqabcpqabcabc【44byte】
abcとpqがたくさんいるのが分かりますね.
[abc][pq][abc]uv[abc][pq]wvu[pq]w[abc]u[pq]v[abc][pq][abc][pq][abc][abc]
実は上手な短縮をすると,このコードでClear出来る経路は,24byteで書くことができます.
腕に自信がある人は,読み進める前に自力で試してみると面白いかもしれません.






さて,24byteになったでしょうか?
まずは素直な考え方を紹介しましょう.[abc]は3文字で8回,[pq]は2文字で6回現れる ので,
これらは別の文字でおく方が短くなります:

x:abc
y:pq
xyxuvxywvuywxuyvxyxyxx【4+3+22=29byte】
だいぶ減りました.さらに[xy]が4回現れているので,これを再び
文字でおけば,この方法で28byteまで減らすことができます.
x:abc
y:pq
z:xy zxuvzwvuywxuyvzzxx【4+3+3+18=28byte】
さて,上のコードを見ても,これ以上減らせる箇所はなさそうです.
たくさん出てくるものはもうまとめきったし,n倍関数も役には立たなさそうです.

しかし,実はもっと良い方法があります.x:abc, y:pqと定義した代わりに
f(X):abcXpqという関数を定義しましょう:
「abc・・・・pq」がある度にf(・・・・)にすることで4byteの得が出来ます.
[pq]が出てくるたびに直前の[abc]とペアにすることで次の手順で
どんどんコードを短縮していくことができます.

  • [abc][pq][abc]uv[abc][pq]wvu[pq]w[abc]u[pq]v[abc][pq][abc][pq][abc][abc]【8+44=52byte, 関数+初期状態】
  • f()[abc]uv[abc][pq]wvu[pq]w[abc]u[pq]v[abc][pq][abc][pq][abc][abc]【52-4=48byte】
  • f()[abc]uvf()wvu[pq]w[abc]u[pq]v[abc][pq][abc][pq][abc][abc]【48-4=44byte】
  • f()f(uvf()wvu)w[abc]u[pq]v[abc][pq][abc][pq][abc][abc]【44-4=40byte】
  • f()f(uvf()wvu)wf(u)v[abc][pq][abc][pq][abc][abc]【40-4=36byte】
  • f()f(uvf()wvu)wf(u)vf()[abc][pq][abc][abc]【36-4=32byte】
  • f()f(uvf()wvu)wf(u)vf()f()[abc][abc]【32-4=28byte】
  • 一気にさきほどのコードのbyte数に追いつきました.
    さきほどの「x:abc, y:pq」という文字のおき方では,1つをxにするたびに
    2byte, 1つをyにするたびに1byteの得でしたが,今回の関数は,1度使用できると
    4byteの得になっています.文字で置きたいもの2つをまとめて1つの文字で置いたかの
    ような効果になるためです.また,関数を定義する部分
    「f(X):abcXpq」は「x:abc, y:pq」よりも1byte余分なだけで済んでいます.


    さて,随分と縮めた

    f(X):abcXpq
    f()f(uvf()wvu)wf(u)vf()f()[abc][abc]【28byte】
    というコードですが,さらに短縮が可能です.素直な方法だと,
    全体に3度(1つはf(X)の定義)現れる[abc]を文字で置いて2byte短縮可能です:
    x:abc
    f(X):xXpq
    f()f(uvf()wvu)wf(u)vf()f()xx【26byte】
    よりよい方法は,「辿り終わった後に余分に実行しても構わない」
    ということに注目したものです.
    (逆に言えば,止まらないといけない部分では使えません).
    つまり,最後に[pq][pq]を足して考えてやればよいのです.
    このことで,[abc]の部分を新たに文字で置かずとも1文字に変形できます.
  • f(X):abcXpq
    f()f(uvf()wvu)wf(u)vf()f()[abc][abc][pq][pq]【28+4byte】
  • f(X):abcXpq
    f()f(uvf()wvu)wf(u)vf()f()[abc]f()[pq]【28byte】
  • f(X):abcXpq
    f()f(uvf()wvu)wf(u)vf()f()f(f())【24byte】
  • 予告通り,24byteに短縮することができました!


    今回は「f(X):[abc]X[pq]」という関数による短縮を紹介しましたが,
    これで全ての[abc][pq]が処理できるのは,「先頭から順に数えて,どの段階でも
    [abc]が[pq]よりもたくさんある」という状況に限られます.
    [pq]の方が少したくさんある場合などは,一部のpqについては
    この関数とは別の方法(新たに文字でおくなど)で対処する必要があります.

    なお,この「文字列を関数に置き換えて短縮していく」
    という作業は結構大変です.しかも途中で1箇所間違えてカッコが1つ足りなかったり
    しても,後から見つけるのが困難な場合が多いです.短縮を繰り返すほど
    「見ても仕組みがよく分からないコード」が出来あがっていきます.
    たくさんこの短縮を利用する場合は,こまめに実行してエラーが起きないか,
    動きは想定通りかなどの検算をやりながら作業を進めるのが良いと思います.

    今回のコードでは,いかにもたくさん反復されるもの[abc][pq]がありました.
    そんな上手くいく状況はそうそうないと思うかもしれませんが,意外と適用例は多いです.
    2例ほど紹介して終わることにしましょう.
    ちなみにどちらも別に深い意味のある動きではないです.


    戻る inserted by FC2 system