การเข้ารหัสที่ปราศจากข้อผิดพลาดที่มีประสิทธิภาพ [ปิด]


20

ภารกิจ

ในฐานะที่เป็นที่รู้จักกันดีสารพันธุกรรมของสิ่งมีชีวิตที่รู้จักทั้งหมดบนโลกถูกเข้ารหัสใน DNA ใช้นิวคลีโอไทด์สี่ adenine, thymine, cytosine และ guanine (ATGC ตัวแทนทั่วไป)

นักชีวสารสนเทศศาสตร์ต้องการเก็บจีโนมทั้งหมดแน่นอนว่าไม่ต้องการเก็บไว้เป็น ASCII เพราะแต่ละตัวเลือกสามารถแสดงได้เพียงสองบิต!

สเปค

ภารกิจของคุณคุณควรเลือกที่จะยอมรับมันคือการเขียนโปรแกรมฟังก์ชั่นหรือวิธีการแปลงการแสดง ASCII เป็นการแสดงไบนารีและกลับ แสดงAเป็นb00, Tเป็นb01, Gเป็นb10, และCเป็นb11(ต่อจากนี้ไป "หน่วย")

นอกจากนี้บิตสูงของแต่ละไบต์ควรมีจำนวนหน่วยในไบต์ทำให้แต่ละไบต์แทน triplet

ตัวอย่างเช่น: กลายเป็น"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"
  • การทำสำเนา Triplet: "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 สำหรับความท้าทายที่การส่งข้อความโต้ตอบกัน นอกจากนี้ฉันกลัวว่ามันจะยากที่จะเป็นไปไม่ได้ที่จะบังคับใช้องค์ประกอบ "เล่ห์เหลี่ยม" อย่างเป็นกลาง
Martin Ender

2
ฉันเห็นด้วยกับความคิดเห็นของ @ m.buettner ว่าส่วนที่อยู่ภายใต้การดูแลยากที่จะตัดสิน ในทางกลับกันฉันรู้สึกว่านี่เป็นเพียงส่วนที่น่าสนใจในการท้าทาย ฉันสามารถรับประกันได้ว่าการสร้างข้อผิดพลาดและอัตรานั้นอยู่ในรายละเอียดที่แน่นอนดังนั้นจึงมีคะแนนสูงสุด หรือฉันจะพลาดบางสิ่งบางอย่างจากสเป็ค? นอกจากนี้หากยอมรับข้อผิดพลาดเพิ่มเติมประเภทนั้นจะถูกเพิ่มลงในรายการด้านบน และคำตอบทั้งหมดจะได้รับคะแนนกับมัน ดูเหมือนว่าคุณกำลังจะเปลี่ยนความท้าทายหลังจากที่ผู้คนเริ่มทำงานหรือส่งคำตอบซึ่งฉันคิดว่าไม่ใช่ความคิดที่ดี
Howard

@ วิธี: ตั้งข้อสังเกต กฎมีการปรับปรุงด้วยเกณฑ์ underhandedness เฉพาะ และด้านการกลายพันธุ์ wrt ข้อผิดพลาดจะถูกลบออก
Williham Totland

1
ฉันจะให้คำตอบ .. แต่ฉันคิดว่าประโยคทั้งสอง "การแปลงแต่ละครั้งควรมีอัตราความผิดพลาดเล็กน้อยตามลำดับข้อผิดพลาดหนึ่งข้อต่อหนึ่งหมื่นถึงหนึ่งล้านหน่วยที่ประมวลผล" และ "รายการจะถูกตัดสิทธิ์หาก: โดยเฉลี่ยก่อให้เกิดข้อผิดพลาดมากกว่าหนึ่งรายการในทุก ๆ หมื่นหน่วย" ไม่เข้ากัน เหมือนกันระหว่าง "การแปลงแต่ละครั้งควรแนะนำอัตราข้อผิดพลาดเล็กน้อยโดยเรียงตามข้อผิดพลาดหนึ่งข้อต่อหนึ่งหมื่นต่อการประมวลผลหนึ่งล้านหน่วย" และ "รายการจะถูกตัดสิทธิ์หาก: โดยเฉลี่ยก่อให้เกิดข้อผิดพลาดน้อยกว่าหนึ่งข้อในทุก ๆ ล้านหน่วย"
Mattsteel

1
ฉันลงคะแนนให้ปิดคำถามนี้เป็นหัวข้อนอกเพราะความท้าทายที่ไม่ได้อยู่ในหัวข้อบนเว็บไซต์นี้อีกต่อไป meta.codegolf.stackexchange.com/a/8326/20469
cat

คำตอบ:


3

Perl 5.10

ฉันดีใจที่ได้นำเสนอทางออกของฉันใน Perl

เมื่อฉันเริ่มต้นความท้าทายฉันค่อนข้างแน่ใจว่า Perl จะอยู่ในระดับที่ต่ำกว่า 1 ชั่วโมง

เพื่อวัตถุประสงค์ในการทดสอบฉันได้พัฒนาเครื่องกำเนิดตัวอย่างแบบเรียบง่ายและเครื่องกำเนิดตัวอย่างที่มีรหัส

จากนั้นฉันก็พัฒนาเครื่องเข้ารหัสที่ใช้ความพยายามมากขึ้นและสร้างรหัสที่ยาวขึ้น เครื่องเข้ารหัสทำงานดังนี้:

  1. แรกวนอ่านไฟล์ทั้งหมดและแยกข้อมูลที่จะมีอาร์เรย์ของทริปเปิลทั้งหมด
  2. วงที่สองลัดเลาะอาร์เรย์และเสริมแต่ละองค์ประกอบของความยาว
  3. วงที่สามลัดเลาะอีกครั้งและแมปอักขระแต่ละตัวเพื่อให้เอาต์พุต

เอาต์พุตไบนารีที่เขียนโค้ดจะถูกจัดรูปแบบเป็นบรรทัดใหม่ที่ยกเลิก "บรรทัด" ของ 20 octets โดยที่แต่ละรหัส octet หนึ่ง triplet โดยมีอักขระสองตัวนำหน้า (เช่นหมายเลขบรรทัดวัฏจักร)

ตัวอย่างเช่นอินพุตสามไบต์ที่สั้นที่สุด :

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 ข้อผิดพลาด

สำหรับการเรียกใช้ไฟล์สามล้านไฟล์ตัวเข้ารหัสแนะนำ 11 ข้อผิดพลาด

โดยมีเงื่อนไขว่าสคริปต์คือ dnacodec3.pl การเรียกใช้จะถูกเรียกใช้ที่พรอมต์คำสั่งตามปกติ:

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

ตัวถอดรหัสทำงานดังนี้:

  1. แรกวนรอบอ่านไฟล์ทั้งหมดและแยกข้อมูลเพื่อมีอาร์เรย์ของ octets ทั้งหมด ติดตามทุกบรรทัดใหม่
  2. วนรอบที่สองตรวจสอบแต่ละ octet โดยคงไว้ซึ่งไม่ขึ้นต้นด้วย 00 และไม่สนใจส่วนที่เหลือ เอาต์พุต Ascii แบบธรรมดาถูกจัดรูปแบบเป็นบรรทัดที่ยกเลิกบรรทัดใหม่ของ triplets คั่นด้วยช่องว่างหนึ่งช่อง ขึ้นบรรทัดใหม่อยู่ในตำแหน่งเดียวกันกับที่พวกเขาอยู่ในอินพุต

ฉันเตรียมไฟล์ทดสอบตัวอย่าง 3 ล้านตัว (ประมาณ 12MByte) และทดสอบตามกำหนดเวลา การใช้แล็ปท็อปของฉันกับ Intel Core i5 vPro ที่ 2.6 GHz ตัวเข้ารหัส 3M จะใช้เวลาน้อยกว่า 20 วินาที ระหว่างการรันจะใช้ RAM 200-220 MByte น่าขยะแขยง!

การถอดรหัสใช้เวลาน้อยกว่า 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' ;

ฉันลืมที่จะแสดงว่าข้อผิดพลาดถูกฉีดที่ใด: การกด @buf ในลูปที่สองทำกลอุบาย
Mattsteel

มันบอบบางฉันจะให้คุณ ฉันจะไม่ทำการทดสอบเต็มรูปแบบจนกว่าจะมีคู่แข่งมากกว่าหนึ่งคน แต่นี่เป็นสิ่งที่ดี :)
Williham Totland

ขอบคุณ ฉันรู้ว่านี่เป็นข้อเสนอแนะสำหรับเพื่อนคนอื่น ๆ ... ฉันต้องการที่จะปรับปรุงการสุ่มตำแหน่งข้อผิดพลาดโดยใช้ func เวลา (ยังไม่ได้ใช้): เริ่มต้นทำงานที่คี่หรือวินาทีที่ควรให้ผลผลิตที่แตกต่างกัน
Mattsteel
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.