シンプルなDNAシミュレーター


18

あなたのコードは、DNAの非常に単純なASCIIアート表現を永久に生成します。必要な形式の入力として2つの数字を受け取ります。リスト、関数の引数、標準入力などです。

  • I0.0〜1.0の秒単位の浮動小数点間隔
  • Z1から64までの整数としてのズームレベル

コードはI、1 秒ごとにstdoutまたはそれに相当するものに1行を出力し、次のような無限出力を生成します(ズームレベル4の場合)。

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

具体的には、DNAの我々の表現は、ハイフンで接続された正弦波のペア、文字から成るものであるacg、及びt、文字の他ACG、およびT。場合はx私たちが現在印刷している行の0から始まるインデックス番号で、小文字の波の中の文字の0ベースの位置は次式で与えられ(sin(πx / Z) + 1) * Z、そして大文字の波にで与えられ(-sin(πx / Z) + 1) * Z、両方の丸い最寄りに(床のではありません)整数。詳細:

  • 2つの波が重なる場合は、大文字の波から始めて、どちらの波が前面にあるかを交互に切り替える必要があります。(小文字の波から始めると、存在しない二重らせんができます!)
  • 大文字小文字を無視して、実際のDNAのように、Aは常にTとペアになり、Cは常にGとペアになります。ペア自体は、4つの可能性にわたって均一に分布するようにランダムに選択する必要があります。ペアの選択がコードの連続した実行で同じか異なるかは関係ありません。あなたのランダムな選択の統計的品質は長い出力が明らかなパターンと、少なくとも十億にピリオドがないほどの問題ではありません(のような欠陥のPRNG RANDUは罰金ですが。)
  • 後続のスペースがないか、そのズームレベル(上記の例では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には本当に理由が必要ですか?バックストーリーはほとんどいつもこのように馬鹿げています。
パトリックロバーツ

@PatrickRoberts私は知っていますが、多くのコード愛好家がしていることの理由がどれほど馬鹿げているかを指摘していました。あまり真剣に考えないでください!;)
TanMath

「ランダムに選択」とはどういう意味ですか?RANDUは大丈夫ですか?短い繰り返しシーケンスはどうですか?
KSFT

回答:


4

Ruby、Rev B 171161バイト

z = 1の出力の修正には10バイトかかります。特殊なケースです。90度で見た場合、らせんの幅は実際には3文字ですが、0度で見ると幅は1文字しか見えません。 z = 1の先行スペース0は不要になりました

角かっこを削除し、必要な-文字の数を計算するときに切り捨ての前にy.absに2を掛けることにより、いくらか節約できます。

最後に、私は回避include Math(のために必要sinPI数の累乗で複素数演算を使用して)i。複素数の虚数部は、周期2 * PIではなく周期4で繰り返されることを除いて、sin xと同等です。この変更の保存は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つの小さな問題:ズームレベル1の先頭にスペースがあります。また、テストプログラムにI=gets.to_iはが必要ですI=gets.to_f
ルーク

おっと!Z = 1は特別なケースです。これは意図的なものではなく、実際に私が提供した数学を考えるとルールの矛盾です。数学の一貫性を保つために、Z = 1の先頭スペースを追加します。
ルーク

一般的なルールの@Lukeは変更すべきではありませんが、実際には矛盾がありました。私が知る限り、他の答えもそれを考慮していません。答えは後で更新しますが、その方が短くなります。
レベルリバーセント

@Lukeは更新されましたが、Z = 1に先行スペースと後続スペースの両方があることを意味します。私はそれがあなたが望むものの精神に従っているのでOKであることを理解していますが、厳密には末尾のスペースに関するフレーズと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]);}

または、メインからの入力を解析する長いバージョン:

#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、and のバイト数が節約されますatof
ルーク

@ルークアークール; 私はそれがどれだけ節約するか見ていきます
デイブ

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

更新:1つの印刷ステートメントですべてを印刷するようにループを調整し、変数がデフォルトでintとして定義され、いくつかのバイトを削るという事実を使用しました。UPDATE2:いくつかの変数の名前変更と、いくつかのロジックを短縮して、さらに数バイト削る。


GCCを取得する必要があります。Linuxですが、Cygwinを使用してWindowsで実行することもできます。変数(プログラムの先頭または関数の引数として宣言されている場合)は型を必要とせず、intであると想定されます。機能についても同じです。そして、それらのインクルードは必要ないと確信しています。
レベルリバーセント

1
また、printfsが多すぎます:-D。1. putcharを使用して一度に1文字を印刷するか、2。印刷したいものを見つけてからputsですべて印刷します。3.大きな複雑な式を含む単一のprintfを使用する方法を考えます。とにかく、+ 1。
レベルリバーセント

提案をありがとう 単一の印刷ステートメントを作成してみます。それは良いアイデアであり、それが私のスコアを改善すると確信しています。今日は時間があるときにこれを再確認します。ありがとう@steveverrill
ダンワケム

2

JavaScriptの(ES6)241の 244 227 222 231バイト

これは面白そうに見えた-アスキーアートが大好き!
まだ始まったばかりで、まだゴルフの途中です...

(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()に入れることができないことがわかります-そうでなければ、var 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%2チェックを後で削除できるようにする)を使用して、さらに4バイトを保存できます。また-m+2可能性があります2-m
デイブ

@デイブ-ありがとう!c ^ =!eが実際に何をするのか説明してもらえますか?私は前にそれを見たことがありません:)
Aᴄʜᴇʀᴏɴғᴀɪʟ16年

と同じc=c^(e==0)です。以前に追加したのと同じ方法でXORを適用します。排他的論理和(ウィキペディアはそれを正しく説明することができます):あなたはXORに慣れていないなら、それはビット演算です
デイブ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.