Эффективное безошибочное * кодирование [закрыто]


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 о том, что закулисную часть сложно судить. С другой стороны, я чувствую, что это единственная интересная часть проблемы. Я могу гарантировать, что генерация и частота ошибок точно соответствуют спецификациям и поэтому имеют максимальные баллы. Или я что-то упускаю из спецификации? Более того, если допущен дополнительный вид ошибки, он будет добавлен в приведенный выше список; и все ответы будут забиты на это. Похоже, вы собираетесь изменить задачу после того, как люди начали работать или предлагать решения, которые я считаю не очень хорошей идеей.
Говард

@ Ховард: отметил. Правила обновляются с конкретными критериями скрытности; и мутационный аспект по отношению к. ошибки удалены.
Виллихам Тотланд

1
Я собираюсь дать свой ответ ... но я думаю, что два предложения "Каждое преобразование должно вводить небольшую частоту ошибок; порядка от одной ошибки на десять тысяч до миллиона обработанных единиц". и «Запись будет дисквалифицирована, если: она в среднем выдает более одной ошибки на каждые десять тысяч единиц». несовместимы То же самое между «Каждое преобразование должно вводить небольшую частоту ошибок; порядка одной ошибки на десять тысяч до миллиона обработанных единиц». и «Запись будет дисквалифицирована, если: она в среднем выдает менее одной ошибки на каждый миллион единиц».
Mattsteel

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 во втором цикле делает свое дело.
Mattsteel

Это тонко, я дам тебе это. Я не буду проводить полномасштабные тесты, пока не будет более одного конкурента, но это хорошая вещь. :)
Виллихам Тотланд

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