ECMAScriptの正規表現、733+ 690+ 158 119 118 (117🐌)バイト
正規表現への私の関心は、4年半以上の非活動の後に活力を取り戻したことがきっかけとなりました。そのため、単項ECMAScript正規表現と一致するより自然な数値セットと関数を探し、正規表現エンジンの改善を再開し、PCREのブラッシュアップも開始しました。
ECMAScript正規表現で数学関数を構築することの異質さに魅了されています。問題はまったく異なる視点からアプローチする必要があり、重要な洞察が得られるまで、それらがまったく解決可能かどうかは不明です。特定の問題を解決可能にするために、どの数学プロパティを使用できるかを見つける際に、より広いネットを強制的にキャストします。
階乗数のマッチングは、2014年に取り組むことすら考慮していなかった問題でした。しかし先月、私はそれができることに気づきました。
他のECMA正規表現の投稿と同様に、警告を出します。ECMAScript正規表現の単項数学問題を解決する方法を学ぶことを強くお勧めします。それは私にとって魅力的な旅であり、自分で試してみたいと思う人、特に数論に興味のある人のためにそれを台無しにしたくありません。1つ1つ解決するための連続したスポイラータグ付きの推奨される問題のリストについては、この以前の投稿を参照してください。
したがって、高度な単項正規表現の魔法を台無しにしたくない場合は、これ以上読んではいけません。この魔法を自分で理解するためにショットを撮りたい場合は、上記のリンクに記載されているECMAScript正規表現の問題を解決することから始めることを強くお勧めします。
これは私のアイデアでした:
他のほとんどの場合と同様に、この番号セットの一致に関する問題は、ECMAでは通常、ループ内の2つの変化する番号を追跡することができないことです。時にはそれらを多重化することができます(例えば、同じベースのパワーを一義的に追加できます)が、それはそれらの特性に依存します。そのため、入力番号から始めて、それを1に達するまで増分的に増加する配当で割ることはできませんでした(少なくとも私はそう思っていました)。
次に、階乗数の素因数の多様性についていくつかの研究を行い、これには公式があることを学びました。これはおそらくECMA正規表現で実装できるものです。
しばらくそれを煮詰め、その間に他のいくつかの正規表現を構築した後、階乗正規表現を書く作業を始めました。数時間かかりましたが、うまくいきました。追加のボーナスとして、アルゴリズムは一致として逆階乗を返すことができます。それを避けることもできませんでした。ECMAでの実装方法の性質上、他のことを行う前に、逆要因が何であるかを推測する必要があります。
欠点は、このアルゴリズムが非常に長い正規表現を作成したことです...しかし、651バイトの乗算正規表現で使用された技術が必要になったことを嬉しく思いました(別の方法が50バイト正規表現)。このトリックを必要とする問題が発生することを期待していました:両方の同じ基数の累乗である2つの数値をループで操作し、それらを明確に加算し、反復ごとに分離します。
しかし、このアルゴリズムの難しさと長さのために、私は(?*...)
それを実装するために(先読みの)分子先読みを使用しました。これはECMAScriptや他の主流の正規表現エンジンではなく、エンジンに実装した機能です。分子の先読み内にキャプチャがなければ、機能的には先読みと同等ですが、キャプチャを使用すると非常に強力になります。エンジンは先読みに戻り、これを使用して、入力の文字を消費せずに(後でテストするために)すべての可能性を循環する値を推測することができます。それらを使用すると、よりクリーンな実装が可能になります。(可変長後読みは、少なくとも分子先読みと同等ですが、後者はより簡単でエレガントな実装を実現する傾向があります。)
したがって、733および690バイトの長さは、実際にはソリューションのECMAScript互換の具体化を表すものではありません。そのアルゴリズムを純粋なECMAScriptに移植することは確かに可能です(その長さはかなり長くなります)が、私はそれには回りませんでした...私ははるかに単純でコンパクトなアルゴリズムを考えたので!分子の先読みなしで簡単に実装できるもの。また、非常に高速です。
この新しいものは、前のものと同様に、逆階乗を推測し、すべての可能性を循環させ、一致するかどうかをテストする必要があります。Nを2で割って、必要な作業のためのスペースを作り、ループをシードします。このループでは、3から始まり毎回増分する除数で入力を繰り返し除算します。(そのため、1!と2!はメインアルゴリズムと一致させることができず、個別に処理する必要があります。)除数は、実行中の商に追加することで追跡されます。これら2つの数値は、M!== N、実行中の商は、Mに等しくなるまでMで割り切れます。
この正規表現は、ループの最も内側の部分で変数による除算を行います。除算アルゴリズムは他の正規表現と同じです(乗算アルゴリズムに似ています):A≤Bの場合、C%A = 0およびBがB≤Cを満たす最大数である場合にのみA * B = C C%B = 0および(CB-(A-1))%(B-1)= 0です。ここで、Cは被除数、Aは除数、Bは商です。(A≥Bの場合にも同様のアルゴリズムを使用でき、AとBの比較方法がわからない場合は、1つの余分な可分性テストだけで十分です。)
ですから、ゴルフに最適化されたフィボナッチ正規表現よりも問題をさらに複雑さを減らすことができたことが大好きですが、同じベースの多重化手法が別の問題を待たなければならないことに失望してため息をつきます実際に必要なのは、これが必要ないからです。これは、私の651バイトの乗算アルゴリズムが50バイトのアルゴリズムに取って代わられたという話です。
編集:私はトリックを使用して1バイト(119→118)をドロップすることができたことによって見出さ汚れた商がより大きい又は除数に等しくなることが保証されている場合には短く分割futherできます。
これ以上苦労せずに、正規表現を次に示します。
真/偽バージョン(118バイト):
^((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\3$|^xx?$
オンラインでお試しください!
逆階乗または不一致(124バイト)を返します。
^(?=((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\3$)\3|^xx?$
オンラインでお試しください!
ECMAScript +で逆階乗または不一致を返します\K
(120バイト)でます。
^((x*)x*)(?=\1$)(?=(xxx\2)+$)((?=\2\3*(x(?!\3)xx(x*)))\6(?=\5+$)(?=((x*)(?=\5(\8*$))x)\7*$)x\9(?=x\6\3+$))*\2\K\3$|^xx?$
そしてコメント付きのフリースペース版:
^
(?= # Remove this lookahead and the \3 following it, while
# preserving its contents unchanged, to get a 119 byte
# regex that only returns match / no-match.
((x*)x*)(?=\1$) # Assert that tail is even; \1 = tail / 2;
# \2 = (conjectured N for which tail == N!)-3; tail = \1
(?=(xxx\2)+$) # \3 = \2+3 == N; Assert that tail is divisible by \3
# The loop is seeded: X = \1; I = 3; tail = X + I-3
(
(?=\2\3*(x(?!\3)xx(x*))) # \5 = I; \6 = I-3; Assert that \5 <= \3
\6 # tail = X
(?=\5+$) # Assert that tail is divisible by \5
(?=
( # \7 = tail / \5
(x*) # \8 = \7-1
(?=\5(\8*$)) # \9 = tool for making tail = \5\8
x
)
\7*$
)
x\9 # Prepare the next iteration of the loop: X = \7; I += 1;
# tail = X + I-3
(?=x\6\3+$) # Assert that \7 is divisible by \3
)*
\2\3$
)
\3 # Return N, the inverse factorial, as a match
|
^xx?$ # Match 1 and 2, which the main algorithm can't handle
これらの正規表現の私のゴルフ最適化の完全な歴史はgithubにあります:
階乗数を照合するための正規表現-多重度比較法、分子lookahead.txt
を一致させるための正規表現.txt(上記のもの)
((x*)x*)
に変更できることに注意してください。正しい機能を失うことなく((x*)+)
、サイズを1バイト(117バイトまで)減らしますが、正規表現は指数関数的にスローダウンします。ただし、このトリックはPCREおよび.NETでは機能しますが、ECMAScriptでは機能しません。これは、ループで長さゼロの一致に遭遇したときの動作のためです。((x+)+)
はECMAScriptで機能しますが、n=3!、これは正規表現に違反します!、\2
の値をキャプチャするために必要3−3=0(1-インデックスを作成する正規表現を変更するこのゴルフ利益を元に戻すことになります)。
.NET正規表現エンジンはECMAScriptモードでこの動作をエミュレートしないため、117バイトの正規表現が機能します。
オンラインでお試しください! (.NET正規表現エンジン+ ECMAScriptエミュレーションを使用した指数関数的スローダウンバージョン)
1
ますか?