メタ放射線硬化剤


19

バックグラウンド

このサイトでは、プログラムを「放射線強化」するように要求する質問が時々あります。つまり、どのバイトが削除されても、プログラムは1つまたは複数のバイトの削除に耐えることができなければなりません。

プログラミングの課題で頻繁に設定されるタスクでは一般的であるように、これらの課題に特に優れた言語を作成したいのは自然なことです。これを行うための自然な方法は、破損を元に戻すことができるメタデータを追加することであることを考えると、実際には設計が必要な言語ではなく、エンコードです。アイデアは、各入力をバイトのシーケンスに変換することで、シーケンスがわずかに照射されても、元の入力を抽出できるようにします。

タスク

次のような2つのプログラムまたは関数、E(エンコーダー)およびD(デコーダー)を記述します。

  • Eは、オクテットのシーケンス(この仕様では「入力」と呼びます)と非負整数の「放射」の2つの引数を取り、「エンコーディング」のオクテットのシーケンスを出力します。
  • Dは1つの引数、オクテットのシーケンス( " encdng ")を取り、オクテットのシーケンス " 再構成 " を出力します。
  • EとDの両方を実行する場合(encdngエンコーディングから放射要素を削除することで選択されるDへの入力(必ずしも連続的ではない))、encdngを形成するために削除された文字に関係なく、再構築入力と等しくなります

明確化

  • あなたが機能を提出する場合は、それらを呼び出す必要はありませんEとしますD。言語に最適な名前を​​選択できます。
  • 「オクテット」は基本的に0から255までの整数であり、整数、文字、または言語に適したものとしてエンコードできます。
  • EとDは完全に決定的でなければなりません(つまり、同じ入力を与えると常に同じ出力が生成されます。「入力」はEの入力放射、またはDのencdngとして定義されます)。特に、Eはサイドチャネルを介してDに情報を伝達しない場合があります。
  • 削除は、シーケンスの1つの要素を削除することにより実行されます。エディターでシーケンスを開き、カーソルを任意のポイントに置き、Backspaceキーを押すことを考えてください。要素が複数回出現する場合、要素のコピーが1つだけ削除される可能性があります(つまり、同じオクテットの他のインスタンスは影響を受けません)。
  • スコアはかなり短い入力に基づいてのみ計算されますが、プログラムは入力および放射に対して理論的に機能する必要があります。特に、inputにどのオクテットが現れても機能しなければなりません。(申し訳ありませんが、入力に表示されないことがわかっている印刷できない文字を使用したい人は、入力が非圧縮性であることを確認する必要があります。
  • 2つの関数を定義する1つのファイルを送信できます。それぞれ関数を定義するか、両方とも完全なプログラムである2つのファイル。または3つのファイル。2つはそれぞれDおよびEを実装します(完全なプログラムであるか関数を定義することにより)。3つ目はDとEの両方に共通のヘッダーファイルまたはライブラリです。使用する送信形式に関係なく、プログラミング言語の実装は、ファイルの場所などの引数を追加せずに両方のプログラムを理解できる必要があります(または、標準の規則に従って、通常とは異なる方法で実装を呼び出すためのバイトペナルティを支払う必要があります)。

勝利条件

各々に対して長さ放射線、聞かせてF長さ放射線の)の合計の長さである符号化全て秒その対応する入力の長さと長さ、及び所定の放射。(つまり、f長さ放射)= 入力のは長 length(E(入力放射))になります。)次に、g長さ放射)がf長さ放射)÷256 長さ。言い換えれば、gは入力の長さと放射線強化の要件に対するエンコードされた出力の平均長です。(理論上はこれをブルートフォースで計算できますが、そのようにスコアを計算するのには信じられないほど時間がかかるでしょう。不明な点があります。おおよそのスコアを投稿してください。他のエントリが同様のスコアを投稿した場合、あなたまたは他の誰かがより深く計算することができます。)

あなたのスコアはの和に等しいグラム長さ放射線のすべてのための)放射範囲の0〜9まで含めて、すべての長さ 99包括範囲0で、プラス(主にハードコーディングを避けるために、または行く競争を維持する場合誰かが数学的に完璧なエンコーディングを発見します。これは、そうでなければ最小限の要因である可能性があります)チャレンジへの提出の合計バイト数(および異常なインタープリターフラグまたは特定のファイル名を要求するようなものに対する標準ペナルティ)。勝者は、最も低いスコアのエントリです(最初に送信するエントリによってタイブレークされます)。


デコーダは放射パラメータも知っていますか?
orlp

(または長さが、私はどちらか知ることはあなたの最もスキームの他を与えるべきであると信じている)
orlp

1
@orlp:いいえ、文字列のみです。では、放射線硬化、あなたのデコーダはどちらか彼らを知らないので、問題は、デコーダ(すなわち言語)は、使用される放射線のルールを知りません。入力からそれらを推測する必要があります。

このコンピューター愛好家のビデオに基づいて、3バイトのトリプレットでコーデルを取る言語を作成します:それらがすべて一致しない場合、何かが間違っていて、適切な値が何であるかを把握するのに十分な情報があります。おそらく、より少ないビットでそれを行う方法はありますが、現時点ではどのように機能するかを考える脳はありません。
-Draco18s

ヘッダーとプログラムをどのように組み合わせますか?
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リンクには、便宜上文字列からの変換が含まれています。これらは長い文字列に対しては非常に効率が悪いことに注意してください。さらにいくつかの文字を試したい場合は、小さい文字コードの文字を使用することをお勧めします。

放射線耐性のあるエンコードを作成するための基本的なアイデアには、2つのステップが含まれます。

  1. 2つの連続する同一のオクテットを決して含まないエンコーディングを見つけます。
  2. エンコードされた文字列の各オクテットをr + 1回繰り返します。ここで、rは放射レベルです。

この方法では、放射は同一文字の1つの実行を完全に削除できないため、各実行から1つの文字を取得してからステップ1をデコードすることで文字列をデコードできます。

したがって、唯一の興味深い部分は、繰り返しオクテットを生成しないエンコーディングを見つけることです。基本的な考え方は、A043096のようなものを数値システムとして使用することです。つまり、整数Nをエンコードするには、オクテットが繰り返されるすべての数値をスキップして、ベースbで単純にカウントアップします。このように最大d桁で表現できる数字の量は、全単射基底b-1で表現できる数字の量と同じだと思います(そのような数字を書きたいときは、制約に違反することなく、各位置にb-1桁の数字を選択します)。

もちろん、最大の圧縮を得るにはb = 256を使用します。入力を整数に変換するには、ベース変換も使用できます。怠けていない場合は、入力に全単射基底を使用しますが、今のところは1(先頭にゼロがないことを確認するため)を先頭に追加し、可能な限り最小の基底を使用して、入力がベースよりも少ない。

次に、この基数はエンコードの先頭に追加され(デコーダーが使用する基数を認識できるように)、残りの数と0オクテットで区切られます(残りの数がゼロで始まらないため、これは機能します)。マイナーな最適化として、空の文字列は空の文字列のままです。

上記の正確なスコアを計算していないのは、各入力がその長さと最大オクテットに基づいてどのくらいの長さになるかの上限のみを計算しているからです。ただし、これらの2つのパラメーターについては、2つの異なる出力長が存在することが多く、それらの間の転換点がどこで発生するのか、まだわからないままです。また、全単射の255の代わりに通常の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(ただし、少なくともbaseを常に使用しているため、b == 1ハードコーディングして0いるを除く2)。


「平均して長さNの文字列は、放射レベルrで(r + 1)* Nオクテット未満でエンコードできないと仮定します。」これが真実である理由はないと思います。O(N + r)であるエンコーディングスキームが存在していても驚かないでしょう。
orlp

1
@orlp私はそれがどのように可能になるか見ていませんが、間違っていることが証明されるのを楽しみにしています。:)
マーティンエンダー

良いアイデア。私はCJamを知りませんが、あなたの説明から、エンコードされたデータのベースを追加しているように聞こえます。その場合、追加されたデータから文字が削除された場合、問題はありますか?(これが@Leoが指摘した間違いであり、ソリューションで修正する必要がありました。)
ミッチェルスペクター

@MitchellSpector各文字をr + 1回繰り返す前にベースが追加されます。そのため、ベースも放射線に対して安全です。
マーティンエンダー

それは良いことです-それは私が私のソリューションでもやったことです。ベースが何であるかを知る前に、デコーダがプリペンドされたデータをデコードできることを確認する必要があります。
ミッチェルスペクター

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が渡されます。

デコーダーの場合:入力は標準入力に文字として渡されます。

両方の場合:stdoutに出力します。

エンコーダは、入力データの先頭にrの数字を追加します。連続する同一の数字の各ペアの間に文字「a」が挿入され、その後に単一の改行が続きます。次に、(先頭に追加された文字で始まる)すべての入力をコピーし、各文字をその文字のr + 1コピーに置き換えます。

デコーダーはこれを取り消し、入力内の残りの文字xのそれぞれを調べ、xに続くxのr個までの同一コピーをスキップし、残りを印刷します。付加されたデータには繰り返される文字がないため、rがわかる前にデコードできます。その時点で、rは既知であり、その値は残りのデータを正しくデコードするために必要です。

元の入力が同一の文字を繰り返していても、これが機能することを確認できます。


スコア計算:

入力の長さがLで、放射パラメーターがr(スコアリング計算では最大9であるため、1桁に収まるため、連続する繰り返し文字がないと仮定します)。付加されたデータは2バイト(数字、改行)であるため、エンコードされたストリームの出力は(r + 1)(L + 2)バイトです。

したがって、g(L、r)=(r + 1)(L + 2)。

したがって、合計スコアは次のように計算できます。

ここに画像の説明を入力してください


ドロップされたオクテットが最初のものである場合はどうなりますか?デコーダはr読む必要がありません
レオ

@レオあなたの言うとおりです。明日修正することを検討します。今夜は遅すぎます。見つけてくれてありがとう。
ミッチェルスペクター

@Leo rの各桁のr + 1個のコピーと、それに続くr + 1個の改行を含めることで修正できると思います。それが正しければ、スコアはあまり上がりません。
ミッチェルスペクター

そのような何かが動作するはずです。より高い放射線(例:の放射線222)で適切に動作することを確認するために追加の対策を講じる必要があると思いますが、幸いなことにスコアは放射線0-9で計算されるため、あまり影響を受けません。PSこの同じエンコーディングを実装することを考えていたので、すぐにエラーを見つけました;)
レオ

はい@Leo、修正はスコアが高々 9の口座放射線値に取るにもかかわらず、放射線のすべての値のために働く
ミッチェルスペクター

3

Perl + Math :: {ModInt、Polynomial、Prime :: 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/^␁//;$_}

制御画像は、対応する制御文字を表すために使用されます(リテラルNUL文字など)。コードを読み取ろうとすることをあまり心配しないでください。より読みやすいバージョンが以下にあります。

で実行し-Mbigint -MMath::ModInt=mod -MMath::Polynomial -MNtheory=:allます。-MMath::Bigint=lib,GMP必要ではありません(したがって、スコアに含まれません)が、他のライブラリの前に追加すると、プログラムの実行が多少速くなります。

スコア計算

ここでのアルゴリズムは多少改善できますが、(Perlが適切なライブラリを持っていないため)書くのがかなり難しいでしょう。このため、エンコードでバイトを節約できることを考えると、コードでサイズと効率のトレードオフをいくつか行いました。ゴルフからすべてのポイントを削り取ろうとする意味はありません。

このプログラムは、600バイトのコードと、コマンドラインオプションに対する78バイトのペナルティで構成され、678ポイントのペナルティが与えられます。スコアの残りは、0〜99のすべての長さと0〜9のすべての放射レベルのベストケースとワーストケース(出力の長さ)でプログラムを実行して計算されました。平均的なケースはその中間のどこかにあり、これによりスコアの範囲が決まります。(別のエントリが同様のスコアで入らない限り、正確な値を計算しようとする価値はありません。)

したがって、これは、エンコード効率のスコアが91100から92141の範囲にあることを意味し、最終スコアは次のようになります。

91100 + 600 + 78 = 91778≤スコア≤92819 = 92141 + 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)は、ブロック全体に設定されるか、次のブロック全体にクリアされるか、ブロックに設定されるかを交互に切り替えますその後、など。ブロックは放射パラメータよりも長いため、各ブロックの少なくとも1つの文字が出力に残っています。したがって、同じMSBの文字を実行することにより、各文字がどのブロックに属しているかを判断できます。ブロックの数は常に放射パラメーターよりも大きいため、encdngには常に少なくとも1つの損傷のないブロックがあります。したがって、最も長いブロックまたは最も長く結ばれているブロックはすべて損傷を受けておらず、短いブロックを損傷したものとして扱うことができます(したがって、文字化け)。また、このような放射パラメータを推定することもできます(それは

消去コーディング

問題の消去符号化部分に関しては、これはリードソロモン構造の単純な特殊なケースを使用します。これは体系的な構成です。(イレージャーコーディングアルゴリズムの)出力は、入力に加えて、放射パラメーターに等しいいくつかの余分なブロックに等しくなります。これらのブロックに必要な値を単純な(そしてゴルフな!)方法で計算することができます。それらを文字化けとして扱い、それらに対してデコードアルゴリズムを実行して値を「再構築」します。

構築の背後にある実際の考え方も非常に単純です。可能な限り最小の多項式を、エンコードのすべてのブロックに適合させます(他の要素から文字化けが補間されます)。多項式がfの場合、最初のブロックはf(0)、2番目のブロックはf(1)などとなります。多項式の次数が入力のブロック数から1を引いたものに等しいことは明らかです(最初に多項式を当てはめ、それを使用して追加の「チェック」ブロックを構築するため)。また、d +1点は、d次の多項式を一意に定義するため、任意の数のブロック(放射パラメータまで)を文字化けすると、同じ多項式を再構築するのに十分な情報である、元の入力と同じ数の損傷していないブロックが残ります。(ブロックを解読するには、多項式を評価する必要があります。)

ベース変換

ここで最後に考慮すべきことは、ブロックが取得した実際の値を処理することです。整数で多項式補間を行う場合、結果は(整数ではなく)有理数であるか、入力値よりもはるかに大きいか、そうでなければ望ましくない可能性があります。そのため、整数を使用する代わりに、有限体を使用します。このプログラムでは、使用される有限体はpを法とする整数の体です。ここで、pは128 放射線 +1より小さい最大素数です(つまり、その素数に等しいいくつかの異なる値をブロックのデータ部分に収めることができる最大の素数)。有限フィールドの大きな利点は、除算(0を除く)が一意に定義され、常にそのフィールド内で値を生成することです。したがって、多項式の補間値は、入力値とまったく同じ方法でブロックに収まります。

入力を一連のブロックデータに変換するには、ベース変換を行う必要があります。入力をベース256から数値に変換し、ベースpに変換します(たとえば、放射パラメータ1の場合、p= 16381)。これは主に、Perlの基本変換ルーチンの欠如によって支えられていました(Math :: Prime :: Utilにはいくつかありますが、bignumの基底には機能せず、ここで扱う素数のいくつかは非常に大きいです)。既に多項式補間にMath :: Polynomialを使用しているため、「桁からの変換」関数として再利用することができました(多項式の係数として桁を表示し、評価することにより)、これはbignumで機能します結構です ただし、逆に言えば、自分で関数を記述する必要がありました。幸いにも、書くのはそれほど難しくありません(または冗長ではありません)。残念ながら、この基本変換は、入力が通常読み取り不能になることを意味します。先行ゼロに関する問題もあります。

出力にp個を超えるブロックを含めることはできません(そうしないと、2つのブロックのインデックスが等しくなりますが、多項式から異なる出力を生成する必要がある可能性があります)。これは、入力が非常に大きい場合にのみ発生します。このプログラムは、非常に簡単な方法で問題を解決します:放射を増加させます(ブロックを大きくし、pを大きくし、より多くのデータを収めることができ、明らかに正しい結果につながります)。

作成する価値のあるもう1つのポイントは、書き込まれたプログラムがそれ以外の場合にクラッシュするため、null文字列をそれ自体にエンコードすることです。また、明らかに最適なエンコーディングであり、放射パラメータが何であっても機能します。

潜在的な改善

このプログラムの主な漸近的な非効率性は、問題の有限体としてモジュロ素数を使用することです。サイズ2 nの有限フィールドが存在します(ブロックのペイロードサイズは自然に128の累乗であるため、ここでまさに必要です)。残念ながら、それらは単純なモジュロ構造よりもかなり複雑です。つまり、Math :: ModIntはそれをカットしません(非プライムサイズの有限フィールドを処理するためのCPANにライブラリが見つかりませんでした)。Math :: Polynomialのオーバーロードされた演算を使用してクラス全体を記述して処理する必要があり、その時点で、バイトコストは、たとえば16384ではなく16381の使用による(非常に小さな)損失を上回る可能性があります。

2のべき乗サイズを使用するもう1つの利点は、基本変換がはるかに簡単になることです。ただし、どちらの場合でも、入力の長さを表すより良い方法が役立ちます。「あいまいな場合に1を追加する」方法は単純ですが、無駄です。ここでは、全単射の基数変換がもっともらしいアプローチの1つです(考えは、基数を数字として、0を数字としてではなく、各数字が1つの文字列に対応するというものです)。

このエンコードの漸近的なパフォーマンスは非常に優れていますが(たとえば、長さ99の入力および放射パラメーター3の場合、エンコードは反復ベースのアプローチで得られる〜400バイトではなく、常に128バイトです)、そのパフォーマンス短い入力ではあまり良くありません。エンコードの長さは常に(放射パラメーター+ 1)の2乗以上です。したがって、放射9での非常に短い入力(長さ1から8)の場合、出力の長さは100です。(長さ9では、出力の長さは100であり、110である場合があります。) -非常に小さな入力に対するコーディングベースのアプローチ。入力のサイズに基づいて複数のアルゴリズム間で変更する価値があるかもしれません。

最後に、それは実際には得点されませんが、非常に高い放射パラメーターでは、各バイトのビット(出力サイズのof)を使用してブロックを区切ることは無駄です。代わりにブロック間に区切り文字を使用する方が安価です。区切り文字からブロックを再構築することは、交互MSBアプローチよりもかなり困難ですが、少なくともデータが十分に長い場合は可能だと思います(短いデータでは、出力から放射パラメータを推測することは困難です) 。パラメータに関係なく漸近的に理想的なアプローチを目指している場合、それは注目すべきことです。

(そしてもちろん、これよりも良い結果を生む完全に異なるアルゴリズムがあるかもしれません!)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.