Meta radyasyon sertleştirici


19

Arka fon

Bu sitede zaman zaman programların "radyasyonla sertleştirilmesini" gerektiren sorular var; bu, hangi baytlar silinirse sililsin, programın bir veya daha fazla bayt silme işleminden sağ çıkabilmesi gerektiği anlamına gelir.

Programlama zorluklarında sıklıkla belirlenen görevler için yaygın olduğu için, bu zorluklarda özellikle iyi bir dil yapmak istemek doğaldır. Bunu yapmanın doğal yolunun, yolsuzluğu tersine çevirmeyi mümkün kılan bazı meta veriler eklemek olduğu göz önüne alındığında, bu aslında tasarlanması gereken bir dil değil, bir kodlamadır; fikir, her girişi bir dizi bayt dizisine dönüştürmektir, öyle ki dizi biraz ışınlansa bile, orijinal girişi çıkarmak mümkündür.

Görev

E (kodlayıcı) ve D (kod çözücü) olmak üzere iki program veya işlev yazın, öyle ki:

  • E iki argüman alır, bir sekizli dizisi ( bu spesifikasyonda " girdi " olarak adlandıracağız ) ve negatif olmayan bir tam sayı " radyasyon " ve bir sekizli " kodlama " dizisi çıkarır ;
  • D bir argüman, bir sekizli dizisi (" encdng ") alır ve bir sekizli " yeniden yapılandırma " dizisi çıkarır ;
  • Hem E hem de D'yi çalıştırırsanız ( enkdng ile , D'ye giriş , kodlamadan radyasyon elemanlarından daha fazlasını silmeden seçilir (mutlaka bitişik olarak değil)), o zaman yeniden oluşturma, enkdng oluşturmak için hangi karakterlerin silinmiş olursa olsun girişe eşit olacaktır .

Açıklamalar

  • Eğer işlevleri gönderirseniz, onları aramak zorunda değilsiniz Eve D; diliniz için en uygun ismi seçebilirsiniz.
  • Bir "sekizli" temel olarak 0 ile 255 arasında bir tamsayıdır; bir tamsayı, bir karakter veya diliniz için uygun olan her şeyi kodlayabilirsiniz.
  • E ve D tamamen deterministik olmalıdır (yani aynı girişleri vermek her zaman aynı çıktıyı üretecektir, burada "girişler" E için giriş ve radyasyon veya D için enkod olarak tanımlanır ). Özellikle, E bir yan kanal vasıtasıyla bilgiyi D'ye iletemeyebilir.
  • Silme işlemleri, dizinin bir elemanı silinerek gerçekleştirilir; seuqence bir editörde açmayı, imleci keyfi bir noktaya yerleştirmeyi ve Backspace tuşuna basmayı düşünün. Bir öğe birden çok kez görünürse, öğenin yalnızca bir kopyasının silinmesi mümkündür (yani aynı sekizlinin diğer örnekleri etkilenmez).
  • Skor sadece oldukça kısa girdilere dayanarak hesaplanmasına rağmen, programınız herhangi bir girdi ve radyasyon için teoride çalışmalıdır . Özellikle, girdide hangi sekizlilerin görüneceği önemli değil . (Maalesef, bildikleri yazdırılamayan karakterleri kullanmak isteyenler girişte görünmeyecek, ancak girişin sıkıştırılamaz olduğundan emin olmalıyım, böylece zorluk sıkıştırmadan ziyade radyasyon sertleşmesi ile ilgilidir.)
  • İki işlevi tanımlayan her iki dosyayı da gönderebilirsiniz; her biri bir işlevi tanımlayan veya her ikisi de tam program olan iki dosya; ya da ikisi sırasıyla D ve E'yi uygulayan (ya tam programlar olarak ya da bir işlev tanımlayarak) ve üçü de D ve E için ortak olan bir başlık dosyası ya da kitaplık olan üç dosyadır. , programlama dili uygulamanızın her iki programı da dosya konumları gibi başka argümanlar olmadan anlayabilmesi gerekir (veya başka bir deyişle, standart kurallarımıza göre uygulamanızı alışılmadık bir şekilde çağırmak için bayt cezası ödemeniz gerekir).

Zafer koşulu

Her uzunluk ve radyasyon için , f ( uzunluk , radyasyon ) , uzunluk uzunluğundaki tüm girdilere ve verilen radyasyona karşılık gelen kodlamaların toplam uzunlukları olsun . (Yani, f ( uzunluk , radyasyon ) = toplam girişin uzunluk uzunluk uzunluğuna (E ( giriş , radyasyon )) sahiptir.) Sonra g ( uzunluk , radyasyon ) eşit f ( uzunluk ,radyasyon ) ÷ 256 uzunluk . Başka bir deyişle g , belirli bir girdi uzunluğu ve belirli bir radyasyon sertleştirme gereksinimi için kodlanmış çıktının ortalama uzunluğudur. (Teorik olarak bunu kaba kuvvetle hesaplayabilirsiniz, ancak puanınızı bu şekilde çözmek muhtemelen uzun sürecektir. Çoğu başvurunun puanlarının ne olduğuna ilişkin matematiksel bir tartışma yapabileceğini umuyorum. emin olun, yaklaşık bir puan gönderin ve başka bir giriş benzer bir puan gönderirse siz veya başka biri daha derinlemesine hesaplayabilirsiniz.)

Puanınız toplamına eşittir g ( uzunluk , radyasyon herkes için) radyasyona 0 ila 9 kapsayıcı ve tüm aralığında uzunluğu 0 ile 99 dahil, artı (çoğunlukla önlemek gömmek için aralığında, ya da rekabet halinde devam etmek Birisi matematiksel olarak mükemmel bir kodlama keşfeder; bu aksi takdirde minimum bir faktör olabilir) meydan okumaya göndermenizdeki toplam bayt sayısı (artı olağandışı tercüman bayrakları veya belirli dosya adları gibi şeyler için standart cezalar). Kazanan, en düşük puana sahip olan giriştir (gönderilecek ilk giriş tarafından tiebroken).


Dekoder, radyasyon parametresini de bilebilir mi?
orlp

(veya uzunluk , ama ben biliyorum ki ya da çoğu şemaları için diğerini vermek gerektiğini bilmek)
orlp

1
@orlp: Hayır, yalnızca dizesi var. Bir de radyasyon sertleştirme problemi, dekoder (yani dil) dekoder onları da bilmiyor böylece radyasyon kuralları, kullanılan bilmez; onları girdilerinden çıkarmak zorundadır.

Bu bilgisayar videosuna dayanarak, 3 baytlık üçüzlerde kodlayıcıları alan bir dil yaparım: hepsi eşleşmezse, bir şeyler ters gitti ve doğru değerin ne olduğunu anlamak için yeterli bilgiye sahibiz. Muhtemelen daha az bitle yapmanın bir yolu var ama şu anda nasıl çalışacağımı beynim yok.
Draco18s

Üstbilgi ve programları nasıl birleştirirsiniz?
CalculatorFeline

Yanıtlar:


8

CJam, puan ≤ 286.516 + 54 + 36 = 286.606

Encoder

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

Çevrimiçi deneyin!

şifre çözücü

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

Çevrimiçi deneyin!

Her ikisi de bir tamsayı alır ve döndürür. TIO bağlantıları kolaylık sağlamak için dizelerden / dizelere dönüştürmeyi içerir. Bunların uzun dizeler için inanılmaz derecede verimsiz olduğunu unutmayın. Birkaç karakter daha denemek isterseniz, küçük karakter kodlarına sahip karakterleri kullanmanızı öneririm.

Radyasyonla sertleştirilmiş kodlama oluşturmak için temel fikir iki adımdan oluşur:

  1. Hiçbir zaman iki ardışık özdeş sekizli içermeyen bir kodlama bulun.
  2. Her sekizli kodlanmış dize r + 1 kez tekrarlayın, burada r radyasyon seviyesidir.

Bu şekilde, radyasyon özdeş karakterlerin bir çalışmasını tamamen silemez, böylece her çalışmadan bir karakter alarak ve ardından adım 1'in kodunu çözerek dizeyi deşifre edebiliriz.

Tek ilginç kısım, asla tekrarlanan sekizlileri vermeyen bir kodlama bulmaktır. Temel fikir, sayı sistemi olarak A043096 gibi bir şey kullanmaktır . Yani, bir tamsayıyı N kodlamak için, bazı baz b'de sayıyoruz , tekrarlanan sekizli ile tüm sayıları atlıyoruz. Bu şekilde d haneye kadar temsil edilebilecek sayıların miktarının bijektif baz b-1'de temsil edilebilecek sayıların miktarı ile aynı olduğuna inanıyorum (çünkü, böyle bir sayı yazmak istediğinizde, kısıtlamayı ihlal etmeden her bir konum için b-1 hanesi arasında seçim yapın ).

Tabii ki, maksimum sıkıştırma elde etmek için b = 256 kullanacağız . Girdiyi bir tamsayıya dönüştürmek için taban dönüşümünü de kullanabiliriz. Eğer tembel olmasaydım, girdi için iki yönlü bir taban kullanırdım, ama şimdilik sadece 1(önde gelen sıfırlar olmadığından emin olmak için) hazırlıyorum ve sonra mümkün olan en küçük üssü kullanıyorum. girdi tabandan küçük.

Bu baz daha sonra kodlamaya eklenir (böylece kod çözücü hangi tabanın kullanılacağını bilir) ve kalan sayıdan 0 sekizli ile ayrılır (bu çalışır, çünkü kalan sayı asla sıfır ile başlamaz). Küçük bir optimizasyon olarak, boş dize boş bir dize olarak kalır.

Yukarıda kesin bir puan hesaplamamamın nedeni, her bir girişin uzunluğuna ve maksimum sekizliğine bağlı olacağı için sadece bir üst sınır hesaplamamdır. Bununla birlikte, bu iki parametre için, genellikle iki farklı çıkış uzunluğu olacaktır ve aralarındaki devrilme noktasının nerede oluştuğunu henüz anlamadım. Ayrıca, uzunluğunu tahmin etmek için, bijektif taban 255 yerine normal taban 255'in uzunluğunu da kullandım, ki bu da olması gerekenden biraz daha büyük. Hesaplama için kullandığım tam Mathematica kodu şöyledir:

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]lmaksimum sekizli ile uzunluk dizeleri sayısını vermelidir b-1( her zaman en az baz kullandığım b == 1için sabit kodladığım hariç ).02


"Ortalama olarak bir uzunluk N dizesinin, r radyasyon seviyesinde (r + 1) * N sekizliden daha azıyla kodlanamayacağı varsayılarak." Bunun doğru olması için hiçbir neden göremiyorum. O (N + r) olan bir kodlama şemasını gördüğüme şaşırmam.
orlp

1
@orlp Bunun nasıl mümkün olacağını görmüyorum, ancak yanlış kanıtlanmayı dört gözle bekliyorum. :)
Martin Ender

İyi fikir. CJam'ı bilmiyorum, ancak açıklamanızdan, tabanı kodlanmış verilere hazırladığınız anlaşılıyor. Bu durumda, eklenmiş verilerden silinen karakterler varsa bir sorun var mı? (Bu, @Leo'nun benim çözümümde düzeltmem gerektiğini belirttiğini söylediğim hata.)
Mitchell Spector

@MitchellSpector her karakter r + 1 kez tekrarlanmadan önce tabana eklenir. Böylece baz radyasyona karşı da güvenlidir.
Martin Ender

Bu iyi - çözümümde de bunu yaptım. Sadece kod çözücünün, bazın ne olduğunu bilmeden önce, eklenen verilerin kodunu çözebileceğinden emin olmanız gerekir.
Mitchell Spector

6

bash + GNU programları, skor 294506 283468

Düzenleme 1: @Leo'nun fark ettiği bir sorunu düzeltir - teşekkürler!

Edit 2: Daha iyi bir skor için radyasyon parametresi için kodlama yöntemi geliştirildi.

Kodlayıcı (97 bayt):

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

Kod çözücü (121 bayt):

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

Kodlayıcı için: Octet dizisi stdin'de karakterler olarak geçti, r radyasyon parametresi bir argüman olarak geçti.

Kod çözücü için: Girdi stdin'de karakter olarak geçti.

Her ikisi için: Çıkış stdout.

Kodlayıcı, herbiri ardışık özdeş çift çifti arasına 'a' karakteri eklenmiş olarak r hanelerinin giriş verilerine öncelik verir, ardından tek bir satırsonu gelir. Daha sonra, her bir karakteri o karakterin r + 1 kopyalarıyla değiştirerek tüm girdiyi (ekli karakterlerden başlayarak) kopyalar.

Kod çözücü bunu geri alır, girişindeki kalan x karakterlerinin her birini geçerek, x'in ardından x'in ardışık özdeş kopyalarını atlar ve kalanları yazdırır. Eklenen verilerin tekrarlanan karakteri yoktur, bu nedenle r bilinmeden önce kodu çözülebilir. Bu noktada, r bilinir ve geri kalan verinin doğru bir şekilde kodunu çözmek için bu değere ihtiyaç duyulur.

Orijinal girişte tekrarlanan aynı karakterler olsa bile bunun çalışıp çalışmadığını kontrol edebilirsiniz.


Puan hesaplaması:

Girişin L uzunluğuna sahip olduğunu ve radyasyon parametresinin r olduğunu varsayalım (puanlama hesaplaması için en fazla 9'dur, bu nedenle bir basamağa sığar ve bu nedenle art arda tekrarlanan karakterleri yoktur). Beklenen veriler 2 bayttır (basamak, yeni satır), dolayısıyla çıktı kodlanmış akış için (r + 1) (L + 2) bayttır.

Böylece g (L, r) = (r + 1) (L + 2).

Toplam puan şu şekilde hesaplanabilir:

resim açıklamasını buraya girin


Ya ilk oktet düşerse? Kod çözücünün rokuması gerekmeyecek
Leo

@Leo Haklısın. Bunu yarın düzeltmeye bakacağım - bu gece çok geç. Tespit ettiğiniz için teşekkürler.
Mitchell Spector

@Leo Bence r rakamlarının her birinin r + 1 kopyasını ve ardından r + 1 satırsonu ekleyerek düzeltilebilir. Eğer bu doğruysa, skor fazla yükselmez.
Mitchell Spector

Böyle bir şey işe yaramalı. Daha yüksek radyasyonlarla (örneğin bir radyasyon 222) düzgün çalıştığından emin olmak için bazı ek önlemler almanız gerektiğini düşünüyorum , ancak neyse ki skor 0-9 radyasyonlarının biraz üzerinde hesaplanıyor, bu yüzden fazla etkilenmeyecek. PS Aynı kodlamayı uygulamayı düşünüyordum, bu yüzden hatayı hemen fark ettim;)
Leo

@Leo Evet, düzeltme, radyasyon için tüm değerler için çalışır, ancak skor en fazla 9 radyasyon değerini hesaba katsa bile
Mitchell Spector

3

Perl + Math :: {ModInt, Polinom, Başbakan :: Util}, skor 28 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/^␁//;$_}

Kontrol resimleri, karşılık gelen kontrol karakterini temsil etmek için kullanılır (örneğin , bir değişmez NUL karakteridir). Kodu okumaya çalışmayla ilgili endişelenmeyin; aşağıda daha okunabilir bir sürümü var.

İle çalıştırın -Mbigint -MMath::ModInt=mod -MMath::Polynomial -MNtheory=:all. -MMath::Bigint=lib,GMPgerekli değildir (ve dolayısıyla skora dahil edilmez), ancak diğer kütüphanelerden önce eklerseniz, programın biraz daha hızlı çalışmasını sağlar.

Puan hesaplama

Buradaki algoritma biraz geliştirilebilir, ancak yazmak daha zor olacaktır (Perl'in uygun kütüphanelere sahip olmaması nedeniyle). Bu nedenle, kodda baytların kaydedilebileceği göz önüne alındığında, kodda birkaç boyut / verimlilik dengesi yaptım, golfün her noktasını tıraş etmeye çalışmanın bir anlamı yok.

Program, 600 bayt kod ve ayrıca komut satırı seçenekleri için 78 bayt cezadan oluşur ve 678 puan ceza verir. Skorun geri kalanı, 0 ila 99 arasındaki her uzunluk ve 0 ila 9 arasındaki her radyasyon seviyesi için programın en iyi durumdaki ve en kötü durumdaki (çıkış uzunluğu açısından) dizesinde çalıştırılmasıyla hesaplanmıştır; ortalama vaka aradaki bir yerde ve bu da skorun sınırlarını veriyor. (Benzer bir puanla başka bir giriş gelmediği sürece kesin değeri hesaplamaya çalışmak değmez.)

Bu nedenle, kodlama verimliliğinden alınan puanın 91100 ila 92141 dahil olduğu anlamına gelir, bu nedenle nihai puan:

91100 + 600 + 78 = 91778 ≤ skor ≤ 92819 = 92141 + 600 + 78

Yorumlar ve test koduyla daha az golf oynayan versiyon

Bu orijinal program + yeni satırlar, girinti ve yorumlar. (Aslında, golf edilmiş sürüm bu sürümden satırsonu / girinti / yorum kaldırılarak üretildi.)

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;

Algoritma

Sorunu basitleştirme

Temel fikir, bu "silme kodlama" problemini (yaygın olarak araştırılan bir sorun değildir) bir silme kodlama problemine (kapsamlı olarak keşfedilen bir matematik alanı) indirmektir. Silme kodlamasının ardındaki fikir, bazen gönderdiği karakterleri bir hatanın bilinen konumunu gösteren "garble" karakteriyle değiştiren bir kanal olan "silme kanalı" üzerinden gönderilecek verileri hazırlamanızdır. (Başka bir deyişle, orijinal karakter hala bilinmemekle birlikte, yolsuzluğun nerede olduğu her zaman açıktır.) Bunun arkasındaki fikir oldukça basittir: girişi uzunluk bloklarına ( radyasyon) bölüyoruz+ 1) kullanın ve veri için her bloktaki sekiz bitin yedisini kullanın; kalan bit (bu yapımda MSB), tüm blok için ayarlanmak, blok için ayarlanan bir sonraki tüm blok için temizlenmek arasında değişir. ondan sonra, vb. Bloklar radyasyon parametresinden daha uzun olduğu için, her bloktan en az bir karakter çıktıda kalır; böylece aynı MSB'ye sahip karakterleri çalıştırarak, her karakterin hangi bloğa ait olduğunu bulabiliriz. Blokların sayısı da her zaman radyasyon parametresinden daha fazladır, bu nedenle her zaman enkodda en az bir hasarsız blok vardır; bu nedenle en uzun veya en uzun süre bağlı tüm blokların hasarsız olduğunu biliyoruz, böylece daha kısa blokları hasarlı (böylece bir garble) olarak tedavi etmemize izin veriyoruz. Ayrıca radyasyon parametresini şu şekilde çıkarabiliriz ('

Silme kodlaması

Sorunun silinme kodlama kısmına gelince, bu, Reed-Solomon yapısının basit bir özel durumunu kullanır. Bu sistematik bir yapıdır: çıktı (silme kodlama algoritmasının) girişe ve radyasyon parametresine eşit bir dizi ekstra bloğa eşittir. Bu bloklar için gereken değerleri basit (ve golfçü!) Bir şekilde, onları çile muamelesi yaparak, sonra değerlerini "yeniden yapılandırmak" için kod çözme algoritmasını çalıştırarak hesaplayabiliriz.

Yapının arkasındaki gerçek fikir de çok basittir: kodlamadaki tüm bloklara (diğer elemanlardan enterpolasyonlu çelenklerle) mümkün olan en düşük derecede bir polinom yerleştiriyoruz; eğer polinom f ise, ilk blok f (0), ikincisi f (1), vb. Polinom derecesinin giriş eksi 1'in blok sayısına eşit olacağı açıktır (çünkü önce bir polinomu yerleştiririz, daha sonra ekstra "kontrol" bloklarını oluşturmak için kullanırız); ve d +1 puanları benzersiz olarak d derece polinomunu tanımlar, herhangi bir sayıda bloğun (radyasyon parametresine kadar) garberleştirilmesi, aynı polinomu yeniden inşa etmek için yeterli bilgi olan orijinal girişe eşit sayıda hasarsız blok bırakacaktır. (O zaman sadece bir bloğu çözmek için polinomu değerlendirmeliyiz.)

Temel dönüşüm

Burada kalan son husus, bloklar tarafından alınan gerçek değerlerle ilgilidir; tamsayılarda polinom enterpolasyonu yaparsak, sonuçlar rasyonel sayılar (tamsayılardan ziyade), giriş değerlerinden çok daha büyük veya istenmeyen bir sonuç olabilir. Bu nedenle, tamsayıları kullanmak yerine sonlu bir alan kullanırız; Bu programda, kullanılan sonlu alan modulo p tamsayıları alanıdır , burada p 128 radyasyondan küçük en büyük asaldır +1(yani, bir bloğun veri kısmına o prime eşit sayıda farklı değer sığdırabileceğimiz en büyük asal). Sonlu cisimlerin en büyük avantajı, bölünmenin (0 hariç) benzersiz bir şekilde tanımlanmış olması ve bu alanda daima bir değer üretmesidir; dolayısıyla, polinomların enterpolasyonlu değerleri, giriş değerlerinin yaptığı gibi bir bloğa sığacaktır.

Veri bloğu bir dizi giriş dönüştürmek amacıyla, o zaman, taban dönüşüm yapmak gerekir: bu durumda baz dönüştürmek, bir dizi halinde bir baz 256 girdi dönüştürmek p , örneğin bir için ( radyasyon 1 parametresi, elimizdeki p= 16381). Bu çoğunlukla Perl'in temel dönüşüm rutinleri eksikliğinden kaynaklandı (Math :: Prime :: Util bazılarına sahiptir, ancak bignum bazları için çalışmazlar ve burada çalıştığımız bazı primler inanılmaz derecede büyüktür). Zaten polinom enterpolasyonu için Math :: Polynomial'ı kullandığımız için, bunu bir "basamak dizisinden dönüştür" işlevi olarak tekrar kullanabildim (basamakları bir polinomun katsayıları olarak görüp değerlendirerek) ve bu bignumlar için çalışıyor sadece iyi. Diğer taraftan, işlevi kendim yazmak zorunda kaldım. Neyse ki, yazmak çok zor değil (veya ayrıntılı). Ne yazık ki, bu temel dönüşüm, girdinin tipik olarak okunamaz hale getirildiği anlamına gelir. Baştaki sıfırlarla ilgili bir sorun da var;

Çıktıda p'den fazla bloğa sahip olamayacağımız not edilmelidir (aksi takdirde iki bloğun indeksleri eşit olur ve yine de polinomdan farklı çıktılar üretmesi gerekir). Bu yalnızca giriş çok büyük olduğunda gerçekleşir. Artan: Bu program çok basit bir yolu sorunu çözer radyasyon (bloklar daha büyük ve yapar p biz doğru sonuca çok daha verileri ve net bir şekilde yol sığabilecek anlamına çok daha büyük).

Yapmaya değer bir diğer nokta ise, boş dizeyi kendisine kodlamamızdır, çünkü yazılı olarak program aksi takdirde çökecektir. Aynı zamanda mümkün olan en iyi kodlamadır ve radyasyon parametresi ne olursa olsun çalışır.

Potansiyel iyileştirmeler

Bu programdaki ana asimtotik verimsizlik, söz konusu sonlu alanlar olarak modulo-prime kullanımı ile ilgilidir. 2 n büyüklüğünde sonlu alanlar mevcuttur (bu tam olarak burada istediğimiz şeydir, çünkü blokların taşıma yükü boyutları doğal olarak 128 güçtür). Ne yazık ki, basit bir modulo yapısından daha karmaşıktırlar, yani Math :: ModInt onu kesmez (ve asal olmayan boyutların sonlu alanlarını işlemek için CPAN'da herhangi bir kütüphane bulamadım); Math :: Polynomial ile başa çıkabilmek için aşırı yüklenmiş aritmetik ile tüm bir sınıf yazmak zorundayım ve bu noktada bayt maliyeti, 16384 yerine 16381 yerine (çok küçük) kaybın üzerinde olabilir.

2 güç boyutu kullanmanın bir diğer avantajı, temel dönüşümün çok daha kolay hale gelmesidir. Bununla birlikte, her iki durumda da, girdinin uzunluğunu temsil etmek için daha iyi bir yöntem yararlı olacaktır; "belirsiz durumlarda 1 başına" yöntemi basit ama israf. Bijektif taban dönüşümü burada mantıklı bir yaklaşımdır (fikir, tabana rakam olarak değil, rakam olarak 0'a sahip olmanızdır, böylece her sayı tek bir dizeye karşılık gelir).

Bu kodlamanın asimptotik performansı çok iyi olmasına rağmen (örneğin 99 uzunluğundaki bir giriş ve 3 radyasyon parametresi için, kodlama her zaman tekrarlanan yaklaşımların alacağı ~ 400 bayt yerine 128 bayt uzunluğundadır), performansı kısa girdilerde daha az iyidir; kodlamanın uzunluğu daima en azından (radyasyon parametresi + 1) karesidir. Bu nedenle radyasyon 9'daki çok kısa girişler (uzunluk 1 ila 8) için, çıkışın uzunluğu yine de 100'dür. (Uzunluk 9'da, çıkışın uzunluğu bazen 100 ve bazen 110'dur.) Tekrar tabanlı yaklaşımlar bu silmeyi açıkça yener. - çok küçük girdilere kodlama tabanlı yaklaşım; girişin boyutuna bağlı olarak birden fazla algoritma arasında değişmeye değer olabilir.

Son olarak, skorlamada gerçekten ortaya çıkmaz, ancak çok yüksek radyasyon parametreleriyle, blokları sınırlamak için her bayttan biraz (çıktı boyutunun)) kullanılması israftır; bunun yerine bloklar arasında sınırlayıcılar kullanmak daha ucuz olacaktır. Blokları sınırlayıcılardan yeniden oluşturmak, alternatif MSB yaklaşımından daha zordur, ancak en azından veriler yeterince uzunsa (kısa verilerle, radyasyon parametresini çıktıdan çıkarmak zor olabilir) mümkün olduğuna inanıyorum. . Parametrelere bakılmaksızın asimptotik olarak ideal bir yaklaşımı hedefliyorsa bakılması gereken bir şey olurdu.

(Ve elbette, bundan daha iyi sonuçlar veren tamamen farklı bir algoritma olabilir!)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.