「m」と「n」の間の自然数に一致する正規表現を生成します


8

正規表現のタイプはPCREです。

それは間のすべての自然数と一致していることを出力有効PCREは、このようなことをするプログラムを作成mし、nそして何か他のものと一致していません。先行ゼロは許可されていません。

たとえば、let mnbe 123とすると4321、プログラムはを出力します 1(2[3-9]|[3-9]\d)|[2-9]\d\d|[123]\d\d\d|4([012]\d\d|3([01]\d|2[01]))

これは正確な文字列と一致するため^$アンカーは暗黙的です。

2つのバランスをとる必要があります。

  1. 正規表現は適切なサイズである必要があります。

  2. コードは短くする必要があります。

最適化しましょう

code length in characters + 2 *regular expression length for input 123456 and 7654321

サイドノート:最短のPCRE正規表現がO(log n log log n)か何かであることを証明できるかどうか興味深いです。


1
受賞基準を定義できますか?たぶんのようなものre_size*5 + prog_size(小さい=良い)、どこre_sizeの最大であるmn5桁までは。それを行うには他にも多くの方法があります-重要なのは、答えを採点できることです。
ugoren 2013年

「mとnの間のすべての自然数に一致するように、有効なPCREを出力するプログラムを作成する」おそらく「他のすべての入力に一致しない」でしょうか。print .*いくつかの言語でいくつかのスマートなお尻を提供しないでください。
dmckee ---元モデレーターの子猫2013

負の数でもっと楽しくなったでしょう:-D
JB

if(min == 123456 && max == 7654321){print_hardcoded_regex}else{enumerate_range_and_join}
John Dvorak 2013年

回答:


7

Perl、スコア455

191文字、132正規表現の長さ

sub b{my$a=shift;$_=(@_>0&&$a.b(@_).($a-->0&&'|')).($a>=0&&($a>1
?"[0-$a]":$a));/\|/?"($_)":$_}sub a{b(@a=split//,<>-1+$x++).(@a>
1&&"|.{1,$#a}")}print'(?!0|('.a.')$)(?=('.a.'$))^\d{1,'.@a.'}$'

入力: 123456, 7654321

(?!0|((1(2(3(4(5[0-5]|[0-4])|[0-3])|[0-2])|1)|0)|.{1,5})$)(?=((7(6(5(4(3(21|1)|[0-2])|[0-3])|[0-4])|[0-5])|[0-6])|.{1,6}$))^\d{1,7}$

更新:ほとんどのパターンがなどで終わっていることに気付いたとき、これをさらに簡略化することができました\d{3}。これらは文字列の長さを強制する以外に何もしていませんでした-そして、それらはあらゆる用語で発生したため、非常に繰り返し行っていました。別の先読みを使用して「未満」条件を強制し、1)数値の最初の部分が入力を超えていないか、2)数値が入力よりも桁数が少ないかを確認することで、これを排除しました。次に、メインの正規表現は、桁数が多すぎないことを確認します。

また、「より大」の状態をチェックするための否定的な先読みというピーターテイラーのアイデアも取り入れました。

(少なくとも私にとって)この問題を簡略化するための鍵は、正規表現を2つに分割することでした:先読みにより、数値が最小値以上であることを確認し、正規表現の主要部分がそれが最大。入力が小さい場合は少しばかげていますが、入力が大きい場合はそれほど悪くありません。

注:0|最初のは、ゼロで始まるものはすべてマッチングから除外するためのものです。これは、正規表現関数の再帰的な性質のために必要です。一致の内部部分はゼロから始めることができますが、数値全体ではできません。正規表現関数は違いを区別できないので、特別なケースとしてゼロで始まる数値を除外しました。

入力: 1, 4

(?!0|(0)$)(?=([0-4]$))^\d{1,1}$

不当に長い正規表現バージョン、29文字:

print'^',join('|',<>..<>),'$'

特別なケースがあることを忘れないでください。つまり、if mが0の場合、先行ゼロがあるにもかかわらず0を許可する必要があります。
Peter Taylor

2
@PeterTaylor、私は「自然数」は正の整数を意味すると思っていました。ウィキペディアを確認すると、ゼロが自然数であるかどうかについて実際には合意がないことがわかります。この時点で、私の解決策を変更するのではなく、あいまいさから

3

JavaScript、スコア118740839

function makere(m,n){r='(';for(i=m;i<n;)r+=i+++'|';return (r+i)+')';}

これが好きかどうかは、「妥当なサイズ」の定義方法にかかっていると思います。:-)


2
これをテストするつもりはありません。私はあなたを信じています。
tomsmeding 2013年

2

Haskell 2063 + 2 * 151 = 2365

生成された正規表現の長さがO(log n log log n)であることが保証されています。

matchIntRange 12345 7654321

1(2(3(4(5[6-9]|[6-9]\d)|[5-9]\d\d)|[4-9]\d{3})|[3-9]\d{4})|[2-9]\d{5}|[1-6]\d{6}|7([0-5]\d{5}|6([0-4]\d{4}|5([0-3]\d{3}|4([012]\d\d|3([01]\d|2[01])))))

import Data.Digits

data RegEx = Range Int Int | MatchNone | All Int
            | Or RegEx RegEx | Concat [RegEx] 

alphabet = "\\d"

instance Show RegEx where
  show (Range i j)
   | i == j    = show i
   | i+1 == j  = concat ["[",show i,show j,"]"]
   | i+2 == j  = concat ["[",show i,show (i+1), show (i+2),"]"]
   | otherwise = concat ["[",show i,"-",show j,"]"]
  show (Or a b)  = show a ++ "|" ++ show b
  show MatchNone = "^$"
  show (All n) 
   | n < 3     = concat $ replicate n alphabet
   | otherwise = concat [alphabet,"{",show n,"}"] 
  show e@(Concat xs) 
   | atomic e  = concatMap show xs
   | otherwise = concatMap show' xs
   where show' (Or a b) = "("++show (Or a b)++")"
         show' x = show x
         atomic (Concat xs) = all atomic xs
         atomic (Or _ _)    = False
         atomic _           = True

-- Match integers in a certain range
matchIntRange :: Int->Int->RegEx
matchIntRange a b
 | 0 > min a b = error "Negative input"
 | a > b       = MatchNone
 | otherwise = build (d a) (d b)
 where build :: [Int]->[Int]->RegEx
       build [] [] = Concat [] 
       build (a@(x:xs)) (b@(y:ys))
         | sl && x == y       = Concat [Range x x, build xs ys]
         | sl && all9 && all0 = Concat [Range x y, All n]
         | sl && all0         = Or (Concat [Range x (y-1), All n]) upper
         | sl && all9         = Or lower (Concat [Range (x+1) y, All n])
         | sl && x+1 <= y-1   = Or (Or lower middle) upper
         | sl                 = Or lower upper
         | otherwise          = Or (build a (nines la)) (build (1:zeros la) b)
         where (la,lb) = (length a, length b)
               sl      = la == lb
               n       = length xs
               upper   = Concat [Range y y, build (zeros n) ys]
               lower   = Concat [Range x x, build xs (nines n)]
               middle  = Concat [Range (x+1) (y-1), All n]
               all9    = all (==9) ys
               all0    = all (==0) xs
       zeros n   = replicate n 0
       nines n   = replicate n 9
       d 0 = [0]
       d n = digits 10 n

以下のコードは、アルゴリズムの理解に役立つ単純なバージョンですが、正規表現サイズを改善するための最適化は行いません。

matchIntRange 123 4321

(((1((2((3|[4-8])|9)|[3-8]((0|[1-8])|9))|9((0|[1-8])|9))|[2-8]((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|9((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|((1((0((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9))|[1-8]((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|9((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|[2-3]((0((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9))|[1-8]((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|9((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9))))|4((0((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9))|[1-2]((0((0|[1-8])|9)|[1-8]((0|[1-8])|9))|9((0|[1-8])|9)))|3((0((0|[1-8])|9)|1((0|[1-8])|9))|2(0|1)))))

正規表現は680文字です。これがコードです

import Data.Digits

data RegEx = Range Int Int | MatchNone | Or RegEx RegEx | Concat [RegEx] 

alphabet = "\\d"

instance Show RegEx where
  show (Range i j)
   | i == j    = show i
   | otherwise = concat ["[",show i,"-",show j,"]"]
  show (Or a b)  = concat ["(",show a,"|",show b,")"]
  show MatchNone = "^$"
  show (Concat xs) = concatMap show xs

matchIntRange :: Int->Int->RegEx
matchIntRange a b
 | 0 > min a b = error "Negative input"
 | a > b       = MatchNone
 | otherwise = build (d a) (d b)
 where build :: [Int]->[Int]->RegEx
       build [] [] = Concat [] 
       build (a@(x:xs)) (b@(y:ys))
         | sl && x == y     = Concat [Range x x, build xs ys]
         | sl && x+1 <= y-1 = Or (Or lower middle) upper
         | sl               = Or lower upper
         | otherwise        = Or (build a (nines la)) (build (1:zeros la) b)
         where (la,lb) = (length a, length b)
               sl      = la == lb
               n       = length xs
               upper   = Concat [Range y y, build (zeros n) ys]
               lower   = Concat [Range x x, build xs (nines n)]
               middle  = Concat [Range (x+1) (y-1), build (zeros n) (nines n)]
       zeros n = replicate n 0
       nines n = replicate n 9
       d 0 = [0]
       d n = digits 10 n

2

GolfScript(126 + 2 * 170 = 466)

~)]{`:&,:_,{:i'('\_(<:/i&=48-:D 2<{D^i!!D*|1,*}{'['\i>2D<'-'*D(']?'3$)<}if/D!!*{'\d{'/i>'1,'*_(i-'}|'D}*}%_')'*]}%'(?!'\~'$)'\

与えられた値に対してそれは与える

(?!(\d{1,5}|1([01]\d{4}|2([0-2]\d{3}|3([0-3]\d{2}|4([0-4]\d{1}|5([0-5]))))))$)([1-6]?\d{1,6}|7([0-5]\d{5}|6([0-4]\d{4}|5([0-3]\d{3}|4([0-2]\d{2}|3([01]\d{1}|2([01])))))))

従うべき解剖が、基本的な考え方は、任意より小さい自然数に一致する正規表現を単一の自然数をマッピングするコードブロックを定義し、入力をオンするlbub(より小さい自然数のための負の先読みにlb組み合わせます)の正規表現(より小さい自然数ub+1)。

ロジックは非常に複雑なので、GolfScript標準でさえ、それは不可解です。詳細な分析を書くまでは、変数のリストを次に示します。

&  the whole number string
i  the current idx
D  the current digit
/  not-the-last-digit
_  total number of digits

@ dan1111、私はPCREのドキュメントを調べましたが、順不同の文字クラスを禁止するものは何も見当たりませんでした。また、使用したテスターはエラーを出しませんでした。それを調べなければならない。OTOHあなたの正規表現エンジンが末尾が終わる式が好きではない場合|、それは私の正規表現ではなく、あなたの正規表現エンジンのバグです。
Peter Taylor

申し訳ありませんが、私はそれ(a|)が実際に有効であることを知りませんでした。ただし、[1-0]以前の正規表現では、Perlまたは私が試したオンラインテスターでは機能しませんでした。

@ dan1111、あなたが指摘した後、私が使用していたオンラインテスターがエラーを飲み込んでいることに気付きました。Perlを備えたマシンでそれを再現し、正規表現をチェックするためにPerlを使用してテストフレームワークを作成しました。指摘してくれてありがとう。
Peter Taylor
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.