เครื่องจำลองดีเอ็นเออย่างง่าย


18

รหัสของคุณกำลังจะสร้างตัวแทน ASCII แบบศิลปะที่เรียบง่ายอย่างมากตลอดไป มันจะใช้ตัวเลขสองตัวเป็นอินพุตในรูปแบบใด ๆ ที่คุณต้องการ: เป็นรายการเป็นอาร์กิวเมนต์สำหรับฟังก์ชันบน stdin เป็นต้น

  • ช่วงเวลาทศนิยมIในหน่วยวินาทีระหว่าง 0.0 ถึง 1.0 (รวม)
  • ระดับการซูมZเป็นจำนวนเต็มตั้งแต่ 1 ถึง 64 (รวม)

รหัสของคุณจะพิมพ์หนึ่งบรรทัดไปยัง stdout หรือเทียบเท่าทุกIวินาทีสร้างเอาต์พุตไม่สิ้นสุดที่มีลักษณะคล้ายนี้ (สำหรับระดับการซูม 4):

    A
 T-----a
G-------c
 G-----c
    g
 t-----A
a-------T
 c-----G
    T
 A-----t
C-------g
...

โดยเฉพาะการเป็นตัวแทนของดีเอ็นเอของเราคือคู่ของคลื่นไซน์เชื่อมต่อกันด้วยยัติภังค์หนึ่งประกอบด้วยตัวอักษรa, c, gและtอื่น ๆ ของตัวละครA, C, และG Tหากxเป็นจำนวน 0 ที่จัดทำดัชนีของบรรทัดที่เรากำลังพิมพ์อยู่ตำแหน่ง 0 ตามของตัวละครในคลื่นตัวพิมพ์เล็กจะได้รับจาก(sin(πx / Z) + 1) * Zและในคลื่นตัวพิมพ์ใหญ่จะได้รับจาก(-sin(πx / Z) + 1) * Zทั้งสองโค้งมน (ไม่ปูพื้น) ที่ใกล้ที่สุด จำนวนเต็ม. รายละเอียดเพิ่มเติม:

  • ในกรณีที่คลื่นทั้งสองทับกันคุณต้องสลับว่าคลื่นใดที่อยู่ข้างหน้าเริ่มต้นด้วยคลื่นตัวพิมพ์ใหญ่ (เริ่มต้นด้วยคลื่นตัวพิมพ์เล็กจะทำให้เราเป็นเกลียวคู่ที่ไม่มีอยู่ !)
  • กรณีที่ไม่สนใจ A จับคู่กับ T และ C เสมอจับคู่กับ G เช่นเดียวกับ DNA จริง คู่ตัวเองควรได้รับการสุ่มเลือกด้วยการกระจายแบบฟอร์มที่เป็นไปได้ทั้งสี่ ไม่สำคัญว่าการเลือกคู่จะเหมือนหรือต่างกันในการรันต่อเนื่องของรหัสของคุณ คุณภาพทางสถิติของตัวเลือกแบบสุ่มของคุณไม่ใช่ปัญหาตราบใดที่เอาต์พุตไม่มีรูปแบบที่ชัดเจนและช่วงเวลาอย่างน้อยพันล้าน (ข้อผิดพลาดPRNGsเช่นRANDUนั้นดี)
  • คุณต้องไม่มีช่องว่างต่อท้ายหรือวางแผ่นทุกบรรทัดไปยังตำแหน่งสูงสุดของคลื่นที่ระดับการซูมนั้น (ในตัวอย่างด้านบนอักขระเก้าตัว) ระดับการซูม 1 อาจมีพื้นที่ต่อท้ายเพิ่มเติมหนึ่งช่องสำหรับเหตุผลทางคณิตศาสตร์

เนื่องจาก DNA มีขนาดเล็กรหัสของคุณจะต้องสั้นที่สุด

ตัวอย่างเพิ่มเติม:

ระดับการซูม 8:

        T
     C-----g
  A-----------t
 C-------------g
G---------------c
 T-------------a
  T-----------a
     T-----a
        c
     g-----C
  t-----------A
 g-------------C
a---------------T
...

ระดับการซูม 2:

  A
T---a
  c
g---C
  G
A---t
  c
a---T
...

ระดับการซูม 1 (สังเกตพื้นที่นำ):

 G
 a
 C
 t
...


9
"เนื่องจาก DNA มีขนาดเล็กรหัสของคุณจะต้องสั้นที่สุด" จริงๆ?
TanMath

3
@ TanMath คุณต้องการเหตุผลในการใช้ Code-Golf หรือไม่? backstories เกือบจะโง่ ๆ แบบนี้ไปเลย
Patrick Roberts

@PatrickRoberts ฉันรู้ แต่ฉันก็แค่ชี้ให้เห็นว่าเหตุผลที่โง่เง่าก็คือนักกอล์ฟหลายคนทำ อย่าเอาจริงเอาจังเกินไป! ;)
TanMath

"เลือกแบบสุ่ม" หมายความว่าอะไร RANDU โอเคไหม สิ่งที่เกี่ยวกับลำดับซ้ำที่สั้นกว่า?
KSFT

คำตอบ:


4

Ruby, Rev B 171 161 ไบต์

การแก้ไขเอาต์พุตสำหรับ z = 1 ราคา 10 ไบต์ มันเป็นกรณีพิเศษ: ส่วนที่เป็นเกลียวมีความกว้าง 3 ตัวจริงถ้าคุณดูที่ 90 องศา แต่เมื่อเราดูที่ 0 องศามันจะมีความกว้างเพียง 1 ตัวเท่านั้น การเว้นวรรคนำหน้าศูนย์บน z = 1 ไม่ต้องการอีกต่อไป

ประหยัดบางอย่างโดยการกำจัดวงเล็บและโดยการคูณ y.abs ด้วย 2 ก่อนที่จะตัดเมื่อคำนวณจำนวน - อักขระที่ต้องการ

สุดท้ายผมหลีกเลี่ยงinclude Math(ที่จำเป็นสำหรับการsinและPI) iโดยการหาค่าตัวเลขที่ซับซ้อนที่มีอำนาจของจำนวน ส่วนจินตภาพของจำนวนเชิงซ้อนเทียบเท่ากับบาป x ยกเว้นว่ามันซ้ำกับระยะเวลา 4 แทนรอบระยะเวลา 2 * PI การบันทึกสำหรับการเปลี่ยนแปลงนี้มีขนาด 1 หรือ 0 ไบต์

->z,i{x=0
loop{y=z*("i".to_c**x).imag
s=(?-*(y.abs*2)).center z*2+1
s[z-y+0.5]='TGAC'[r=rand(4)]
x!=0&&s[z+y+0.5]='actg'[r]
puts s
sleep i
x+=2.0/z
x>3.99&&x=0}}

Ruby, Rev A 165 ไบต์

นี่เป็นวิธีที่เกินความคาดหมาย มีโอกาสเล่นกอล์ฟที่อาจเกิดขึ้นได้ไม่กี่แห่ง

include Math
->z,i{x=0
loop{y=z*sin(x)
s=('--'*(y.abs+h=0.5)).center(z*2+1)
s[z+h-y]='TGAC'[r=rand(4)]
x!=0&&s[z+h+y]='actg'[r]
puts s
sleep(i)
x+=PI/z
x>6.28&&x=0}}

แสดงความคิดเห็นในโปรแกรมทดสอบ

include Math
f=->z,i{x=0
  loop{y=z*sin(x)
    s=('--'*(y.abs+h=0.5)).center(z*2+1)  #make a space-padded string of z*2+1 characters, containing enough - signs
    s[z+h-y]='TGAC'[r=rand(4)]            #insert random capital letter, saving index in r
    x!=0&&s[z+h+y]='actg'[r]              #insert small letter. This will normally go on top of the capital as it is done second, but supress for x=0 to make helix
    puts s
    sleep(i)
    x+=PI/z                               #increment x
    x>6.28&&x=0                           #reset x if equal to 2*PI (this proofs against loss of floating point precision, making correct output truly infinite.)
  }
}

Z=gets.to_i
I=gets.to_f
f[Z,I]

ดูดี! ปัญหาเล็กน้อยที่หนึ่ง: มีพื้นที่ชั้นนำสำหรับระดับการซูม 1. นอกจากนี้ในการทดสอบโปรแกรมของคุณควรจะเป็นI=gets.to_i I=gets.to_f
ลุค

อ๊ะ! คุณพูดถูกว่า Z = 1 เป็นกรณีพิเศษ นั่นไม่ได้ตั้งใจและจริง ๆ แล้วเป็นความขัดแย้งในกฎที่กำหนดคณิตศาสตร์ที่ฉันให้ ฉันจะเพิ่มพื้นที่นำสำหรับ Z = 1 เพื่อให้คณิตศาสตร์สอดคล้องกัน
ลุค

@ ลุคในกฎทั่วไปไม่ควรเปลี่ยน แต่จริงๆแล้วมีความขัดแย้ง เท่าที่ฉันสามารถบอกได้คำตอบอื่น ๆ ก็ไม่ได้คิดเช่นกัน ฉันจะอัปเดตคำตอบของฉันในภายหลังจากนั้นเพราะจะทำให้สั้นลง
เลเวลริเวอร์เซนต์

@ ลุคอัปเดตแล้ว แต่หมายความว่าฉันมีทั้งพื้นที่ว่างนำหน้าและพื้นที่ต่อท้ายบน Z = 1 ฉันเข้าใจว่าเป็นไปตามจิตวิญญาณของสิ่งที่คุณต้องการและตกลงแม้ว่ามันจะไม่ได้เป็นไปตามถ้อยคำเกี่ยวกับช่องว่างต่อท้ายและตัวอย่างสำหรับ Z = 1
เลเวลริเวอร์เซนต์

อ๊ะอีกครั้งใช่ว่าไม่เป็นไร ขอโทษสำหรับความสับสน.
ลูกา

3

C, 294 289 285 283 281 270 265 237 218 ไบต์

#include<math.h>
o,i,p,r;char*c="acgtTGCA",d[256]={[0 ...254]='-'};P(w,z)float w;{for(;;poll(0,0,r=w*1e3))p=fabs(sinf(M_PI*i++/z))*z+.5,r=rand()&3,o^=4*!p,printf(p?"%*c%s%c\n":"%*c\n",z-p+1,c[r+o],d+256-p*2,c[r+4-o]);}

หรือเวอร์ชันที่ยาวกว่าซึ่งแยกวิเคราะห์อินพุตจาก main:

#include<stdlib.h>
#include<math.h>
o,i,p,r;char*c="acgtTGCA",d[256]={[0 ...254]='-'};main(n,v)char**v;{for(;n=strtod(v[2],0);poll(0,0,n=atof(v[1])*1e3))p=fabs(sinf(M_PI*i++/n))*n+.5,r=rand()&3,o^=4*!p,printf(p?"%*c%s%c\n":"%*c\n",n-p+1,c[r+o],d+256-p*2,c[r+4-o]);}

เป็นการใช้งานโดยรวมที่ค่อนข้างงี่เง่าโดยมีเทคนิคการพิมพ์บางอย่างที่ขาดหายไปมันมีบางส่วนที่ขาดหายไปใช้ไวยากรณ์ K&R สำหรับฟังก์ชั่นและอาศัยการเริ่มต้นของ GCC ดังนั้นจึงไม่ได้มาตรฐาน รุ่นฟังก์ชั่นยังคงใช้ globals ดังนั้นจึงสามารถเรียกได้เพียงครั้งเดียว!

รุ่นฟังก์ชั่นใช้เวลา 2 พารามิเตอร์ รอ (เป็นวินาที) และซูม นี่คือผู้โทรมา:

#include <stdlib.h>
int main( int argc, const char *const *argv ) {
    if( argc != 3 ) {
        printf( "Usage: %s <delay> <zoom>\n", argv[0] );
        return EXIT_FAILURE;
    }
    const float delay = atof( argv[1] );
    const int zoom = strtod( argv[2], 0 );
    if( delay < 0 || zoom <= 0 ) {
        printf( "Invalid input.\nUsage: %s <delay> <zoom>\n", argv[0] );
        return EXIT_FAILURE;
    }
    P( delay, zoom );
    return EXIT_SUCCESS;
}

ทำงานเป็น:

./dna <delay> <zoom>
./dna 0.5 8

ชำรุด:

// Globals initialise to 0
o,                                 // Ordering (upper/lower first)
i,                                 // Current iteration
p,                                 // Current indent
r;                                 // Current random value
char*c="acgtTGCA",                 // The valid letters
    d[256]={[0 ...254]='-'};       // Line of dashes (for printing)
main(n,v)char**v;{                 // K&R-style main definition (saves 2 bytes)
    // n will be used for Zoom, random number & casting delay
    for(
        ;n=strtod(v[2],0);         // Store zoom
        poll(0,0,n=atof(v[1])*1e3) // After each loop, use poll to delay
                                   // (Use variable to cast delay to int)
    )
        p=fabs(sinf(M_PI*i++/n))*n+.5,   // Calculate separation / 2
        r=rand()&3,                      // Pick random number [0-4)
        o^=4*!p,                         // Reverse order if crossing
        printf(p                         // Print... if not crossing:
                ?"%*c%s%c\n"             //  indent+character+dashes+character
                :"%*c\n",                //  Else indent+character
                n-p+1,                   // Width of indent + 1 for char
                c[r+o],                  // First character
                d+256-p*2,               // Dashes
                c[r+4-o]                 // Second character
        );
}

คุณได้รับอนุญาตให้ใช้ฟังก์ชั่นแทนหลัก () ซึ่งจะช่วยให้คุณประหยัดไบต์ของและstrtod atof
ลุค

@Luke Ah cool; ผมจะดูว่ามากที่จะช่วยประหยัด ...
เดฟ

3

C, 569 402 361 ไบต์

#include<stdlib.h>
u,l,r,m,n,Z,I,y=0,x=0;main(c,char**v){Z = atoi(v[1]);I=atof(v[2])*1000000;srand(time(0));char *a="ACGTtgca";while(1){r=rand()%4;usleep(I);double s=sin(3.14*x++/Z);u=floor(((-1*s+1)*Z)+0.5);l=floor(((s+1)*Z)+0.5);m=(u<l)?u:l;n=u<l?l:u;char z[n+1];memset(z,' ',n);z[l]=a[r+4];z[u]=a[r];for(y=m+1;y<n;y++)z[y]='-';z[n+1]='\0';printf("%s\n",z);}}

การทำอย่างนี้ค่อนข้างเร็วดังนั้นฉันแน่ใจว่ามีบางอย่างที่ฉันสามารถทำได้เพื่อลดคะแนนของฉัน แต่ฉันมีความสุขที่ฉันได้รับโปรแกรมนี้เพื่อรวบรวมและทำงานอย่างถูกต้องในการพยายามครั้งแรก

รุ่น De-golf:

#include<stdio.h>
#include<math.h>
#include<unistd.h>
#include<time.h>
#include<stdlib.h>
u,l,r,m,n,Z,I,y=0,x=0;
main(c,char**v){
   Z = atoi(v[1]);
   I=atof(v[2])*1000000;
   srand(time(0));
   char *a="ACGTtgca";
   while(1){
      r=rand()%4;
      usleep(I);
      double s=sin(3.14*x++/Z);
      u=floor(((-1*s+1)*Z)+0.5);
      l=floor(((s+1)*Z)+0.5);
      m=(u<l)?u:l;
      n=u<l?l:u;
      char z[n+1];
      memset(z,' ',n);
      z[l]=a[r+4];
      z[u]=a[r];
      for(y=m+1;y<n;y++)z[y]='-';
      z[n+1]='\0';
      printf("%s\n",z);
   }
}

UPDATE: ฉันปรับลูปเพื่อพิมพ์ทุกอย่างในคำสั่งการพิมพ์เดียวและใช้ความจริงที่ว่าตัวแปรถูกกำหนดเป็น int โดยค่าเริ่มต้นเพื่อโกนบางไบต์ UPDATE2: การเปลี่ยนชื่อ var บางรายการและการใช้ลอจิกแบบสั้น ๆ เพื่อลดจำนวนไบต์


คุณต้องได้รับ GCC มันเป็น Linux แต่คุณสามารถเรียกใช้บน windows ด้วย Cygwin ได้ ตัวแปร (ถ้าประกาศไว้ที่จุดเริ่มต้นของโปรแกรมหรือเป็นอาร์กิวเมนต์ของฟังก์ชัน) ไม่จำเป็นต้องมีประเภทพวกมันจะถือว่าเป็น int เช่นเดียวกันกับฟังก์ชั่น และฉันค่อนข้างมั่นใจว่าคุณไม่จำเป็นต้องมีสิ่งเหล่านั้น
เลเวลริเวอร์เซนต์

1
นอกจากนี้คุณมีงานพิมพ์มากเกินไป :-D อย่างใดอย่างหนึ่ง 1. ใช้ putchar เพื่อพิมพ์อักขระหนึ่งตัวต่อครั้งหรือ 2. หาสิ่งที่คุณต้องการพิมพ์แล้วพิมพ์ด้วยการใส่ 3. หาวิธีใช้งาน printf เดียวโดยใช้นิพจน์ที่ซับซ้อนขนาดใหญ่ อย่างไรก็ตาม +1
เลเวลริเวอร์เซนต์

โอเคขอบคุณสำหรับคำแนะนำ! ฉันจะลองพิมพ์ใบแทรกเดียว นั่นเป็นความคิดที่ดีและฉันแน่ใจว่ามันจะปรับปรุงคะแนนของฉัน ฉันจะเสียใจเมื่อฉันมีเวลาในวันนี้ ขอบคุณ @steveverrill
Danwakeem

2

JavaScript (ES6) 241 244 227 222 231 ไบต์

สิ่งนี้ดูน่าสนใจ - ฉันรักศิลปะ ASCII!
เพิ่งเริ่มต้นยังอยู่ในขั้นตอนของการเล่นกอล์ฟ ...

(I,Z)=>{c=i=0,setInterval(_=>{with(Math)m=sin(PI*i++/Z),a=round(++m*Z),b=round((2-m)*Z),r=random()*4|0,D="TGAC"[r],d="actg"[r],e=a-b,c^=!e,p=" ".repeat(a>b?b:a)+(c?D:d)+"-".repeat(e?abs(e)-1:0)+(e?a>b?d:D:""),console.log(p)},I*1e3)

--- แก้ไข: ปรากฎว่าฉันไม่สามารถใส่ใน eval () - มิฉะนั้นจะไม่สามารถเข้าถึง vars I และ Z (เพิ่ม 9 ไบต์)

- บันทึกไปแล้ว 6 ไบต์ขอบคุณ user81655
- บันทึกได้ 5 ไบต์ขอบคุณ Dave

คำอธิบาย

(I,Z)=>{
  c=i=0,                                // clear vars
  setInterval(_=>{                      // repeat

    with(Math)                         
      m=sin(PI*i++ / Z),                // calculate waves
      a=round(++m * Z),
      b=round((2-m) * Z),
      r=random()*4|0,                   // get random amino-acids
      D="TGAC"[r],
      d="actg"[r],
      e=a-b,
      c^=!e,                            // alternate upper/lowercase
      p=                                // prepare output
        " ".repeat(
          a>b ? b : a
        )+(
          c ? D : d
        )+

        "-".repeat(
          e ? abs(e)-1 : 0
        )+(
          e ? a>b ? d : D : ""
        ),

      console.log(p)                    // return output
  },I*1e3)                              // repeat for every 'I' seconds
}

1
คุณสามารถบันทึกอีก 4 ไบต์โดยใช้c^=!eแทนc+=a==b(ให้คุณลบการ%2ตรวจสอบในภายหลัง) นอกจากนี้ยัง-m+2อาจจะ2-m!
เดฟ

@Dave - ขอบคุณ! คุณจะอธิบายว่า c ^ =! e ทำอะไรได้จริงหรือ ฉันไม่เคยเห็นแบบนั้นมาก่อน :)
59

มันเหมือนกับc=c^(e==0); มันใช้ XOR ในลักษณะเดียวกับที่คุณเคยมีการเพิ่ม ถ้าคุณไม่คุ้นเคยกับแฮคเกอร์ก็ดำเนินการบิต: Exclusive OR (วิกิพีเดียสามารถอธิบายได้อย่างถูกต้อง)
เดฟ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.