Trình mô phỏng DNA đơn giản


18

Mã của bạn sẽ tạo ra một đại diện DNA nghệ thuật ASCII rất đơn giản, mãi mãi. Nó sẽ lấy hai số làm đầu vào theo bất kỳ định dạng nào bạn muốn: làm danh sách, làm đối số cho hàm, trên stdin, v.v.

  • Một khoảng thời gian trôi nổi tính Ibằng giây giữa 0,0 và 1,0 (đã bao gồm)
  • Mức thu phóng Zdưới dạng số nguyên từ 1 đến 64 (đã bao gồm)

Mã của bạn sẽ in một dòng thành thiết bị xuất chuẩn hoặc tương đương mỗi Igiây, tạo ra một đầu ra vô hạn trông giống như thế này (đối với mức thu phóng 4):

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

Cụ thể, đại diện của chúng ta về DNA là một cặp sóng sin nối với nhau bằng dấu gạch nối, bao gồm một trong những nhân vật a, c, g, và t, người kia trong những nhân vật A, C, G, và T. Nếu xlà số 0 được lập chỉ mục của dòng chúng tôi hiện đang in, vị trí dựa trên 0 của ký tự trong sóng chữ thường được đưa ra (sin(πx / Z) + 1) * Zvà trong sóng chữ hoa được đưa ra bởi (-sin(πx / Z) + 1) * Z, cả hai được làm tròn (không được thả nổi) đến gần nhất số nguyên. Biết thêm chi tiết:

  • Trong trường hợp hai sóng trùng nhau, bạn cần xen kẽ sóng nào ở phía trước, bắt đầu bằng sóng chữ hoa. (Bắt đầu với sóng chữ thường sẽ cho chúng ta một chuỗi xoắn kép không tồn tại !)
  • Bỏ qua trường hợp, A luôn ghép với T và C luôn ghép với G, như trong DNA thực. Các cặp nên được chọn ngẫu nhiên với phân phối đồng đều theo bốn khả năng. Không có vấn đề gì nếu sự lựa chọn của các cặp giống nhau hoặc khác nhau trên các lần chạy mã liên tiếp của bạn. Chất lượng thống kê của các lựa chọn ngẫu nhiên của bạn không phải là vấn đề miễn là đầu ra không có mô hình rõ ràng và ít nhất là trong hàng tỷ (các PRNG thiếu sót như RANDU vẫn ổn.)
  • Bạn phải không có khoảng trắng ở cuối hoặc đệm mỗi dòng đến vị trí tối đa của sóng ở mức thu phóng đó (trong ví dụ trên, chín ký tự.) Thu phóng cấp 1 có thể có một không gian theo dõi bổ sung tùy chọn vì lý do toán học.

Vì DNA nhỏ, mã của bạn sẽ cần phải càng ngắn càng tốt.

Ví dụ khác:

Thu phóng cấp 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
...

Thu phóng cấp 2:

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

Thu phóng cấp 1 (lưu ý không gian hàng đầu):

 G
 a
 C
 t
...


9
"Bởi vì DNA nhỏ, mã của bạn sẽ cần phải càng ngắn càng tốt." Có thật không?
TanMath

3
@TanMath Bạn có thực sự cần một lý do để Code-Golf không? Các câu chuyện hậu trường hầu như luôn ngớ ngẩn như thế này, chỉ cần đi với nó.
Patrick Roberts

@PatrickRoberts Tôi biết, nhưng tôi chỉ chỉ ra lý do ngớ ngẩn như thế nào, nhiều người chơi golf mã. Đừng quá nghiêm túc! ;)
TanMath

"Chọn ngẫu nhiên" nghĩa là gì? RANDU có ổn không? Điều gì về một chuỗi lặp lại ngắn hơn?
KSFT

Câu trả lời:


4

Ruby, Rev B 171 161 byte

Sửa lỗi đầu ra cho z = 1 tốn 10 byte. Đó là một trường hợp đặc biệt: đường xoắn ốc thực sự rộng 3 ký tự nếu bạn nhìn nó ở góc 90 độ, nhưng khi chúng ta nhìn vào nó ở 0 độ thì nó chỉ rộng 1 ký tự. không có khoảng trắng hàng đầu trên z = 1 không còn cần thiết

Một số khoản tiết kiệm bằng cách loại bỏ dấu ngoặc và nhân y.abs với 2 trước khi cắt bớt khi tính toán số lượng ký tự cần thiết.

Cuối cùng, tôi đã tránh include Math(bắt buộc cho sinPI) bằng cách sử dụng số học số phức với quyền hạn của số i. Phần ảo của số phức tương đương với sin x, ngoại trừ việc nó lặp lại với chu kỳ 4 thay vì giai đoạn 2 * PI. Tiết kiệm cho thay đổi này là 1 hoặc 0 byte.

->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 byte

Đây là cách lâu hơn dự kiến. Có một vài cơ hội chơi gôn tiềm năng được khám phá.

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}}

Nhận xét trong chương trình thử nghiệm

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]

Trông được! Một vấn đề nhỏ: có một không gian hàng đầu cho mức thu phóng 1. Ngoài ra, trong chương trình thử nghiệm của bạn I=gets.to_iphải có I=gets.to_f.
Lu-ca

Rất tiếc! Bạn đúng rằng Z = 1 là trường hợp đặc biệt. Điều đó không có chủ ý và thực sự là một mâu thuẫn trong các quy tắc được đưa ra cho toán học mà tôi cung cấp. Tôi sẽ thêm không gian hàng đầu cho Z = 1 để làm cho phép toán nhất quán.
Lu-ca

@Luke trong các quy tắc chung không nên thay đổi, nhưng thực sự đã có một mâu thuẫn. Theo như tôi có thể nói, các câu trả lời khác cũng không xem xét nó. Sau đó, tôi sẽ cập nhật câu trả lời của mình, vì nó sẽ ngắn hơn theo cách đó.
Cấp sông St

@Luke đã cập nhật, nhưng điều đó có nghĩa là tôi có cả không gian hàng đầu và không gian kéo dài trên Z = 1. Tôi hiểu rằng đó là theo tinh thần của những gì bạn muốn và do đó OK, mặc dù nó không hoàn toàn theo cách diễn đạt về không gian dấu và ví dụ cho Z = 1.
Cấp sông St

Một lần nữa, vâng, điều đó tốt Xin lỗi vì sự nhầm lẫn.
Lu-ca

3

C, 294 289 285 283 281 270 265 237 218 byte

#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]);}

Hoặc phiên bản dài hơn phân tích cú pháp đầu vào từ chính:

#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]);}

Đây là một triển khai tổng thể khá ngu ngốc, với một số thủ thuật printf được đưa vào. Nó có một số thiếu bao gồm, sử dụng cú pháp K & R cho chức năng và dựa vào các trình khởi tạo phạm vi của GCC, vì vậy điều này không chuẩn lắm. Ngoài ra phiên bản chức năng vẫn sử dụng toàn cầu, vì vậy nó chỉ có thể được gọi một lần!

Phiên bản chức năng có 2 tham số; chờ đợi (tính bằng giây) và phóng to. Đây là một người gọi cho nó:

#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;
}

Chạy như:

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

Phá vỡ:

// 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
        );
}

Bạn được phép sử dụng hàm thay vì hàm main (), điều này sẽ giúp bạn tiết kiệm các byte của strtodatof.
Lu-ca

@Luke Ah tuyệt vời; Tôi sẽ thấy nó tiết kiệm được bao nhiêu ...
Dave

3

C, 569 402 361 byte

#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);}}

Đã đánh bại nó khá nhanh vì vậy tôi chắc chắn có một số điều khác tôi có thể làm để giảm điểm nhưng tôi rất vui vì đã có chương trình này để biên dịch và chạy đúng trong lần thử đầu tiên.

Phiên bản 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);
   }
}

CẬP NHẬT: Tôi đã điều chỉnh vòng lặp để in mọi thứ trong một câu lệnh in và sử dụng thực tế là các biến được định nghĩa là int theo mặc định để cạo một số byte. CẬP NHẬT2: Một số đổi tên var và rút ngắn logic để cạo thêm một vài byte.


Bạn cần phải có được GCC. Đó là Linux nhưng bạn cũng có thể chạy nó trên windows với Cygwin. Các biến (nếu được khai báo ở đầu chương trình hoặc dưới dạng đối số hàm) không cần một kiểu, chúng được coi là int. Tương tự với các chức năng. Và tôi khá chắc chắn rằng bạn sẽ không cần những thứ đó.
Cấp sông St

1
Ngoài ra, bạn có quá nhiều printfs :-D. Hoặc là 1. sử dụng putchar để in một ký tự cùng một lúc hoặc 2. tìm ra những gì bạn muốn in và sau đó in tất cả bằng các lệnh. 3. tìm ra cách sử dụng một printf với biểu thức phức tạp lớn trong đó. Dù sao, +1.
Cấp sông St

Được rồi cảm ơn cho những lời đề nghị! Tôi sẽ cố gắng và thực hiện một tuyên bố in duy nhất. Đó là một ý tưởng tốt và tôi chắc chắn rằng nó sẽ cải thiện điểm số của tôi. Tôi sẽ regolf điều này khi tôi có một số thời gian ngày hôm nay. Cảm ơn @steveverrill
Danwakeem

2

JavaScript (ES6) 241 244 227 222 231 byte

Điều này có vẻ thú vị - Tôi yêu nghệ thuật ASCII!
Mới bắt đầu, vẫn đang trong quá trình chơi gôn ...

(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)

--- EDIT: hóa ra tôi thực sự không thể đặt nó trong eval () - nếu không thì nó không thể truy cập vào vars I và Z (vì vậy thêm 9 byte)

- đã lưu 6 byte nhờ người dùng81655
- đã lưu 5 byte nhờ Dave

Giải trình

(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
Bạn có thể lưu thêm 4 byte bằng cách sử dụng c^=!ethay vì c+=a==b(cho phép bạn xóa %2kiểm tra sau). Cũng -m+2có thể được 2-m!
Dave

@Dave - cảm ơn! Bạn có phiền giải thích c ^ =! E thực sự làm gì không? Tôi chưa bao giờ thấy điều đó trước đây :)
A

Nó giống như c=c^(e==0); nó áp dụng XOR giống như cách trước đây bạn đã có một bổ sung. Nếu bạn không quen với XOR, thì đó là thao tác theo chiều bit: eXinating HOẶC (wikipedia có thể giải thích chính xác)
Dave
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.