Ефективне кодування без помилок * [закрито]


20

Місія

Як відомо, генетичний матеріал усіх відомих на Землі істот кодується в ДНК; з використанням чотирьох нуклеотидів аденину, тиміну, цитозину та гуаніну. (Зазвичай представлений ATGC).

Біоінформатик, що бажає зберігати цілий геном, звичайно, не хотів би зберігати це як ASCII, оскільки кожен вибір може бути представлений лише двома бітами!

Специфікація

Якщо ви вирішите прийняти це, ваша місія полягає в тому, щоб написати пару програм, функцій або методів для перетворення представлення ASCII у бінарне представлення і назад; представляючи Aяк b00, Tяк b01, Gяк b10і Cяк b11(відтепер "одиниці").

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

Наприклад: "GATTACCA"стає b11 100001 b11 010011 b10 1100xx.

У ASCII до двійкового введення пробіли, вкладки та нові рядки слід ігнорувати. Будь-який символ, який не міститься в наборі, [ \r\n\tATGC]є помилкою і може бути ігнорований або припинено обробкою.

У вході двійкового до ASCII байти, два великих біта яких b00можуть бути проігноровані.

Вихід ASCII може містити пробіли; але ніколи не повинен бути більше 4-кратного розміру двійкового вводу плюс один байт, і повинен закінчуватися новим рядком.

Двійковий вихід може містити довільну кількість b00xxxxxx"керуючих" байтів; але ніколи не повинно бути довше входу ASCII.

Кожна програма перетворення повинна підтримувати введення довільної довжини; і має завершити кодування або декодування приблизно за лінійний час.

Закрутка

На жаль для біоінформатиста, щодо якого ви виконуєте це завдання, він якимось чином образив вас на особистому, але, можливо, дріб'язковому рівні.

Можливо, він один раз вийшов з вашою сестрою і більше ніколи не подзвонив їй. Можливо, він наступив на хвіст вашої собаки. Специфіка не дуже важлива.

Важливо, що у вас є шанс на окупність!

Деталі

Кожна конверсія повинна вводити невеликий показник помилок; на порядок однієї помилки на десять тисяч до мільйона опрацьованих одиниць.

Помилка може бути однією з таких:

  • Помилки дублювання: "GAT TAC CA"стає"GAT TAA CCA"
  • Помилки видалення: "GAT TAC CA"стає"GAT TAC A"
  • Помилки перекладу: "GAT TAC CA"стає"GTA TAC CA"
  • Подвійне дублювання: "GAT TAC CA"стає"GAT TAC TAC CA"
  • Потрійні ковзання: "GAT TAC CA"стає"TAC GAT CA"
  • Потрійні звороти: "GAT TAC CA"стає"GAT CAT CA"

Ці помилки будуть введені, звичайно, не повинні бути очевидними з коду; і незалежно від довжини вводу; конверсія повинна ввести принаймні одну помилку.

Два прогони з однаковими входами не повинні обов'язково давати однакові виходи.

Хитрість

Голосний біоінформатик - помірно компетентний кодер; і як такий, деякі конструкції будуть автоматично виявлені, і як такі заборонені:

  • Він автоматично виявить будь-які виклики до системних генераторів випадкових чисел, таких як rand (), random (), або зчитування з / dev / urandom або / dev / random (або будь-якого мовного еквівалента).
  • Він також помітить будь-які зайві змінні, лічильники чи петлі.

Оцінка балів

Кодер і декодер оцінюються індивідуально.

Кожен буде запускатися 3 рази проти набору 100 випадково генерованих вхідних файлів, розмір кожного файлу розміром близько 3 мільйонів одиниць.

Дані для тестових випадків кодера будуть створені приблизно так:

for (l = 1 => bigNum)
  for (t = 1 => 20)
    random_pick(3,ATGC)
    t == 20 ? newline : space

Дані для тестових випадків декодера будуть створені приблизно так:

for (u = 1 => bigNum)
  for (t = 1 => 20)
    random_byte() | 0b11000000
   0x00

Кодер

  • Кожен байт, відсутній від очікуваної мінімальної довжини фактичної довжини, набере -1 бал, максимум до -1000. (Очікувана мінімальна довжина ceil(count(ATGC) / 3).)

Дешифратор

  • Кожен байт за очікувану максимальну довжину фактичної довжини набере -1 бал, максимум до -1000. (Очікувана максимальна довжина size(input) * 4 + 1.)

І те й інше

  • Кожен вид помилки, який може бути допущений, набере 100 балів; за 600 можливих балів за кожну, 1200 всього.
  • Кожен тестовий випадок, для якого кодер видає більше 30% більше або менше помилок, ніж його власне середнє значення, буде накладено штрафом на -5 балів.
  • Кожен тестовий випадок, для якого кодер видає менше 15% більше або менше помилок, ніж його власне середнє значення, отримуватиме 5 балів.
  • Кожен тестовий випадок, коли всі три запуски дають однакові результати, буде накладено штрафом на -10 балів.

Жорсткі вимоги

Запис буде дискваліфіковано, якщо:

  • Для будь-якого дійсного введення, тривалішого за один триплет, він не може створити навіть одну помилку.
  • Його продуктивність така, що він не може завершити випробувальну рукоятку протягом приблизно однієї години.
  • Він в середньому створює більше однієї помилки кожні десять тисяч одиниць.
  • Він в середньому створює менше однієї помилки на кожні мільйон одиниць.

Інтерфейс

Вступники повинні прийняти вхід на стандартному вході та вивести на стандартний вихід.

Якщо запис - одна програма з подвійною функцією; комутатори -eі -dповинні встановити програму для кодування та декодування відповідно.

Приклад викликів:

$ encoder <infile.txt >outfile.bin
$ decoder <infile.bin >outfile.txt
$ recoder -e <infile.txt >outfile.bin

Переможець

Переможець - запис із найвищим балом; теоретичний максимум - 1200 для видів помилок плюс 3000 балів для стабільності швидкості генерації помилок.

У навряд чи випадку розіграшу; переможець визначатиметься підрахунком голосів.

Додаткові примітки

Для цілей запуску тестової рукоятки кожен запис повинен містити інструкції із запуску чи компіляції.

Усі записи бажано виконувати на машині Linux без X.


4
Змінено тег. KotH - це завдання, коли заявки взаємодіють між собою. Крім того, я боюся, що буде важко неможливо об'єктивно застосувати "непосильний" компонент.
Мартін Ендер

2
Я погоджуюся з коментарем @ m.buettner про те, що сувору частину важко судити. З іншого боку, я відчуваю, що це єдина цікава частина у виклику. Я можу гарантувати, що генерування та швидкість помилок знаходяться саме в специфікаціях і тому мають максимальні бали. Або я пропускаю щось із специфікації? Більше того, якщо буде допущено додатковий вид помилки, вона буде додана до вищевказаного списку; і всі відповіді будуть набрані на ньому. звучить так, що ви збираєтеся змінити виклик після того, як люди почали працювати або подавати рішення, які, на мою думку, не є гарною ідеєю.
Говард

@Howard: Помічено. Правила оновлюються за допомогою конкретних критеріїв недоторканості; і мутаційний аспект wrt. помилки видаляються
Вілліхам Тотланд

1
Я збираюся дати свою відповідь .. але я думаю, що два речення "Кожна конверсія повинна вводити невеликий показник помилок; в порядку однієї помилки на десять тисяч до мільйона опрацьованих одиниць". і "Запис буде дискваліфіковано, якщо: він в середньому створює більше однієї помилки на кожні десять тисяч одиниць." несумісні. Те саме між "Кожна конверсія повинна вводити невеликий коефіцієнт помилок; в порядку однієї помилки на десять тисяч до мільйона оброблюваних одиниць". та "Запис буде дискваліфіковано, якщо: Він в середньому створює менше однієї помилки на кожні мільйон одиниць."
Маттстель

1
Я голосую за те, щоб закрити це питання як позатематичне, тому що недостатньо важкі виклики більше не є темою на цьому сайті. meta.codegolf.stackexchange.com/a/8326/20469
кіт

Відповіді:


3

Perl 5.10

Я радий представити своє рішення в Perl.

Коли я розпочав виклик, я був повністю впевнений, що Perl залишиться набагато нижче межі 1 години.

Для тестування я розробив генератор простого зразка та генератор закодованого зразка.

Тоді я розробив кодер, який взяв більше зусиль і створив довший код. Кодер працює наступним чином:

  1. Перший цикл зчитує весь файл і розбиває дані, щоб мати масив усіх триплетів
  2. Другий цикл проходить масив і додає кожному елементу його довжину
  3. третій цикл проходить знову і відображає кожен символ, щоб дати вихід.

Кодований двійковий вихід настільки відформатований як "рядки", що закінчуються новим рядком, 20 октетів, де кожен октет кодує один триплет з двома символами префікса (як циклічний рядок-число).

наприклад, найкоротший трибайтовий вхід:

AAA

має дати найкоротший вихід у три байти плюс новий рядок.

00ÿ

тобто

30 30 FF 0A

і

AGG CGC AAC GGC TAA ATC GTT TTC ACA CCA CGT TTG AAA CGG GTG ACA CGA GAT TTA GTC
TAT GGT ACT AGG TAC GCC GTG GTG CGT GCG GAG TTA CTA GAT GTG TTA GTA CGC CAT CGT

слід надати наступне двійкове.

01ÊûÃëÐÇå×ÌüùÖÀúæÌøáÔç
00ÑéÍÊÓïææùîâÔôáæÔäûñù

Якщо через невеликий показник помилок: для найменшого введення сценарій вводить 1 помилку.

Для запуску файлу в 3 мільйони триплетів кодер вводить 11 помилок.

За умови, що сценарій dnacodec3.pl, запуск викликається в командному рядку, як зазвичай:

$> perl dnacodec3.pl -e < plain.txt > coded.txt

Декодер працює наступним чином:

  1. Перший цикл зчитує весь файл і розбиває дані, щоб мати масив усіх октетів. Він відстежує кожен новий рядок.
  2. Другий цикл вивчає кожен октет, зберігаючи ті, що не починаються з 00, і нехтує рештою. Звичайний висновок Ascii форматується як завершені новою лінією триплети, розділені одним пробілом. Новий рядок знаходиться в тому ж самому положенні, що був у вводі.

Я підготував тестовий файл зразка 3 мільйони трійок (близько 12 МБайт) і перевірив на час. Використовуючи мій ноутбук із процесором Intel Core i5 vPro на частоті 2,6 ГГц, запуск 3M-кодера завжди займає менше 20 секунд. Під час пробігу потрібно 200-220 Мбайт оперативної пам’яті. Яка трата!

Виконання декодування займає менше 10 секунд. Він не може вводити помилки ... поки що.

Знову для запуску розшифровки

$> perl dnacodec3.pl -d < coded.txt > plain.txt

Ось код

#!/usr/bin/perl
use strict ;
use warnings ;
my $switch = shift || die "usage $0 [-e|-d]\n";
my %map = qw( x 10  X 11  c 0b  ? 00
              A 00  T 01  G 10  C 11  
              0 00  1 01  2 10  3 11  
              00 A  01 T  10 G  11 C  ) ;
my $r = 20 ;
my @dummy = unpack ( '(A4)*', '0xxx' x $r ) ;
my $map = oct( $map{ c } . ($map{ C } x 9) ) ;
my $t = time() ;
my @inp = () ;
my @out = () ;
my @buf = () ;
my $n ;

sub arch {
    push @buf, @dummy[ 0 .. $r - $#buf - 2 ] ;
    push @out, "@buf" ;
    @buf = () ;
}

sub encode {
    my $mask = '(A3)*' ;
    while ( my $row = <STDIN> ) {
        chomp $row ;
        $row =~ s/\s+//g ;
        $row =~ s/[^\r\n\tATGC]//g ;
        next unless $row ;
        my @row = unpack( $mask, $row ) ;
        push @inp, @row if $row ;
    }
    $n = scalar @inp ;
    $r = $n if $r > $n ;
    for ( my $i = $n - 1 ; $i >= 0 ; --$i ) {
        my $e = $inp[$n-$i-1] ;
        my $l = length( $e ) ;
        my $d = $e =~ /\?/? 0: $l ;
        push @buf, ( $d -((($i-($n>>1))&$map)?0:1) )
           . $e . 'x'x(3-$l) ;
        arch unless $i % $r ;
    }
    arch if scalar @buf ;
    my $m = scalar @out ;
    for ( my $j = $m - 1 ; $j >= 0; --$j ) {
        my @ary = () ;
        my $e = $out[$m-$j-1] ;
        for my $byte ( split / /, $e ) {
            my @byte = split ( //, $byte ) ;
            my @trad = map { $map{ $_ } } @byte ;
            my $byte = join( '', @trad ) ;
            push @ary, $byte ;
        };
        my $row = sprintf( '%02d', $j % $r) ;
        $row .= pack( '(B8)*', @ary ) ;
        print "$row\n" ;
    }
}

sub decode {
    my $mask = '(B8)*' ;
    while ( my $row = <STDIN> ) {
        chomp $row ;
        next unless $row ;
        my @row = unpack( $mask, $row ) ;
        push @inp, @row[0..$#row], '?' if $row ;
    }
    $n = scalar @inp ;
    my @ary = () ;
    for ( my $i = $n - 1 ; $i >= 0 ; --$i ) {
        my $e = $inp[$n-$i-1] ;
        if ( $e ne '?' ) {
            my $u = oct( '0b'. substr($e,0,2) ) ;
            my $a = '' ;
            for my $j ( 1 .. $u ) {
                $a .= $map{ substr($e,$j+$j,2) } ;
            }
            push @ary, $a if $u ;
        }
        else {
            my $row = "@ary" ;
            $row =~ s/\s{2,}//g ;
            print "$row\n" if $row ;
            @ary =() ;
        }
    }
}

decode if $switch eq '-d' ;
encode if $switch eq '-e' ;

А ось генератор зразків:

sub test_coder {
    my $n = shift || 1000 ;
    my @b = qw( A C G T ) ;
    for (my $l = 0; $l < $n; $l++) {
        my @ary = () ;
        for (my $t = 0; $t < 20; $t++) {
            push @ary, $b[ int(rand(4)) ] . $b[ int(rand(4)) ] . $b[ int(rand(4)) ] ;
        }
        print "@ary\n" ;
    }
    1;
}

sub test_decoder {
    my $n = shift || 1000;
    for (my $l = 0; $l < $n; $l++) {
        my @ary = () ;
        for (my $t = 0; $t < 20; $t++) {
            push @ary, int(rand(256)) | 0b11000000 ;
        }
        my $row = pack( 'C*', @ary ) ;
        print "$row\000" ;
    }
    1;
}


test_coder( @ARGV ) if $switch eq '-g' ;
test_decoder( @ARGV )  if $switch eq '-h' ;

Я забув показати, куди вводиться помилка: push @buf у другому циклі робить трюк.
Маттстель

Це тонко, я тобі це дам. Я не буду проводити повномасштабні тести, поки не буде більше одного конкурента, але це хороші речі. :)
Вілліхам Тотланд

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