간단한 DNA 시뮬레이터


18

귀하의 코드는 매우 간단한 ASCII 아트 표현의 DNA를 영원히 생성 할 것입니다. 리스트, 함수에 대한 인수, stdin 등 원하는 형식으로 두 숫자를 입력으로 사용합니다.

  • I0.0에서 1.0 사이 의 부동 소수점 간격 (초)
  • Z1에서 64까지의 정수인 줌 레벨

코드는 I초 당 한 줄을 stdout 또는 그와 동등한 것으로 인쇄하여 다음과 같은 무한 출력을 생성합니다 (줌 레벨 4의 경우).

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

즉, DNA의 고객 대표는, 하이픈으로 연결된 정현파 한 쌍의 하나의 문자로 이루어진 a, c, g, 및 t, 문자의 나머지 A, C, G, 및 T. 경우 x우리가 현재 인쇄하고있는 라인의 0 인덱스 번호입니다, 소문자 파의 문자의 0 기반의 위치는 다음과 같이 주어진다 (sin(πx / Z) + 1) * Z하고, 대문자 파에 의해 주어진다 (-sin(πx / Z) + 1) * Z모두 둥근는 가장 가까운에 (하지 낭패) 정수. 자세한 내용은 :

  • 두 개의 파도가 겹치는 경우 대문자로 시작하여 앞쪽에있는 파도를 번갈아 가며 교체해야합니다. (소문자 웨이브로 시작하면 존재하지 않는 이중 나선이 생깁니다 !)
  • 대소 문자를 무시하면 A는 항상 T와 쌍을 이루고 C는 항상 실제 DNA와 같이 G와 쌍을 이룹니다. 쌍 자체는 4 가지 가능성에 대한 균일 한 분포로 무작위로 선택되어야합니다. 코드의 연속 실행에서 쌍 선택이 동일하거나 다른지 여부는 중요하지 않습니다. 출력물에 명백한 패턴이없고 최소한 수십억의 기간이있는 한 무작위 선택의 통계적 품질은 문제가되지 않습니다 ( RANDU 와 같은 결함이있는 PRNG 는 괜찮습니다).
  • 후행 공백이 없어야하거나 모든 선을 해당 줌 레벨에서 웨이브의 최대 위치까지 채 웁니다 (위의 예에서는 9 자).

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에 대한 이유가 정말로 필요합니까? 백 스토리는 거의 항상 이와 같이 바보입니다.
Patrick Roberts

@PatrickRoberts 알고 있지만 그 이유가 얼마나 바보 같은지 지적하고 많은 코드 골퍼가합니다. 너무 심각하게 생각하지 마십시오! ;)
TanMath

"무작위 선택"이란 무엇입니까? RANDU는 괜찮습니까? 더 짧은 반복 시퀀스는 어떻습니까?
KSFT

답변:


4

루비, 개정판 B 171161 바이트

z = 1의 출력 수정에는 10 바이트가 필요합니다. 특별한 경우입니다. 90도에서 보면 나선은 실제로 3 자 너비이지만 0도에서 보면 1 자 너비 만 보입니다. z = 1의 선행 공백이 더 이상 필요하지 않습니다.

필요한 문자 수를 계산할 때 대괄호를 제거하고 자르기 전에 y.abs에 2를 곱하면 비용이 절약됩니다.

마지막으로 number의 거듭 제곱으로 복소수 산술을 사용하여 ( 와에 include Math필요한)을 피했습니다 . 복소수의 허수 부분은 기간 2 * PI 대신 기간 4로 반복된다는 점을 제외하고 sin x와 같습니다. 이 변경 사항 저장은 1 또는 0 바이트입니다.sinPIi

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

루비, Rev A 165 bytes

예상보다 길다. 탐구해야 할 몇 가지 잠재적 인 골프 기회가 있습니다.

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.
Luke

으악! Z = 1은 특별한 경우입니다. 그것은 의도적이지 않으며 실제로 내가 제공 한 수학에 주어진 규칙에서 모순입니다. 수학의 일관성을 유지하기 위해 Z = 1에 선행 공간을 추가하겠습니다.
Luke

@Luke의 일반적인 규칙은 변경해서는 안되지만 실제로 모순이있었습니다. 내가 알 수있는 한 다른 답변도 고려하지 않았습니다. 그러면 답변이 짧아 지므로 나중에 답변을 업데이트하겠습니다.
레벨 리버 St

@Luke가 업데이트되었지만 Z = 1에 선행 공간과 후행 공간이 모두 있음을 의미합니다. 나는 그것이 후행 공백에 대한 문구와 Z = 1의 예에 엄격하게 따르지는 않지만 원하는 것의 정신에 따른다는 것을 이해합니다.
레벨 리버 St

다시 그렇습니다, 그렇습니다. 혼란을 드려 죄송합니다.
Luke

3

C, 294 289 285 283 268 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]);}

일부 printf 트릭을 사용하여 전체적으로 꽤 멍청한 구현입니다. 누락 된 일부가 포함되어 있으며 함수에 K & R 구문을 사용하며 GCC의 범위 이니셜 라이저를 사용하므로 이는 표준이 아닙니다. 또한 함수 버전은 여전히 ​​전역을 사용하므로 한 번만 호출 할 수 있습니다!

기능 버전은 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
        );
}

main () 대신 함수를 사용하면 strtod및 의 바이트를 절약 할 수 있습니다 atof.
Luke

@ 루크 아 멋진; 나는 그것이 얼마나 절약되는지 볼 것이다 ...
Dave

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

점수를 줄이려면 할 수있는 다른 일이 있다고 확신하지만 첫 번째 시도에서이 프로그램을 컴파일하고 제대로 실행하게되어 기쁩니다.

골프 골프 버전 :

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

업데이트 : 하나의 print 문에서 모든 것을 인쇄하도록 루프를 조정하고 변수가 기본적으로 int로 정의되어 일부 바이트를 면도한다는 사실을 사용했습니다. UPDATE2 : 몇 가지 더 많은 바이트를 면도하기 위해 일부 var 이름 바꾸기 및 일부 로직 단축.


GCC를 확보해야합니다. Linux이지만 Cygwin을 사용하여 Windows에서 실행할 수도 있습니다. 변수 (프로그램 시작시 또는 함수 인수로 선언 된 경우)는 유형이 필요하지 않으며 int 인 것으로 가정합니다. 기능과 동일합니다. 그리고 나는 당신이 그 포함이 필요하지 않을 것이라고 확신합니다.
레벨 리버 St

1
또한 너무 많은 printfs가 있습니다 :-D. 1. putchar를 사용하여 한 번에 한 문자 씩 인쇄하거나 2. 인쇄 할 내용을 해결 한 다음 puts로 모두 인쇄하십시오. 3. 복잡한 표현이 많은 단일 printf를 사용하는 방법을 알아 봅니다. 어쨌든 +1.
레벨 리버 세인트

좋습니다 제안에 감사드립니다! 나는 하나의 인쇄 진술을 시도하고 만들 것입니다. 그것은 좋은 생각이며 그것이 내 점수를 향상시킬 것이라고 확신합니다. 오늘 시간이 있으면 이걸 골라 줄 게요. 감사합니다 @ steveverrill
Danwakeem

2

자바 스크립트 (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 바이트 추가)

-user81655 덕분에 6 바이트 절약-Dave 덕분에
5 바이트 절약

설명

(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
c^=!e대신에 검사 c+=a==b를 제거 할 수 있도록 대신 4 바이트를 더 저장할 수 있습니다 %2. 또한 -m+2될 수 있습니다 2-m!
Dave

@ 데이브-감사합니다! c ^ =! e가 실제로 무엇을하는지 설명해 주시겠습니까? 나는 전에 그것을 본 적이 없다 :)
Aᴄʜᴇʀᴏɴғᴀɪʟ

그것은 동일합니다 c=c^(e==0); 이전에 추가 한 것과 같은 방식으로 XOR을 적용합니다. XOR에 익숙하지 않다면 비트 연산입니다 : eXclusive OR (wikipedia에서 올바르게 설명 할 수 있음)
Dave
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.