Металевий затверджувач


19

Фон

На цьому веб-сайті у нас періодично виникають запитання, які вимагають «радіаційного загартування» програм; це означає, що програма повинна мати можливість пережити видалення одного або декількох байтів, незалежно від того, які байти видаляються.

Як звичайно для завдань, які часто ставляться перед завданнями програмування, природно хотіти створити мову, яка особливо добре підходить для цих завдань. Зважаючи на те, що природний спосіб зробити це - додати деякі метадані, що дозволяють повернути корупцію, насправді це не мова, яка потребує проектування, а кодування; ідея полягає в тому, щоб перетворити кожен вхід у послідовність байтів таким чином, що навіть якщо послідовність злегка опромінена, можливо витягнути вихідний вхід.

Завдання

Напишіть дві програми або функції, E (кодер) і D (декодер), таким чином:

  • E приймає два аргументи, послідовність октетів (які ми будемо називати " вхідними " в цій специфікації) і негативне ціле число " випромінювання ", і виводить послідовність октетів " кодування ";
  • D приймає один аргумент, послідовність октетів (" encdng ") і виводить послідовність октетів " реконструкція ";
  • Якщо ви запускаєте як E, так і D (з encdng , вхід до D, вибраний шляхом видалення не більше ніж елементів випромінювання з кодування (не обов'язково безперервно)), то реконструкція буде дорівнює вводу, незалежно від того, які символи були видалені, щоб утворити encdng .

Роз'яснення

  • Якщо ви передаєте функції, ви не повинні називати їх Eі D; ви можете вибрати будь-яке ім’я, яке найбільше підходить для вашої мови.
  • "Октет" - це ціле число від 0 до 255 включно, яке ви можете кодувати як ціле число, символ або будь-яке, що підходить для вашої мови.
  • E і D повинні бути повністю детермінованими (тобто надання їм однакових входів завжди дасть однаковий вихід, де "входи" визначаються як вхід і випромінювання для E, або encdng для D). Зокрема, E може не передавати інформацію D через бічний канал.
  • Видалення виконуються шляхом видалення одного елемента послідовності; подумайте відкрити наступність у редакторі, розмістити курсор у довільній точці та натиснути Backspace. Якщо елемент з’являється декілька разів, можливо, буде видалена лише одна копія елемента (тобто інші екземпляри того ж октету не вплинуть).
  • Хоча оцінка оцінюється лише на основі досить короткого введення , програма повинна теоретично працювати для будь-якого введення та випромінювання . Зокрема, він повинен працювати незалежно від того, які октети відображаються у вводі . (Вибачте, люди, які хотіли б можливість використовувати недруковані символи, які вони знають, не з’являться на вході, але мені потрібно переконатися, що вхід є нестислимим, щоб виклик ставився до загартовування радіацією, а не стиснення.)
  • Ви можете подати або один файл, який визначає дві функції; два файли, кожен з яких визначає функцію або які є повноцінними програмами; або три файли, два з яких реалізують D і E відповідно (або через повну програму, або через визначення функції), а третій - це файл заголовка або бібліотеку, спільний для D і E. Незалежно від того, яку форму подання ви використовуєте , ваша реалізація мови програмування повинна бути в змозі зрозуміти обидві програми без додаткових аргументів, таких як розташування файлів (інакше ви повинні сплатити байтовий штраф за виклик вашої реалізації незвичним способом, згідно з нашими стандартними правилами).

Стан перемоги

Для кожної довжини і випромінювання нехай f ( довжина , випромінювання ) - це загальна довжина кодуючого s, що відповідає всім входам з довжиною довжини , і заданому випромінюванню . (Тобто f ( довжина , випромінювання ) = сума введення має довжину довжини довжини (E ( вхід , випромінювання )).) Тоді нехай g ( довжина , випромінювання ) дорівнює f ( довжина ,випромінювання ) ÷ 256 довжина . Іншими словами, g - середня довжина кодованого виходу для заданої довжини введення та заданої вимоги до випромінювання. (Теоретично ви могли обчислити це грубою силою, але, швидше за все, знадобиться багато часу, щоб таким чином опрацювати свій рахунок. Я очікую, що більшість заявок зможе зробити математичний аргумент щодо того, який їх рахунок. Якщо ви ' знову не впевнений, опублікуйте приблизний бал, і ви чи хтось інший зможете обчислити його більш глибоко, якщо інший запис розміщує аналогічний бал.)

Ваш бал дорівнює сумі g ( довжина , випромінювання ) для всіх випромінювань в діапазоні від 0 до 9 включно і всієї довжини в діапазоні від 0 до 99 включно плюс (переважно для уникнення жорсткого кодування або для продовження конкуренції, якщо хтось виявляє математично досконале кодування; це, мабуть, мінімальний коефіцієнт, інакше) загальна кількість байтів, які ви подали на виклик (плюс стандартні штрафи за речі, такі як вимагати незвичних прапорців перекладача або конкретних імен). Переможець - це запис із найнижчою оцінкою (розбитий на перший запис, який потрібно подати).


Чи може декодер також знати параметр випромінювання ?
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 для зручності включають перетворення з / в рядки. Зауважте, що вони є неймовірно неефективними для довших рядків. Якщо ви хочете спробувати ще кілька символів, рекомендую використовувати символи з малими кодами символів.

Основна ідея створення кодуваного радіацією кодування включає два етапи:

  1. Знайдіть кодування, яке ніколи не містить двох послідовних однакових октетів.
  2. Повторіть кожен октет у кодованому рядку r + 1 раз, де r - рівень випромінювання.

Таким чином, випромінювання не може повністю видалити один запуск однакових символів, так що ми можемо розшифрувати рядок, взявши по одному символу з кожного запуску, а потім розшифруючи етап 1.

Тож єдиною цікавою частиною є пошук кодування, який ніколи не дає повторних октетів. Основна ідея - використовувати щось на зразок A043096 в якості системи числення. Тобто для кодування цілого числа N ми просто підраховуємо деяку базу b , пропускаючи всі числа з повторними октетами. Я вважаю, що кількість чисел, яка може бути представлена ​​таким чином з до d цифр, така сама, як кількість чисел, яка може бути представлена ​​в біективної базі b-1 (оскільки, коли ви хочете написати таке число, ви можете вибирати між b-1 цифрою для кожної позиції, не порушуючи обмеження).

Звичайно, для отримання максимальної компресії будемо використовувати b = 256 . Щоб перетворити вхід на ціле число, ми також можемо використовувати базове перетворення. Якби я не лінувався, я би використовував бієктивну базу для введення, але поки що я просто готую 1(щоб переконатися, що немає провідних нулів), а потім використовую найменшу можливу базу, таку, що всі октети в вхід менше, ніж базовий.

Потім ця база готується до кодування (щоб декодер знав, яку базу використовувати) та відокремлював від числа, що залишився, 0-октетом (це працює, тому що решта номер ніколи не починається з нуля). Як незначна оптимізація, порожня рядок залишається порожньою.

Причиною того, що я не нарахував точну оцінку вище, є те, що я обчислюю лише верхню межу, на скільки часу кожен вхід буде базуватися на його довжині та максимальному октеті. Однак для цих двох параметрів часто будуть дві різної довжини виходу, і я ще не намагався з'ясувати, де знаходиться точка перекидання між ними. Я також використовував довжину звичайної основи 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(за винятком тих випадків b == 1, де я це жорстко кодував, 0тому що я завжди використовую принаймні базу 2).


"Якщо припустити, що в середньому рядок довжиною N не може бути закодований менше (r + 1) * N октетів на рівні випромінювання r." Я не бачу причин, щоб це було правдою. Я не був би здивований, побачивши існуючу схему кодування, яка є 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 передається як аргумент.

Для декодера: Введення передається як символи в stdin.

Для обох: вихід на stdout.

Кодер попередньо додає до вхідних даних цифри r із символом 'a', вставленим між кожною парою послідовних однакових цифр, з наступним новим рядком. Потім він копіює весь вхід (починаючи з попередньо передбачених символів), замінюючи кожен символ r + 1 копіями цього символу.

Дешифратор скасовує це, перебираючи кожен із символів x, що залишилися у своєму вході, пропускаючи до r послідовних однакових копій x після x та друкуючи те, що залишилося. Заздалегідь задані дані не мають повторних символів, тому їх можна декодувати до того, як стане відомо r. У цей момент r відомий, і це значення потрібно для правильного декодування решти даних.

Ви можете перевірити, що це працює, навіть якщо в оригінальному введенні є повторення однакових символів.


Вирахування балів:

Припустимо, вхід має довжину L, а параметр випромінювання - r (що становить не більше 9 для підрахунку підрахунку, тому він вписується в одну цифру і тому не має послідовних повторних символів). Заздалегідь передбачені дані - 2 байти (цифра, новий рядок), тому вихід - (r + 1) (L + 2) байт для кодованого потоку.

Отже, g (L, r) = (r + 1) (L + 2).

Звідси випливає, що загальний бал можна обчислити як

введіть тут опис зображення


Що робити, якщо скинутий октет є першим? Декодер не мав rби читати
Лев,

@Leo Ви маєте рацію. Подивлюсь, як це виправити завтра - сьогодні вже пізно. Дякуємо, що помітили його.
Мітчелл Спектор

@Leo Я думаю, що це можна виправити, включивши r + 1 копії кожної з цифр r, а потім 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) і використовувати сім з восьми біт у кожному блоці для отримання даних, тоді як решта біт (у цій конструкції, MSB) чергується між встановленням для цілого блоку, зрозумілим для всього наступного блоку, встановленим для блоку після цього тощо. Оскільки блоки довші, ніж параметр випромінювання, щонайменше один символ з кожного блоку виживає на виході; тож, беручи пробіги символів з тим самим MSB, ми можемо розібратися, до якого блоку належав кожен символ. Кількість блоків також завжди більша, ніж параметр випромінювання, тому ми завжди маємо принаймні один непошкоджений блок у encdng; Таким чином, ми знаємо, що всі блоки, які є найдовшими або зв'язані довше, не пошкоджені, що дозволяє нам обробляти будь-які короткі блоки як пошкоджені (таким чином, пошкодження). Ми також можемо вивести такий параметр випромінювання, як цей (він '

Кодування стирання

Що стосується частини проблеми, що кодує стирання, то тут використовується простий спеціальний випадок конструкції Ріда-Соломона. Це систематична побудова: вихід (алгоритму кодування стирання) дорівнює входу плюс ряд додаткових блоків, рівний параметру випромінювання. Ми можемо обчислити значення, необхідні для цих блоків, простим (і гольф!) Способом, трактуючи їх як шаблони, а потім запустивши на них алгоритм декодування, щоб "реконструювати" їх значення.

Справжня ідея, що стоїть за конструкцією, також дуже проста: ми підключаємо поліном мінімально можливого ступеня до всіх блоків в кодуванні (з шаблонами, інтерпольованими з інших елементів); якщо многочлен f , перший блок - f (0), другий - f (1) тощо. Зрозуміло, що ступінь многочлена буде дорівнює кількості блоків введення мінус 1 (тому що ми підходимо поліном до тих, хто спочатку, а потім використовуємо його для побудови додаткових «перевіряючих» блоків); і оскільки д +1 точки однозначно визначають многочлен ступеня г, ушивання будь-якої кількості блоків (до параметра випромінювання) залишить кількість непошкоджених блоків, рівних вихідному входу, що є достатньою інформацією для відновлення того ж многочлена. (Тоді ми просто повинні оцінити поліном, щоб не відміняти блок.)

Базова конверсія

Остаточний розгляд, що залишився тут, стосується фактичних значень, прийнятих блоками; якщо ми робимо поліноміальну інтерполяцію на цілі числа, результати можуть бути раціональними числами (а не цілими числами), набагато більшими, ніж вхідні значення, або інакше небажаними. Таким чином, замість цілих чисел ми використовуємо скінченне поле; у цій програмі використовуваним кінцевим полем є поле цілих чисел по модулю p , де р - найбільший простір менше 128 випромінювання +1(тобто найбільший простір, для якого ми можемо помістити ряд чітких значень, рівних цьому простим, у частину даних блоку). Великою перевагою кінцевих полів є те, що поділ (за винятком 0) є однозначно визначеним і завжди створюватиме значення в цьому полі; таким чином, інтерпольовані значення поліномів помістяться в блок точно так само, як і вхідні значення.

Для того, щоб перетворити вхід в ряд даних блоку, тоді нам потрібно зробити перетворення бази: перетворити вхід з бази 256 в число, потім перетворити в базу p (наприклад, для параметра випромінювання 1, маємо p= 16381). Це здебільшого стримувалося відсутністю підпрограми перетворення базових програм Perl (Math :: Prime :: Util має деякі, але вони не працюють для баз bignum, і деякі з праймів, з якими ми тут працюємо, неймовірно великі). Оскільки ми вже використовуємо Math :: Polynomial для поліноміальної інтерполяції, я зміг повторно використовувати його як функцію "перетворити з цифрної послідовності" (переглянувши цифри як коефіцієнти полінома та оцінивши його), і це працює для bignums просто добре. Йдучи іншим шляхом, я мусив сам писати функцію. На щастя, писати не дуже складно (або багатослівно). На жаль, це базове перетворення означає, що введення зазвичай стає нечитабельним. Існує також проблема з першими нулями;

Слід зазначити, що у виході ми не можемо мати більше p блоків (інакше індекси двох блоків стали б рівними, але, можливо, потрібно отримати різні виходи з многочлена). Це відбувається лише тоді, коли вхід надзвичайно великий. Ця програма вирішує проблему дуже простим способом: збільшуючи радіацію (це робить блоки більші, а p значно більшими, це означає, що ми можемо помістити набагато більше даних і що явно призводить до правильного результату).

Ще один момент, який варто зробити, це те, що ми кодуємо нульову рядок собі, тому що програма, як написано, на неї вийде з ладу. Це також очевидно найкраще можливе кодування, і воно працює незалежно від параметрів випромінювання.

Потенційні поліпшення

Основна асимптотична неефективність у цій програмі полягає у використанні модулю-прайме як кінцевих полів, про які йдеться. Кінцеві поля розміром 2 n існують (це саме те, що ми хотіли б тут, оскільки розміри корисної навантаження блоків, природно, є потужністю 128). На жаль, вони є більш складними, ніж проста модульна конструкція, тобто Math :: ModInt не вирізав би її (і я не міг знайти жодної бібліотеки на CPAN для обробки обмежених полів непрості розміри); Я мав би написати цілий клас із перевантаженою арифметикою для Math :: Polynomial, щоб мати змогу впоратися з цим, і в цей момент вартість байта може потенційно перевищувати (дуже малі) втрати від використання, наприклад, 16381, а не 16384.

Ще одна перевага використання розмірів потужності-2 полягає в тому, що базове перетворення стане набагато простішим. Однак у будь-якому випадку кращим методом представлення довжини введення був би корисний; метод "додати 1 в неоднозначних випадках" простий, але марнотратний. Перетворення біективної бази - це один правдоподібний підхід (ідея полягає в тому, що ви маєте базу як цифру, а 0 як не цифру, так що кожне число відповідає одному рядку).

Хоча асимптотична ефективність цього кодування дуже хороша (наприклад, для вводу довжиною 99 та параметром випромінювання 3, кодування завжди має довжину 128 байт, а не ~ 400 байт, які отримали б підходи, засновані на повторенні), його продуктивність менш хороший на коротких входах; довжина кодування завжди не менше квадрата (параметр випромінювання + 1). Отже, для дуже коротких входів (довжина від 1 до 8) при випромінюванні 9 довжина виходу, тим не менш, становить 100. (При довжині 9 довжина виходу іноді становить 100, а іноді і 110.) Підходи, засновані на повторенні, чітко перемагають це стирання - підхід на основі кодування на дуже малих входах; можливо, варто змінити між декількома алгоритмами залежно від розміру вхідних даних.

Нарешті, це не дійсно приходить в бал, але з дуже високими параметрами випромінювання, використання трохи кожного байта (⅛ вихідного розміру) для розмежування блоків є марним; дешевше було б використовувати роздільники між блоками. Реконструювати блоки з роздільників досить важче, ніж із підходом змінної MSB, але я вважаю, що це можливо, принаймні, якщо дані є досить довгими (з короткими даними, може бути важко вивести параметр випромінювання з виходу) . Це було б на що слід звернути увагу, якщо прагнути до асимптотично ідеального підходу незалежно від параметрів.

(І звичайно, може бути зовсім інший алгоритм, який дає кращі результати, ніж цей!)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.