Codifica efficiente senza errori * [chiusa]


20

La missione

Come è noto, il materiale genetico di tutte le creature conosciute sulla Terra è codificato nel DNA; usando i quattro nucleotidi adenina, timina, citosina e guanina. (Comunemente rappresentato da ATGC).

Un bioinformatico che desidera archiviare un intero genoma non vorrebbe ovviamente memorizzarlo come ASCII, perché ogni scelta può essere rappresentata da due semplici bit!

La specifica

La tua missione, se scegli di accettarla, è quella di scrivere una coppia di programmi, funzioni o metodi per convertire una rappresentazione ASCII in una rappresentazione binaria e viceversa; che rappresenta Acome b00, Tcome b01, Gcome b10e Ccome b11(d'ora in poi "unità").

Inoltre, i bit alti di ciascun byte dovrebbero contenere il conteggio delle unità nel byte, facendo in modo che ciascun byte rappresenti una tripletta.

Ad esempio: "GATTACCA"diventa b11 100001 b11 010011 b10 1100xx.

Nell'input da ASCII a binario, gli spazi, le schede e le nuove righe devono essere ignorati. Qualsiasi carattere che non si trova nel set di [ \r\n\tATGC]è un errore e può essere ignorato o terminare l'elaborazione.

Nell'input da binario a ASCII, i byte i cui due bit alti sono b00possono essere ignorati.

L'uscita ASCII può contenere spazi bianchi; ma non dovrebbe mai essere più di 4 volte la dimensione dell'ingresso binario più un byte lungo e deve terminare con una nuova riga.

L'output binario può contenere un numero arbitrario di b00xxxxxxbyte di "controllo"; ma non deve mai essere più lungo dell'ingresso ASCII.

Ogni programma di conversione deve supportare input di lunghezza arbitraria; e dovrebbe completare la codifica o decodifica in un tempo approssimativamente lineare.

La svolta

Sfortunatamente per il bioinformatico per il quale stai svolgendo questo compito, ti ha in qualche modo fatto del male a un livello personale ma forse meschino.

Forse è uscito con tua sorella una volta e non l'ha mai più chiamata. Forse ha calpestato la coda del tuo cane. I dettagli non sono molto importanti.

Ciò che è importante è che hai una possibilità di rimborso!

I dettagli

Ogni conversione dovrebbe introdurre un piccolo tasso di errore; sull'ordine di un errore per diecimila a un milione di unità elaborate.

Un errore può essere uno dei seguenti:

  • Errori di duplicazione: "GAT TAC CA"diventa"GAT TAA CCA"
  • Errori di cancellazione: "GAT TAC CA"diventa"GAT TAC A"
  • Errori di traduzione: "GAT TAC CA"diventa"GTA TAC CA"
  • Duplicazioni di triplette: "GAT TAC CA"diventa"GAT TAC TAC CA"
  • La tripletta scivola: "GAT TAC CA"diventa"TAC GAT CA"
  • Inversioni della tripletta: "GAT TAC CA"diventa"GAT CAT CA"

Che errori verranno introdotti non dovrebbe ovviamente essere immediatamente evidente dal codice; e indipendentemente dalla lunghezza dell'input; la conversione dovrebbe introdurre almeno un errore.

Due corse con ingressi identici non devono necessariamente produrre uscite identiche.

Il trucco

Il vile bioinformatico è un programmatore moderatamente competente; e come tale, alcuni costrutti verranno automaticamente scoperti e come tali vietati:

  • Scoprirà automaticamente tutte le chiamate ai generatori di numeri casuali di sistema, come rand (), random () o lettura da / dev / urandom o / dev / random (o qualunque sia l'equivalente della lingua).
  • Noterà anche eventuali variabili, contatori o loop superflui.

Il punteggio

L'encoder e il decoder verranno assegnati individualmente.

Ciascuno verrà eseguito 3 volte contro un set di 100 file di input generati casualmente, ogni file con una dimensione dell'ordine di 3 milioni di unità.

I dati per i casi di test dell'encoder verranno creati approssimativamente in questo modo:

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

I dati per i casi di test del decodificatore verranno creati approssimativamente in questo modo:

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

L'encoder

  • Ogni byte mancante dalla lunghezza minima prevista nella lunghezza effettiva segnerà -1 punti, fino a un massimo di -1000. (La lunghezza minima prevista è ceil(count(ATGC) / 3).)

Il decodificatore

  • Ogni byte oltre la lunghezza massima prevista nella lunghezza effettiva segnerà -1 punti, fino a un massimo di -1000. (La lunghezza massima prevista è size(input) * 4 + 1.)

Tutti e due

  • Ogni tipo di errore che può essere prodotto segnerà 100 punti; per un totale di 600 punti possibili per ciascuno, 1200 totali.
  • Ogni caso di test per il quale l'encoder produce più del 30% in più o in meno di errori rispetto alla propria media sarà penalizzato di -5 punti.
  • Ad ogni caso di test per il quale l'encoder produce meno del 15% in più o in meno di errori rispetto alla propria media verranno assegnati 5 punti.
  • Ogni caso di test in cui tutte e tre le esecuzioni producono output identici sarà penalizzato di -10 punti.

Requisiti rigidi

Un'iscrizione sarà squalificata se:

  • Per qualsiasi input valido più lungo di una tripletta non riesce a produrre nemmeno un errore.
  • Le sue prestazioni sono tali da non poter completare il guanto di prova entro circa un'ora.
  • In media produce più di un errore ogni diecimila unità.
  • In media produce meno di un errore ogni milione di unità.

L'interfaccia

I partecipanti devono accettare input sull'input standard e output sull'output standard.

Se la voce è un programma con doppia funzione; gli switch -ee -ddovrebbero impostare il programma per la codifica e la decodifica, rispettivamente.

Invocazioni di esempio:

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

Il vincitore

Il vincitore è l'ingresso con il punteggio più alto; il massimo teorico è 1200 per tipi di errore più 3000 punti per stabilità nel tasso di generazione dell'errore.

Nel caso improbabile di pareggio; il vincitore sarà determinato dal conteggio dei voti.

Le note aggiuntive

Ai fini dell'esecuzione del guanto di prova, ogni voce deve includere istruzioni di esecuzione o compilazione.

Tutte le voci dovrebbero essere preferibilmente eseguibili su una macchina Linux senza X.


4
Modificato il tag. KotH è per le sfide in cui i contributi interagiscono tra loro. Inoltre temo che sarà impossibile rendere oggettivamente impossibile applicare il componente "subdolo".
Martin Ender,

2
Concordo con il commento di @ m.buettner che la parte subdola è difficile da giudicare. D'altro canto, ritengo che questa sia l'unica parte interessante della sfida. Posso garantire che la generazione e il tasso di errore rientrano esattamente nelle specifiche e quindi hanno il punteggio massimo. O mi manca qualcosa dalle specifiche? Inoltre, se viene accettato un ulteriore tipo di errore, verrà aggiunto all'elenco precedente; e tutte le risposte verranno segnate su di esso. sembra che cambierai la sfida dopo che le persone hanno iniziato a lavorare o a presentare soluzioni che penso non sia una buona idea.
Howard,

@Howard: notato. Le regole sono aggiornate con specifici criteri di subdolezza; e l'aspetto mutazionale scritto. gli errori vengono rimossi.
Williham Totland,

1
Ho intenzione di dare la mia risposta .. ma penso che le due frasi "Ogni conversione dovrebbe introdurre un piccolo tasso di errore; nell'ordine di un errore ogni diecimila a un milione di unità elaborate". e "Una voce sarà squalificata se: in media produce più di un errore ogni diecimila unità". sono incompatibili. Lo stesso tra "Ogni conversione dovrebbe introdurre un piccolo tasso di errore; nell'ordine di un errore ogni diecimila a un milione di unità elaborate". e "Una voce sarà squalificata se: in media produce meno di un errore ogni milione di unità".
Mattsteel,

1
Sto votando per chiudere questa domanda come fuori tema perché le sfide subdole non sono più in argomento su questo sito. meta.codegolf.stackexchange.com/a/8326/20469
cat

Risposte:


3

Perl 5.10

Sono lieto di presentare la mia soluzione in Perl.

Quando ho iniziato la sfida ero abbastanza sicuro che Perl sarebbe rimasto ben al di sotto del limite di 1 ora.

A scopo di test ho sviluppato un semplice generatore di campioni e un generatore di codice codificato.

Quindi ho sviluppato l'encoder che ha richiesto uno sforzo maggiore e ha prodotto un codice più lungo. L'encoder funziona come segue:

  1. Il primo ciclo legge l'intero file e divide i dati per avere una matrice di tutte le terzine
  2. il secondo loop attraversa l'array e antepone a ciascun elemento la sua lunghezza
  3. il terzo ciclo attraversa di nuovo e mappa ogni personaggio per fornire l'output.

L'output binario codificato è così formattato come "righe" terminate di nuova riga di 20 ottetti in cui ogni ottetto codifica una tripletta, con due caratteri di prefisso (come un numero di riga ciclico).

ad esempio, l' ingresso di tre byte più corto :

AAA

dovrebbe fornire l'output più breve di tre byte più new-line.

00ÿ

questo è

30 30 FF 0A

e

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

dovrebbe dare il seguente binario.

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

Dovrebbe a causa del piccolo tasso di errore: per l'input più piccolo, lo script introduce 1 errore.

Per l'esecuzione di un file di 3 milioni di terzine, il codificatore introduce 11 errori.

A condizione che lo script sia dnacodec3.pl, l'esecuzione viene richiamata al prompt dei comandi come al solito:

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

Il decodificatore funziona come segue:

  1. Il primo ciclo legge l'intero file e divide i dati per avere una matrice di tutti gli ottetti. Tiene traccia di ogni nuova linea.
  2. il secondo ciclo esamina ciascun ottetto, mantenendo quelli che non iniziano con 00 e ignora il resto. L'output Ascii semplice è formattato come linee terminate di nuova linea di terzine separate da uno spazio. Le nuove righe si trovano nella stessa posizione in cui si trovavano nell'input.

Ho preparato un file di prova di esempio di 3 milioni di terzine (circa 12 MB) e ho testato il tempo. Utilizzando il mio laptop con una Intel Core i5 vPro a 2,6 GHz, il funzionamento dell'encoder 3M richiede sempre meno di 20 secondi. Durante la corsa sono necessari 200-220 MByte di RAM. Che spreco!

L'esecuzione della decodifica richiede meno di 10 secondi. Non può introdurre errori ... per ora.

Ancora una volta, per l'esecuzione della decodifica

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

Ecco il codice

#!/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' ;

Ed ecco il generatore di esempio:

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' ;

Ho dimenticato di mostrare dove viene iniettato l'errore: il push @buf nel secondo loop fa il trucco.
Mattsteel,

È sottile, te lo darò. Non eseguirò test su larga scala fino a quando non ci sarà più di un concorrente, ma questa è roba buona. :)
Williham Totland,

Grazie. So che questo è un suggerimento per altri amici ... Vorrei migliorare la casualità della posizione dell'errore usando la funzione del tempo (ancora inutilizzata): l'avvio eseguito a giorni pari o dispari dovrebbe produrre risultati diversi
Mattsteel
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.