메타 방사선 경화제


19

배경

이 사이트에서는 때때로 프로그램을 "방사선 강화"해야하는 질문이 있습니다. 즉, 어떤 바이트가 삭제 되더라도 프로그램이 하나 이상의 바이트를 삭제하더라도 살아남을 수 있어야합니다.

프로그래밍 과제에서 자주 설정되는 작업의 경우와 마찬가지로 이러한 과제에 특히 적합한 언어를 만드는 것이 당연합니다. 자연스럽게 만드는 방법은 손상을 되돌릴 수있는 메타 데이터를 추가하는 것이므로 실제로 디자인이 필요한 언어가 아니라 인코딩입니다. 아이디어는 각 입력을 바이트 시퀀스로 변환하여 시퀀스에 약간의 조사가 있더라도 원래 입력을 추출 할 수있는 방식으로 변환하는 것입니다.

작업

E (엔코더)와 D (디코더)의 두 프로그램 또는 기능을 다음과 같이 작성하십시오.

  • E는 두 개의 인수, 즉 이 명세에서 우리는 " 입력 " 이라고 부르는 일련의 8 진수 와 음이 아닌 정수 " 방사선 "을 취하고, 8 진수 의 시퀀스 " 인코딩 "을 출력합니다 .
  • D는 하나의 인수, 즉 옥텟 시퀀스 ( " encdng ")를 취하여 옥텟 시퀀스 " 복원 "을 출력한다 ;
  • E와 D를 모두 실행하는 경우 ( encdng 를 사용하여 인코딩 에서 방사 요소 만 삭제하여 선택한 D에 대한 입력 (연속적 일 필요는 없음)) encdng 을 형성하기 위해 어떤 문자를 삭제했는지에 상관없이 재구성입력 과 같습니다 .

설명

  • 이 기능을 제출하는 경우, 당신이 그들을 호출 할 필요가 없습니다 ED; 언어에 가장 적합한 이름을 선택할 수 있습니다.
  • "옥텟"은 기본적으로 0에서 255까지의 정수이며 정수, 문자 또는 언어에 적합한 것으로 인코딩 할 수 있습니다.
  • E와 D는 전적으로 결정 론적이어야합니다. 즉, 동일한 입력을 제공하면 항상 동일한 출력을 생성합니다. 여기서 "입력"은 E의 입력방사선으로 정의 되거나 D의 encdng 로 정의됩니다 . 특히, E는 사이드 채널을 통해 정보를 D와 통신하지 않을 수있다.
  • 삭제는 시퀀스의 한 요소를 삭제하여 수행됩니다. 편집기에서 seuqence를 열고 커서를 임의의 지점에 놓고 Backspace를 누르십시오. 요소가 여러 번 나타나는 경우 요소의 사본 하나만 삭제 될 수 있습니다 (즉, 동일한 옥텟의 다른 인스턴스는 영향을받지 않습니다).
  • 점수는 입력 이 상당히 짧은 경우에만 계산되지만 프로그램은 입력복사에 대해 이론적으로 작동해야합니다 . 특히, 어떤 옥텟이 input에 나타나는지에 상관없이 작동해야합니다 . (죄송합니다. 자신이 알고있는 인쇄 할 수없는 문자를 사용하려는 사람들은 입력에 나타나지 않지만 압축이 아닌 방사선 경화에 대한 문제가되도록 입력을 압축 할 수 없도록해야합니다.)
  • 두 가지 기능을 정의하는 하나의 파일을 제출할 수 있습니다. 각각 기능을 정의하거나 전체 프로그램 인 두 개의 파일; 또는 3 개의 파일 중 2 개는 D와 E를 각각 구현하고 (전체 프로그램이거나 함수 정의를 통해) 3 번째는 D와 E에 공통된 헤더 파일 또는 라이브러리입니다. 어떤 형식의 제출 형식에 관계없이 , 프로그래밍 언어 구현은 파일 위치와 같은 추가 인수없이 두 프로그램을 모두 이해할 수 있어야합니다. 그렇지 않으면 표준 규칙에 따라 비정상적인 방식으로 구현을 호출하기 위해 바이트 페널티를 지불해야합니다.

승리 조건

각각의 길이방사선 ,하자 f를 ( 길이 , 방사선 )의 전체 길이가 될 인코딩하는 모든 대응하는 s의 입력 길이와 길이 및 주어진 방사선 . (즉, f ( length , radiation ) = 합 입력 에 길이 length length (E ( input , radiation )이 있음). 그런 다음 g ( length , radiation )을 f ( length ,방사선 ) ÷ 256 길이 . 다시 말해, g 는 주어진 입력 길이와 주어진 방사선 경화 요구 사항에 대한 인코딩 된 출력의 평균 길이입니다. (이론적으로 당신은 이것을 무차별 적으로 계산할 수 있지만, 그런 식으로 점수를 계산하는 데 엄청나게 오랜 시간이 걸릴 것입니다. 나는 대부분의 제출물이 점수가 무엇인지에 대해 수학적인 주장을 할 수있을 것으로 기대합니다. 확실하지 않은 경우 대략적인 점수를 게시하면 다른 항목이 유사한 점수를 게시하는 경우 귀 하나 다른 사람이 더 깊이 계산할 수 있습니다.)

당신의 점수는 0에서 9까지의 범위에있는 모든 방사선과 0에서 99까지의 범위에있는 모든 길이 에 대한 g ( 길이 , 방사선 ) 의 합과 같 으며 플러스 (주로 하드 코딩을 피하거나 경쟁이 계속되는 경우) 누군가는 수학적으로 완벽한 인코딩을 발견합니다. 이것은 도전에 제출할 때의 총 바이트 수 (및 비정상적인 해석기 플래그 또는 특정 파일 이름이 필요한 것과 같은 것에 대한 표준 처벌)와 달리 수학적으로 완벽한 인코딩입니다. 당첨자는 가장 낮은 점수를받은 출품작입니다 (첫 번째 출품작에 의해 동점).


디코더가 방사선 매개 변수도 알 수 있습니까?
orlp

(또는 길이 , 그러나 나는 알고 있으면 대부분의 계획에 대해 다른 것을 제공해야한다고 생각합니다)
orlp

1
@orlp : 아니요. 문자열 만 있습니다. A의 방사선 - 경화 문제, 디코더 (즉, 언어) 귀하의 디코더가 그들 중 하나 알고하지 않도록 방사선 규칙이 사용 알지 못한다; 입력에서 그것들을 추론해야합니다.

이 컴퓨터 친숙한 비디오를 기반으로 3 바이트 삼중 코드로 된 코드를 작성합니다. 모두 일치하지 않으면 문제가 발생하여 올바른 값이 무엇인지 알 수있는 충분한 정보가 있습니다. 더 적은 비트로 그것을 할 수있는 방법이있을 수 있지만 지금은 어떻게 작동하는지 두뇌가 없습니다.
Draco18s 2016 년

헤더와 프로그램을 어떻게 결합합니까?
CalculatorFeline

답변:


8

CJam, 점수 ≤ 286,516 + 54 + 36 = 286,606

엔코더

{_{1\+_:e>)_0a+@@b0\{{)_256b_,\e`,-}g}*256b+\)e*}{*}?}

온라인으로 사용해보십시오!

디코더

{_{e`1f=(\256b{256b_,\e`,=},,\b1>}&}

온라인으로 사용해보십시오!

둘 다 목록 정수를 가져오고 리턴합니다. TIO 링크에는 편의를 위해 문자열 간 변환이 포함됩니다. 긴 문자열에 대해서는 매우 비효율적입니다. 더 많은 문자를 사용하려면 문자 코드가 작은 문자를 사용하는 것이 좋습니다.

방사선 강화 인코딩을 만들기위한 기본 아이디어는 다음 두 단계로 이루어집니다.

  1. 두 개의 동일한 동일한 옥텟을 포함하지 않는 인코딩을 찾으십시오.
  2. 인코딩 된 문자열 r + 1 의 각 옥텟을 반복 합니다. 여기서 r 은 방사선 레벨입니다.

이런 식으로, 방사선은 동일한 문자의 하나의 런을 완전히 삭제할 수 없으므로 각 런에서 하나의 문자를 취한 다음 1 단계를 디코딩하여 문자열을 디코딩 할 수 있습니다.

따라서 유일한 흥미로운 부분은 반복 옥텟을 생성하지 않는 인코딩을 찾는 것입니다. 기본 아이디어는 A043096 과 같은 것을 숫자 시스템으로 사용하는 것 입니다. 즉, 정수 N 을 인코딩하기 위해 반복되는 옥텟으로 모든 숫자를 건너 뛰고 일부 b 에서 간단히 계산합니다 . 나는이 방법으로 최대 d 자릿수로 표현 할 수있는 숫자의 양은 bijective base b-1 로 나타낼 수있는 숫자의 양과 같다고 생각합니다 (그러한 숫자를 쓰려고 할 때부터 제약 조건을 위반하지 않고 각 위치에 대해 b-1 자리를 선택하십시오 .

물론 최대 압축을 얻으려면 b = 256을 사용 합니다. 입력을 정수로 바꾸기 위해 기본 변환도 사용할 수 있습니다. 게으르지 않으면 입력에 bijective base를 사용하지만 지금은 앞에 10을 추가 하지 않기 위해 앞에 붙인 다음 가능한 가장 작은베이스를 사용하여 모든 옥텟이 입력이 밑보다 작습니다.

이베이 스는 인코딩 앞에 추가되어 (디코더가 사용할베이스를 알 수 있도록) 남은 숫자와 0 옥텟으로 분리됩니다 (남은 숫자는 절대 0으로 시작하지 않기 때문에 작동합니다). 사소한 최적화로 빈 문자열은 빈 문자열로 유지됩니다.

위의 정확한 점수를 계산하지 않은 이유는 각 입력이 길이와 최대 옥텟을 기반으로하는 기간에 대한 상한 만 계산하기 때문입니다. 그러나이 두 매개 변수의 경우 종종 두 개의 다른 출력 길이가 있으며, 그 사이의 티핑 포인트가 발생하는 지점을 파악하는 데 신경 쓰지 않았습니다. 나는 또한 bijective base 255 대신에 평범한 base 255의 길이를 사용하여 그 길이를 추정했다. 계산에 사용한 정확한 Mathematica 코드는 다음과 같습니다.

num[l_, 1] = 0;
num[l_, base_] := num[l, base] = base^l - Sum[num[l, b], {b, base - 1}]
Sum[
  num[l, b]*(r + 1)*(2 + IntegerLength[2*b^l - 1, 255])/256^l, 
  {r, 0, 9}, {l, 1, 99}, {b, 2, 256}
]
N@%

num[l, b]l최대 옥텟을 가진 길이의 문자열 수를 지정해야합니다 b-1(단 b == 1, 0항상 적어도 base를 사용 하기 때문에 하드 코딩 한 경우 제외 2).


"길이가 N 인 스트링은 평균적으로 방사선 레벨 r에서 (r + 1) * N 옥텟보다 작은 것으로 인코딩 될 수 없다고 가정한다." 나는 이것이 사실 일 이유가 없다고 본다. 나는 O (N + r) 인 인코딩 체계가 존재하는 것을보고 놀라지 않을 것입니다.
orlp

1
@orlp 나는 그것이 어떻게 가능한지 알지 못하지만 틀린 것으로 기대되고 있습니다. :)
Martin Ender

좋은 생각. CJam은 모르지만 설명에서 인코딩 된 데이터에 기본을 추가하는 것처럼 들립니다. 이 경우 앞에 추가 된 데이터에서 삭제 된 문자가 있으면 문제가 있습니까? (저는 @Leo가 지적한 실수로 솔루션에서 수정해야했습니다.)
Mitchell Spector

@MitchellSpector는 각 문자를 r + 1 번 반복하기 전에 밑을 추가합니다. 베이스도 방사선 안전합니다.
마틴 엔더

그게 좋았어. 내 솔루션에서 끝났어. 베이스가 무엇인지 알기 전에 디코더가 프리 펜딩 된 데이터를 디코딩 할 수 있는지 확인하면됩니다.
Mitchell Spector

6

bash + GNU 유틸리티, 점수 294506 283468

편집 1 : @Leo가 알아 차린 문제 수정-감사합니다!

편집 2 : 더 나은 점수를 위해 방사선 매개 변수의 인코딩 방법을 개선했습니다.

인코더 (97 바이트) :

for((j=0;j<$1;j++)){ p+=p\;;}
(sed 's/\(.\)\1/\1a\1a/g'<<<$1;cat)|xxd -p -c1|sed ${p-;}|xxd -r -p

디코더 (121 바이트) :

read n
n=`sed 's/\(.\)\1*/\1/g'<<<$n`
sed -r "s/( ..)\1{0,${n//a}}/\1/g"<<<' 0a '`xxd -p -c1`|sed 's/^ [^ ]*//'|xxd -r -p

인코더의 경우 : stdin에서 문자로 전달 된 옥텟 시퀀스, 방사선 매개 변수 r이 인수로 전달되었습니다.

디코더 : stdin에서 문자로 입력이 전달됩니다.

둘 다 : stdout에서 출력.

엔코더는 입력 데이터 앞에 r 자릿수를 붙이고 연속 된 동일한 숫자의 각 쌍 사이에 문자 'a'를 삽입 한 다음 단일 줄 바꿈을 붙입니다. 그런 다음 모든 문자를 (사전 문자로 시작하여) 복사하여 각 문자를 해당 문자의 r + 1 사본으로 바꿉니다.

디코더는이 작업을 취소하고 입력에서 나머지 각 문자 x를 거치면서 x 다음에 나오는 x의 동일한 연속 복사본을 건너 뛰고 남아있는 내용을 인쇄합니다. 앞에 붙은 데이터에는 반복되는 문자가 없으므로 r을 알기 전에 디코딩 할 수 있습니다. 이 시점에서, r은 알려져 있으며, 그 값은 나머지 데이터를 정확하게 디코딩하기 위해 필요합니다.

원래 입력이 동일한 문자를 반복하더라도 이것이 작동하는지 확인할 수 있습니다.


점수 계산 :

입력의 길이가 L이고 방사선 매개 변수가 r이라고 가정합니다 (점수 계산의 경우 최대 9이므로 한 자리에 들어가므로 연속 반복되는 문자가 없음). 앞에 추가 된 데이터는 2 바이트 (숫자, 개행)이므로 출력은 인코딩 된 스트림에 대해 (r + 1) (L + 2) 바이트입니다.

따라서 g (L, r) = (r + 1) (L + 2)입니다.

총 점수는 다음과 같이 계산 될 수 있습니다.

여기에 이미지 설명을 입력하십시오


떨어진 옥텟이 첫 번째 옥텟이면 어떻게됩니까? 디코더는 r읽을 필요가 없습니다
Leo

@ 레오 당신이 맞아요. 나는 내일 고정 살펴 보겠습니다 - 너무 늦게 오늘 밤. 그것을 찾아 주셔서 감사합니다.
미첼 스펙터

@ 레오 나는 r의 각 자리수의 r + 1 사본을 포함하고 r + 1 개행을 포함하여 고칠 수 있다고 생각합니다. 맞습니다 경우, 점수는 매우에 의해 이동되지 않습니다.
미첼 스펙터

그런 식으로 작동합니다. 더 높은 방사선 (예 : 방사선 222)에서 제대로 작동하는지 확인하기 위해 몇 가지 추가 조치를 취해야 하지만 운 좋게도 점수는 0-9 방사선에 대해서만 계산되므로 크게 영향을받지 않습니다. 추신 : 나는이 동일한 인코딩을 구현하려고 생각했기 때문에 바로 오류를 발견했습니다.)
Leo

@Leo 예,이 수정은 점수가 최대 9의 방사선 값만 고려하더라도 방사선의 모든 값에 적용됩니다.
Mitchell Spector

3

Perl + Math :: {ModInt, 다항식, 프라임 :: Util}, 점수 ≤ 92819

$m=Math::Polynomial;sub l{($n,$b,$d)=@_;$n||$d||return;$n%$b,l($n/$b,$b,$d&&$d-1)}sub g{$p=$m->interpolate([grep ref$_[$_],0..$map{$p->evaluate($_)}0..$}sub p{prev_prime(128**$s)}sub e{($_,$r)=@_;length||return'';$s=$r+1;s/^[␀␁]/␁$&/;@l=map{mod($_,p$s)}l(Math::BigInt->from_bytes($_),p$s);$@l+$r>p($s)&&return e($_,$s);$a=0;join'',map{map{chr$_+$a}l($_->residue,128,$s,($a^=128))}g(@l)}sub d{@l=split/([␀-␡]+)/,$_[0];@l||return'';$s=vecmax map length,@l;@l=g map{length==$s&&mod($m->new(map{ord()%128}split//)->evaluate(128),p$s)}@l;$$_=$m->new(map{$_->residue}@l)->evaluate(p$s)->to_bytes;s/^␁//;$_}

제어 이미지 (예를 대응하는 제어 특성을 나타내는 데 사용되는 리터럴 널 문자이다). 많은 코드를 읽으려고에 대해 걱정하지 마십시오; 아래에 더 읽기 쉬운 버전이 있습니다.

으로 실행합니다 -Mbigint -MMath::ModInt=mod -MMath::Polynomial -MNtheory=:all. -MMath::Bigint=lib,GMP필요하지 않으므로 (점수에 포함되지 않음) 다른 라이브러리 앞에 추가하면 프로그램이 다소 빠르게 실행됩니다.

점수 계산

여기서 알고리즘은 다소 개선 될 수는 없지만 쓰기가 더 어렵습니다 (Perl에 적절한 라이브러리가 없기 때문에). 이로 인해 코드에서 바이트를 절약 할 수 있다는 점을 감안할 때 코드에서 몇 가지 크기 / 효율성 트레이드 오프를 만들었습니다. 골프에서 모든 포인트를 줄이려고 할 필요는 없습니다.

이 프로그램은 코드의 600 바이트 플러스 678 포인트 페널티를주는 명령 줄 옵션에 대한 처벌, 78 바이트로 구성된다. 나머지 점수는 0에서 99까지의 모든 길이와 0에서 9까지의 모든 방사선 레벨에 대해 최상의 경우와 최악의 경우 (출력 길이로) 문자열에서 프로그램을 실행하여 계산되었습니다. 평균 사례는 중간에 있으며 점수에 한계가 있습니다. (다른 항목이 비슷한 점수로 나오지 않는 한 정확한 값을 계산할 가치는 없습니다.)

따라서 이것은 포괄적 따라서, 최종 스코어는 91,100 92,141에 부호화 효율의 점수 범위 인 것을 의미한다 :

91100 + 600 + 78 = 91,778 점수 ≤ ≤ 92,819 + 92,141 = 600 + 78

주석 및 테스트 코드가 포함 된 덜 골판지 버전

이것은 원래 프로그램 + 줄 바꿈, 들여 쓰기 및 주석입니다. (실제로 골프 버전은이 버전에서 줄 바꿈 / 들여 쓰기 / 설명을 제거하여 제작되었습니다.)

use 5.010;                    # -M5.010; free
use Math::BigInt lib=>'GMP';  # not necessary, but makes things much faster
use bigint;                   # -Mbigint
use Math::ModInt 'mod';       # -MMath::ModInt=mod
use Math::Polynomial;         # -MMath::Polynomial
use ntheory ':all';           # -Mntheory=:all
use warnings;                 # for testing; clearly not necessary

### Start of program

$m=Math::Polynomial;          # store the module in a variable for golfiness

sub l{ # express a number $n in base $b with at least $d digits, LSdigit first
    # Note: we can't use a builtin for this because the builtins I'm aware of
    # assume that $b fits into an integer, which is not necessarily the case.
    ($n,$b,$d)=@_;
    $n||$d||return;
    $n%$b,l($n/$b,$b,$d&&$d-1)
}

sub g{ # replaces garbled blocks in the input with their actual values
    # The basic idea here is to interpolate a polynomial through all the blocks,
    # of the lowest possible degree. Unknown blocks then get the value that the
    # polynomial evaluates to. (This is a special case of Reed-Solomon coding.)
    # Clearly, if we have at least as many ungarbled blocks as we did original
    # elements, we'll get the same polynomial, thus we can always reconstruct
    # the input.
    # Note (because it's confusing): @_ is the input, $_ is the current element
    # in a loop, but @_ is written as $_ when using the [ or # operator (e.g.
    # $_[0] is the first element of @_.
    # We waste a few bytes of source for efficiency, storing the polynomial
    # in a variable rather than recalculating it each time.
    $p=$m->interpolate([grep ref$_[$_],0..$#_],[grep ref,@_]);
    # Then we just evaluate the polynomial for each element of the input.
    map{$p->evaluate($_)}0..$#_
}

sub p{ # determines maximum value of a block, given (radiation+1)
    # We split the input up into blocks. Each block has a prime number of
    # possibilities, and is stored using the top 7 bits of (radiation+1)
    # consecutive bytes of the output. Work out the largest possible prime that
    # satisfies this property.
    prev_prime(128**$s)
}

sub e{ # encoder; arguments: input (bytestring), radiation (integer)
    ($_,$r)=@_; # Read the arguments into variables, $_ and $r respectively
    length||return''; # special case for empty string
    $s=$r+1; # Also store radiation+1; we use it a lot
    # Ensure that the input doesn't start with NUL, via prepending SOH to it if
    # it starts with NUL or SOH. This means that it can be converted to a number
    # and back, roundtripping correctly.
    s/^[␀␁]/␁$&/; #/# <- unconfuse Stack Exchange's syntax highlighting
    # Convert the input to a bignum, then to digits in base p$s, to split it
    # into blocks.
    @l=map{mod($_,p$s)}l(Math::BigInt->from_bytes($_),p$s);
    # Encoding can reuse code from decoding; we append $r "garbled blocks" to
    # the blocks representing the input, and run the decoder, to figure out what
    # values they should have.
    $#l+=$r;
    # Our degarbling algorithm can only handle at most p$s blocks in total. If
    # that isn't the case, try a higher $r (which will cause a huge increase in
    # $b and a reduction in @l).
    @l+$r>p($s)&&return e($_,$s);
    # Convert each block to a sequence of $s digits in base 128, adding 128 to
    # alternating blocks; this way, deleting up to $r (i.e. less than $s) bytes
    # will preserve the boundaries between each block; then convert that to a
    # string
    $a=0; # we must initialize $a to make this function deterministic
    join'',map{map{chr$_+$a}l($_->residue,128,$s,($a^=128))}g(@l)
}

sub d{ # decoder: arguments; encdng (bytestring)
    # Reconstruct the original blocks by looking at their top bits
    @l=split/([␀-␡]+)/,$_[0];
    @l||return''; # special case for empty string
    # The length of the longest block is the radiation parameter plus 1 (i.e.
    # $s). Use that to reconstruct the value of $s.
    $s=vecmax map length,@l;
    # Convert each block to a number, or to undef if it has the wrong length.
    # Then work out the values for the undefs.
    @l=g map{
        # Convert blocks with the wrong length to undef.
        length==$s&&
            # Convert other blocks to numbers, via removing any +128 and then
            # using Math::Polynomial to convert the digit list to a number.
            mod($m->new(map{ord()%128}split// #/# <- fix syntax highlighting
            )->evaluate(128),p$s)
    }@l;
    # Remove the redundant elements at the end; now that they've reconstructed
    # the garbled elements they have no further use.
    $#l-=$s-1;
    # Convert @l to a single number (reversing the conversion into blocks.)
    $_=$m->new(map{$_->residue}@l)->evaluate(p$s)
        # Convert that number into a string.
        ->to_bytes;
    # Delete a leading SOH.
    s/^␁//;  #/# <- unconfuse Stack Exchange's syntax highlighting
    # Finally, return the string.
    $_
}


### Testing code
use Encode qw/encode decode/;

# Express a string using control pictures + IBM437, to make binary strings
# easier for a human to parse
sub format_string {
    ($_)=@_;
    $_ = decode("Latin-1", $_);
    s/[\0-\x1f]/chr (0x2400 + ord $&)/eag;
    s/\x7f/chr 0x2421/eag;
    s/[ -~\x80-\xff]/decode("IBM437",$&)/eag;
    encode("UTF-8","\x{ff62}$_\x{ff63}")
}

sub test {
    my ($string, $radiation, $samples) = @_;
    say "Input: ", format_string($string);
    my $encoding = e($string, $radiation);
    say "Encoding: ", format_string($encoding);
    say "Input length ", length($string), ", encoding length ", length($encoding), ", radiation $radiation";
    my $decoding = d($encoding);
    $decoding eq $string or die "Mistake in output!";
    say "Decoding: ", format_string($decoding), " from ",
        format_string($encoding);

    # Pseudo-randomly generate $samples radiation-damaged versions.
    srand 1;
    for my $i (1..$samples) {
        my $encdng = $encoding;
        for my $r (1..$radiation) {
            substr $encdng, int(rand(length $encdng)), 1, "";
        }
        my $newdecoding = d($encdng);
        say "Decoding: ", format_string($newdecoding), " from ",
            format_string($encdng);
        $newdecoding eq $string or die "Mistake in output!";
    }

    say "";
    length $encoding;
}

test "abcdefghijklm", 1, 10;
test "abcdefghijklm", 2, 10;
test "abcdefghijklm", 5, 10;
test "abcdefghijklm", 10, 10;
test "\0\0\0\0\0", 1, 10;
test "\5\4\3\2\1", 2, 10;
test "a", 10, 10;

my %minlength = ();
my %maxlength = ();

for my $length (0..99) {
    my ($min, $max) = ("", "");
    $length and ($min, $max) =
        ("\2" . "\0" x ($length - 1), "\1" . "\377" x ($length - 1));
    for my $radiation (0..9) {
        $minlength{"$length-$radiation"} = test $min, $radiation, 1;
        $maxlength{"$length-$radiation"} = test $max, $radiation, 1;
    }
}

say "Minimum score: ", vecsum values %minlength;
say "Maximum score: ", vecsum values %maxlength;

연산

문제 단순화

기본 아이디어는이 "삭제 코딩"문제 (일반적으로 탐색되지 않은 문제)를 소거 코딩 문제 (포괄적으로 탐구되는 수학 영역)로 줄이는 것입니다. 삭제 코딩의 기본 개념은 "삭제 채널"을 통해 데이터를 전송할 준비를하고 있다는 것입니다.이 채널은 전송하는 문자를 알려진 오류 위치를 나타내는 "가블"문자로 대체하는 채널입니다. (즉, 원래 문자는 여전히 알려지지 않았지만 손상이 발생한 위치는 항상 분명합니다.) 그 뒤에있는 아이디어는 매우 간단합니다. 입력을 길이 블록 ( 방사선) 으로 나눕니다.+ 1), 각 블록에서 8 비트 중 7 비트를 데이터로 사용하고 나머지 비트 (이 구성에서는 MSB)가 전체 블록에 대해 설정되고 다음 블록 전체에 대해 설정 해제되고 블록에 대해 설정됩니다. 그 후, 그리고에 이렇게. 블록이 방사선 매개 변수보다 길기 때문에 각 블록에서 하나 이상의 문자가 출력에 남아 있습니다. 따라서 동일한 MSB로 문자를 실행하여 각 문자가 속한 블록을 해결할 수 있습니다. 블록의 수는 항상 방사선 매개 변수보다 크므로 encdng에는 항상 하나 이상의 손상되지 않은 블록이 있습니다. 따라서 우리는 가장 길거나 가장 긴 묶인 모든 블록이 손상되지 않았으므로 더 짧은 블록을 손상된 것으로 취급 할 수 있습니다. 우리는 또한 '(그것을 같은 방사선 매개 변수를 추론 할 수

소거 코딩

문제의 삭제 코딩 부분에 관해서는,이 리드 - 솔로몬 구조의 간단한 특별한 경우를 사용합니다. 이 체계적인 구조이다 : (소거 코딩 알고리즘)의 출력이 입력 더하기 추가 블록의 수와 동일한, 상기 방사선 파라미터와 동일. 우리는 간단한에서 이러한 블록에 필요한 값을 계산 (및 golfy!) 방법을, 다음 "재구성"자신의 값으로 그들에 디코딩 알고리즘을 실행 깨짐로 치료를 통해 할 수 있습니다.

구성의 실제 개념도 매우 간단합니다. 가능한 최소한의 다항식을 인코딩의 모든 블록에 맞 춥니 다 (다른 요소에서 내포 된 가블). 다항식이 f 이면 첫 번째 블록은 f (0)이고 두 번째 블록 은 f (1) 등입니다. 다항식의 차수는 입력에서 1을 뺀 블록 수와 같습니다 (다항식을 먼저 다항식에 맞추고이를 사용하여 여분의 "체크"블록을 구성하기 때문에). 하고 있기 때문에 D +1 포인트 고유 정도의 다항식을 정의 D를(방사선 파라미터까지) 임의의 수의 블록을 잘못 전달하는 손상 블록의 개수가 동일한 다항식을 재구성하기에 충분한 정보 인 일본어 입력 동일 떠날 것이다. 그런 다음 블록을 깨지지 않도록 다항식을 평가하면됩니다.

자료 변환

여기에 남은 마지막 고려 사항은 블록이 취하는 실제 값과 관련이 있습니다. 우리가 정수에 다항식 보간을 할 경우, 결과는 입력 값보다 훨씬 큰, 또는 기타 바람직하지 않은 유리수 (이 아닌 정수) 일 수있다. 따라서 정수를 사용하는 대신 유한 필드를 사용합니다. 이 프로그램에서 사용 된 유한 필드 정수 모듈의 필드 (P) , p는 가장 큰 소수 128 미만이며 , 방사선 +1은(즉, 블록의 데이터 부분에 해당 소수와 동일한 여러 고유 값을 맞출 수있는 가장 큰 소수). 유한 필드의 가장 큰 장점은 나눗셈 (0을 제외하고)이 고유하게 정의되고 해당 필드 내에서 항상 값을 생성한다는 것입니다. 따라서, 다항식의 보간 된 값은 입력 값과 같은 방식으로 블록에 맞습니다.

블록 데이터의 일련의 입력을 변환하기 위해, 다음에, 우리는 기본 변환을 수행해야 다음베이스로 변환 번호로 기재 (256)로부터 입력을 변환 P 예에 대해 ( 방사선 (1)의 파라미터, 우리가 = 16381). 이것은 대부분 Perl의 기본 변환 루틴 부족으로 인해 발생했습니다 (Math :: Prime :: Util에는 일부가 있지만 bignum base에는 작동하지 않으며 여기에서 작업하는 소수는 엄청나게 큽니다). 우리는 이미 다항식 보간에 Math :: Polynomial을 사용하고 있기 때문에, "숫자를 다항식의 계수로보고 평가함으로써)"숫자 시퀀스에서 변환 "기능으로 재사용 할 수있었습니다. 괜찮아 그러나 다른 방법으로 가면 함수를 직접 작성해야했습니다. 운 좋게도 작성하기가 너무 어렵거나 장황하지 않습니다. 불행히도,이 기본 변환은 입력을 일반적으로 읽을 수 없도록 렌더링됨을 의미합니다. 선행 0에는 문제가 있습니다.

우리는 출력에 p 개 이상의 블록을 가질 수 없다는 점에 유의해야합니다 (그렇지 않으면 두 블록의 인덱스가 동일 해지지 만 다항식에서 다른 출력을 생성해야 할 수도 있음). 이것은 입력이 매우 큰 경우에만 발생합니다. 이 프로그램은 매우 간단한 방식으로 문제를 해결합니다. 방사선 증가 (블록을 더 크게, p를 크게하여 더 많은 데이터를 수용 할 수 있고 정확한 결과를 얻을 수 있음).

작성해야 할 또 하나의 요점은 작성된 것처럼 프로그램이 충돌하면 null 문자열을 자체로 인코딩한다는 것입니다. 또한 가능한 최고의 인코딩이며 방사선 매개 변수가 무엇이든 상관없이 작동합니다.

잠재적 개선

이 프로그램에서 주요 점근 비효율은 문제의 유한 필드로 모듈로 프라임을 사용하는 것과 관련이 있습니다. 크기가 2 n 인 유한 필드가 존재합니다 (블록의 페이로드 크기가 자연스럽게 128의 거듭 제곱이기 때문에 여기에서 정확히 원하는 것입니다). 불행하게도, 그들은 수학 :: ModInt 그것을 잘라 않을 것이다 (나는 비 프라임 크기의 유한 필드를 처리하기위한 CPAN에 어떤 라이브러리를 찾을 수 없습니다) 즉, 간단한 모듈로 건설보다는 더 복잡한 것; 나는 수학 :: 다항식을 처리 할 수 과부하 산술 전체 클래스를 작성해야하고, 줄 그 시점에서 바이트 비용 잠재적 예를 들어, 사용에서 (아주 작은) 손실 overweigh, 16,381 아니라 16,384 이하인 수 있습니다.

2의 거듭 제곱 크기를 사용하는 또 다른 이점은 기본 변환이 훨씬 쉬워진다는 것입니다. 그러나 두 경우 모두 입력 길이를 나타내는 더 나은 방법이 유용합니다. "모호한 경우 1 앞에 추가"방법은 간단하지만 낭비입니다. 형용사 기준 변환은 여기에서 하나의 그럴듯한 접근 방식입니다 (각각의 숫자는 단일 문자열에 해당하도록 숫자는 숫자가 아니고 0은 숫자가 아님).

이 인코딩의 점근 적 성능은 매우 우수하지만 (예 : 길이 99의 입력 및 3의 방사선 매개 변수의 경우, 인코딩은 반복 기반 접근법이 얻을 수있는 ~ 400 바이트가 아니라 항상 128 바이트 길이 임) 성능 짧은 입력에는 좋지 않습니다. 인코딩의 길이는 항상 (방사선 매개 변수 + 1)의 제곱 이상입니다. 따라서 방사선 9에서 매우 짧은 입력 (길이 1 ~ 8)의 경우 출력 길이는 100입니다. (길이 9에서 출력 길이는 때때로 100 및 때로는 110입니다.) 반복 기반 접근 방식은이 소거를 분명히 능가합니다. 매우 작은 입력에 대한 코딩 기반 접근법; 입력 크기에 따라 여러 알고리즘간에 변경하는 것이 좋습니다.

마지막으로, 정말를 단락 블록 (출력 크기의 ⅛) 모든 바이트의 비트가 낭비 사용하여 매우 높은 방사선 매개 변수와 함께 득점을 마련하지 않지만, 대신 블록 사이에 구분 기호를 사용하는 것이 저렴하다. 구분 기호로 블록을 재구성하는 것은 대체 MSB 접근 방식보다 다소 어렵지만 적어도 데이터가 충분히 길면 (짧은 데이터로 인해 출력에서 ​​방사선 매개 변수를 추론하기가 어려울 수 있음) 가능하다고 생각합니다 . 즉 관계없이 매개 변수의 점근 적으로 이상적인 접근을 목표로하는 경우를 보면 뭔가 될 것입니다.

(물론,이보다 더 나은 결과를 완전히 다른 알고리즘이있을 수 있습니다!)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.