Effiziente fehlerfreie * Codierung [geschlossen]

20

Die Mission

Bekanntlich ist das genetische Material aller bekannten Lebewesen auf der Erde in DNA kodiert; unter Verwendung der vier Nukleotide Adenin, Thymin, Cytosin und Guanin. (Gemeinsam vertreten durch ATGC).

Ein Bioinformatiker, der ein ganzes Genom speichern möchte, möchte dies natürlich nicht als ASCII speichern, da jede Auswahl durch nur zwei Bits dargestellt werden kann!

Die Spezifikation

Ihre Aufgabe ist es, ein Paar von Programmen, Funktionen oder Methoden zu schreiben, um eine ASCII-Darstellung in eine binäre Darstellung und zurück zu konvertieren. Darstellen Aals b00, Tals b01, Gals b10und Cals b11(fortan "Einheiten").

Außerdem sollten die High-Bits jedes Bytes die Anzahl der Einheiten im Byte enthalten, sodass jedes Byte ein Triplett darstellt.

Zum Beispiel: "GATTACCA"wird b11 100001 b11 010011 b10 1100xx.

Leerzeichen, Tabulatoren und Zeilenumbrüche sollten in der ASCII-zu-Binäreingabe ignoriert werden. Jedes Zeichen, das nicht im Satz von enthalten [ \r\n\tATGC]ist, ist ein Fehler und kann entweder ignoriert oder die Verarbeitung abgebrochen werden.

In der Binär-zu-ASCII-Eingabe können Bytes, deren zwei hohe Bits sind b00, ignoriert werden.

Die ASCII-Ausgabe kann Leerzeichen enthalten. sollte aber niemals mehr als das 4-fache der Größe des Binäreingangs plus ein Byte lang sein und muss mit einem Zeilenumbruch enden.

Der Binärausgang kann eine beliebige Anzahl von b00xxxxxx"Steuerbytes" enthalten; darf aber nie länger sein als der ASCII-Eingang.

Jedes Konvertierungsprogramm muss Eingaben beliebiger Länge unterstützen. und sollte die Codierung oder Decodierung in ungefähr linearer Zeit abschließen.

Die Wendung

Unglücklicherweise für den Bioinformatiker, für den Sie diese Aufgabe ausführen, hat er Ihnen in gewisser Weise Unrecht getan, auf einer persönlichen, aber vielleicht kleinen Ebene.

Vielleicht ist er einmal mit deiner Schwester ausgegangen und hat sie nie wieder angerufen. Vielleicht ist er auf den Schwanz Ihres Hundes getreten. Die Einzelheiten sind nicht wirklich wichtig.

Wichtig ist, dass Sie eine Chance auf Rückzahlung haben!

Die Details

Bei jeder Konvertierung sollte eine kleine Fehlerrate auftreten. in der Größenordnung von einem Fehler pro zehntausend bis eine Million Einheiten verarbeitet.

Ein Fehler kann einer der folgenden sein:

  • Kopierfehler: "GAT TAC CA"wird"GAT TAA CCA"
  • Löschfehler: "GAT TAC CA"wird"GAT TAC A"
  • Übersetzungsfehler: "GAT TAC CA"wird"GTA TAC CA"
  • Triplettverdopplungen: "GAT TAC CA"werden"GAT TAC TAC CA"
  • Drillingsschlupf: "GAT TAC CA"wird"TAC GAT CA"
  • Triplettumkehrungen: "GAT TAC CA"werden"GAT CAT CA"

Dass Fehler eingeführt werden, sollte natürlich nicht sofort aus dem Code ersichtlich sein; und unabhängig von der Länge der Eingabe; Die Konvertierung sollte mindestens einen Fehler verursachen.

Zwei Läufe mit identischen Eingaben sollten nicht unbedingt identische Ausgaben erzeugen.

Der Trick

Der abscheuliche Bioinformatiker ist ein mäßig kompetenter Kodierer; Daher werden einige Konstrukte automatisch erkannt und sind als solche verboten:

  • Er erkennt automatisch alle Aufrufe von System-Zufallszahlengeneratoren wie rand (), random () oder das Lesen aus / dev / urandom oder / dev / random (oder was auch immer das Sprachäquivalent ist).
  • Er wird auch alle überflüssigen Variablen, Zähler oder Schleifen bemerken.

Die Wertung

Der Kodierer und der Dekodierer werden einzeln bewertet.

Jede wird dreimal mit einem Satz von 100 zufällig generierten Eingabedateien ausgeführt, wobei jede Datei eine Größe in der Größenordnung von 3 Millionen Einheiten hat.

Die Daten für die Encoder-Testfälle werden ungefähr so ​​erstellt:

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

Die Daten für die Decoder-Testfälle werden ungefähr so ​​erstellt:

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

Der Encoder

  • Jedes Byte, das in der erwarteten Mindestlänge in der tatsächlichen Länge fehlt, wird mit -1 bis maximal -1000 Punkten bewertet. (Die erwartete Mindestlänge beträgt ceil(count(ATGC) / 3).)

Der Decoder

  • Jedes Byte über der erwarteten maximalen Länge in der tatsächlichen Länge wird mit -1 bis maximal -1000 Punkten bewertet. (Die erwartete maximale Länge beträgt size(input) * 4 + 1.)

Beide

  • Für jede Art von Fehler, die erzeugt werden kann, werden 100 Punkte vergeben. Für jeweils 600 Punkte sind 1200 Punkte möglich.
  • Jeder Testfall, bei dem der Encoder mehr als 30% mehr oder weniger Fehler als der eigene Durchschnitt erzeugt, wird mit -5 Punkten bestraft.
  • Für jeden Testfall, bei dem der Encoder weniger als 15% mehr oder weniger Fehler als der eigene Durchschnitt erzeugt, werden 5 Punkte vergeben.
  • Jeder Testfall, in dem alle drei Läufe zu identischen Ergebnissen führen, wird mit -10 Punkten bestraft.

Harte Anforderungen

Ein Eintrag wird disqualifiziert, wenn:

  • Für jede gültige Eingabe, die länger als ein Triplett ist, wird kein einziger Fehler erzeugt.
  • Die Leistung ist so, dass der Testhandschuh nicht innerhalb von ungefähr einer Stunde beendet werden kann.
  • Es erzeugt durchschnittlich mehr als einen Fehler pro zehntausend Einheiten.
  • Es erzeugt im Durchschnitt weniger als einen Fehler pro Million Einheiten.

Die Schnittstelle

Die Teilnehmer sollten Eingaben in die Standardeingabe und Ausgaben in die Standardausgabe akzeptieren.

Wenn der Eintrag ein Programm mit Doppelfunktion ist; Die Schalter -eund -dsollten das Programm für die Codierung bzw. Decodierung einstellen.

Beispielaufrufe:

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

Der Gewinner

Der Gewinner ist der Beitrag mit der höchsten Punktzahl. Das theoretische Maximum ist 1200 für Fehlerarten plus 3000 Punkte für die Stabilität der Fehlererzeugungsrate.

Im unwahrscheinlichen Fall eines Unentschieden; Der Gewinner wird anhand der Stimmenzahl ermittelt.

Die zusätzlichen Hinweise

Zum Ausführen des Testhandschuhs sollte jeder Eintrag Anweisungen zum Ausführen oder Kompilieren enthalten.

Alle Einträge sollten vorzugsweise auf einem Linux-Computer ohne X ausführbar sein.

Williham Totland
quelle
4
Tag geändert KotH ist für Herausforderungen gedacht, bei denen Einreichungen miteinander interagieren. Ich befürchte auch, dass es schwierig bis unmöglich sein wird, die "hinterhältige" Komponente objektiv durchzusetzen.
Martin Ender
2
Ich stimme dem Kommentar von @ m.buettner zu, dass der hinterhältige Teil schwer zu beurteilen ist. Auf der anderen Seite denke ich, dass dies der einzig interessante Teil der Herausforderung ist. Ich kann garantieren, dass die Fehlererzeugung und -rate genau innerhalb der Spezifikationen liegen und daher die maximalen Punkte haben. Oder vermisse ich etwas aus der Spezifikation? Darüber hinaus Wenn eine zusätzliche Art von Fehlern angenommen wird, wird es an die oben aufgeführte Liste hinzugefügt werden; und alle Antworten werden darauf gewertet. Das hört sich so an, als würden Sie die Herausforderung ändern, nachdem Leute angefangen haben zu arbeiten oder Lösungen einzureichen, die ich für keine gute Idee halte.
Howard
@ Howard: zur Kenntnis genommen. Die Regeln werden mit spezifischen Unterhandlungskriterien aktualisiert. und der Mutationsaspekt wrt. Fehler werden beseitigt.
Williham Totland
1
Ich werde meine Antwort geben ... aber ich denke, die beiden Sätze "Jede Konvertierung sollte eine kleine Fehlerrate einführen; in der Größenordnung von einem Fehler pro zehntausend bis zu einer Million verarbeiteter Einheiten." und "Ein Eintrag wird disqualifiziert, wenn: Er durchschnittlich mehr als einen Fehler pro zehntausend Einheiten erzeugt." sind inkompatibel. Das Gleiche gilt für "Jede Konvertierung sollte eine kleine Fehlerrate verursachen, in der Größenordnung von einem Fehler pro zehntausend bis zu einer Million verarbeiteter Einheiten." und "Ein Eintrag wird disqualifiziert, wenn: Er durchschnittlich weniger als einen Fehler pro Million Einheiten erzeugt."
Mattsteel
1
Ich stimme dafür, diese Frage als "Off-Topic" zu schließen, da hinterhältige Herausforderungen auf dieser Site nicht mehr zum Thema gehören. meta.codegolf.stackexchange.com/a/8326/20469
cat

Antworten:

3

Perl 5.10

Ich bin froh, meine Lösung in Perl präsentieren zu können.

Als ich die Herausforderung startete, war ich mir ziemlich sicher, dass Perl deutlich unter dem Limit von 1 Stunde bleiben würde.

Zu Testzwecken habe ich einen einfachen Probengenerator und einen codierten Probengenerator entwickelt.

Dann entwickelte ich den Encoder, der einen größeren Aufwand und einen längeren Code produzierte. Der Encoder funktioniert wie folgt:

  1. Die erste Schleife liest die gesamte Datei und teilt die Daten auf, um ein Array aller Triplets zu erhalten
  2. Die zweite Schleife durchläuft das Array und stellt jedem Element seine Länge voran
  3. Die dritte Schleife wird erneut durchlaufen und ordnet jedes Zeichen zu, um die Ausgabe zu erhalten.

Die codierte Binärausgabe ist so formatiert, dass sie mit einem Zeilenumbruch abgeschlossene "Zeilen" mit 20 Oktetten enthält, wobei jedes Oktett ein Triplett mit zwei Präfixzeichen (wie eine zyklische Zeilennummer) codiert.

Zum Beispiel die kürzeste Drei-Byte-Eingabe:

AAA

sollte die kürzeste Ausgabe von drei Bytes plus New-Line ergeben.

00ÿ

das ist

30 30 FF 0A

und

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

sollte die folgende binäre geben.

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

Sollte wegen der geringen Fehlerrate: Für die kleinste Eingabe führt das Skript 1 Fehler ein.

Bei einem Dateilauf von 3 Millionen Triplets führt der Encoder 11 Fehler ein.

Vorausgesetzt, das Skript ist dnacodec3.pl, wird die Ausführung wie gewohnt an der Eingabeaufforderung aufgerufen:

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

Der Decoder arbeitet wie folgt:

  1. Die erste Schleife liest die gesamte Datei und teilt die Daten auf, um ein Array aller Oktette zu erhalten. Es verfolgt jede neue Zeile.
  2. Die zweite Schleife prüft jedes Oktett, behält diejenigen bei, die nicht mit 00 beginnen, und ignoriert den Rest. Die reine ASCII-Ausgabe wird als Zeilen mit Zeilenende und Triplett-Abstand von einem Leerzeichen formatiert. Die neuen Zeilen befinden sich an derselben Position wie in der Eingabe.

Ich habe eine Testdatei mit 3 Millionen Triplets (ca. 12 MB) erstellt und auf Timing getestet. Wenn ich meinen Laptop mit einem Intel Core i5 vPro mit 2,6 GHz benutze, dauert die Ausführung des 3M-Encoders immer weniger als 20 Sekunden. Während des Laufs werden 200-220 MByte RAM benötigt. Was für eine Verschwendung!

Der Dekodierungslauf dauert weniger als 10 Sekunden. Es kann keine Fehler einführen ... vorerst.

Wieder für den Dekodierlauf

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

Hier ist der Code

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

Und hier ist der Sample Generator:

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' ;
Mattstahl
quelle
Ich habe vergessen zu zeigen, wo der Fehler eingeschleust wird: Der push @ buf in der zweiten Schleife macht den Trick.
Mattsteel
Es ist subtil, das gebe ich dir. Ich werde keine groß angelegten Tests durchführen, bis es mehr als einen Konkurrenten gibt, aber das ist gutes Zeug. :)
Williham Totland
Vielen Dank. Ich weiß, dass dies ein Vorschlag für andere Freunde ist ... Ich möchte die Zufälligkeit der Fehlerposition mithilfe der (noch nicht verwendeten)
Zeitfunktion