C(≥4000)
このコードは、2 GB未満のRAMを使用する(少し多く使用する)ことも、最初の1分間で出力を生成することもありません。しかし、約6分待つと、すべてのk -merカウントが一度に出力されます。
kマーをカウントする最高のkを制限するオプションが含まれています。kが妥当な範囲に制限されている場合、コードは1分以内に終了し、2 GiB未満のRAMを使用します。OPはこのソリューションを評価し、4000を大幅に超えない限度で1分未満で終了します。
どのように機能しますか?
アルゴリズムには4つのステップがあります。
- 入力ファイルをバッファに読み込み、改行を取り除きます。
インデックスの配列を入力バッファーにサフィックスソートします。たとえば、文字列のサフィックスは次のmississippi
とおりです。
mississippi
ississippi
ssissippi
sissippi
issippi
ssippi
sippi
ippi
ppi
pi
i
辞書式順にソートされたこれらの文字列は次のとおりです。
i
ippi
issippi
ississippi
mississippi
pi
ppi
sippi
sissippi
ssippi
ssissippi
すべてのkについて長さkのすべての等しい部分文字列が、サフィックスでソートされた配列の隣接するエントリにあることが簡単にわかります。
整数配列が入力され、各インデックスkに個別のkマーの数を格納します。これは、プロセスをスピードアップするために少し複雑な方法で行われます。2つの隣接するエントリをサフィックスでソートされた配列と見なします。
p l
v v
issippi
ississippi
pは2つのエントリの最長共通プレフィックスの長さを示し、lは2番目のエントリの長さを示します。例えば、A対について、我々は、長さの新しい別個のサブストリング検索K用のp < K ≤ リットル。以来、P « Lはしばしば保持し、各ペアの配列多数のエントリをインクリメントすることは非現実的です。代わりに、配列を差分配列として格納します。ここで、各エントリkは、k -merの数と(k -1)-mer の数の差を示します。これはフォームの更新をオンにします
0 0 0 0 +1 +1 +1 +1 +1 +1 0 0 0
フォームのはるかに高速な更新に
0 0 0 0 +1 0 0 0 0 0 -1 0 0
lは常に異なり、実際にはすべての0 < l < nが正確に1回出現することを注意深く観察することで、差を省略し、代わりに差から金額に変換するときに各差から1を差し引くことができます。
- 差異は金額に変換されて印刷されます。
ソースコード
このソースは、接尾辞配列のソートにlibdivsufsortを使用します。この呼び出しでコンパイルすると、コードは仕様に従って出力を生成します。
cc -O -o dsskmer dsskmer.c -ldivsufsort
または、次の呼び出しでコンパイルすると、コードはバイナリ出力を生成できます。
cc -O -o dsskmer -DBINOUTPUT dsskmer.c -ldivsufsort
最高制限するKれるk個の -mersがカウントされる、供給-DICAP = K kが限界であるが。
cc -O -o dsskmer -DICAP=64 dsskmer.c -ldivsufsort
-O3
コンパイラがこのオプションを提供している場合は、コンパイルしてください。
#include <stdlib.h>
#include <stdio.h>
#include <divsufsort.h>
#ifndef BINOUTPUT
static char stdoutbuf[1024*1024];
#endif
/*
* load input from stdin and remove newlines. Save length in flen.
*/
static unsigned char*
input(flen)
int *flen;
{
size_t n, len, pos, i, j;
off_t slen;
unsigned char *buf, *sbuf;
if (fseek(stdin, 0L, SEEK_END) != 0) {
perror("Cannot seek stdin");
abort();
}
slen = ftello(stdin);
if (slen == -1) {
perror("Cannot tell stdin");
abort();
}
len = (size_t)slen;
rewind(stdin);
/* Prepare for one extra trailing \0 byte */
buf = malloc(len + 1);
if (buf == NULL) {
perror("Cannot malloc");
abort();
}
pos = 0;
while ((n = fread(buf + pos, 1, len - pos, stdin)) != 0)
pos += n;
if (ferror(stdin)) {
perror("Cannot read from stdin");
abort();
}
/* remove newlines */
for (i = j = 0; i < len; i++)
if (buf[i] != '\n')
buf[j++] = buf[i];
/* try to reclaim some memory */
sbuf = realloc(buf, j);
if (sbuf == NULL)
sbuf = buf;
*flen = (int)j;
return sbuf;
}
/*
* Compute for all k the number of k-mers. kmers will contain at index i the
* number of (i + 1) mers. The count is computed as an array of differences,
* where kmers[i] == kmersum[i] - kmersum[i-1] + 1 and then summed up by the
* caller. This algorithm is a little bit unclear, but when you write subsequent
* suffixes of an array on a piece of paper, it's easy to see how and why it
* works.
*/
static void
count(buf, sa, kmers, n)
const unsigned char *buf;
const int *sa;
int *kmers;
{
int i, cl, cp;
/* the first item needs special treatment */
kmers[0]++;
for (i = 1; i < n; i++) {
/* The longest common prefix of the two suffixes */
cl = n - (sa[i-1] > sa[i] ? sa[i-1] : sa[i]);
#ifdef ICAP
cl = (cl > ICAP ? ICAP : cl);
#endif
for (cp = 0; cp < cl; cp++)
if (buf[sa[i-1] + cp] != buf[sa[i] + cp])
break;
/* add new prefixes to the table */
kmers[cp]++;
}
}
extern int
main()
{
unsigned char *buf;
int blen, ilen, *sa, *kmers, i;
buf = input(&blen);
sa = malloc(blen * sizeof *sa);
if (divsufsort(buf, sa, blen) != 0) {
puts("Cannot divsufsort");
abort();
}
#ifdef ICAP
ilen = ICAP;
kmers = calloc(ilen + 1, sizeof *kmers);
#else
ilen = blen;
kmers = calloc(ilen, sizeof *kmers);
#endif
if (kmers == NULL) {
perror("Cannot malloc");
abort();
}
count(buf, sa, kmers, blen);
#ifndef BINOUTPUT
/* sum up kmers differences */
for (i = 1; i < ilen; i++)
kmers[i] += kmers[i-1] - 1;
/* enlarge buffer of stdout for better IO performance */
setvbuf(stdout, stdoutbuf, _IOFBF, sizeof stdoutbuf);
/* human output */
for (i = 0; i < ilen; i++)
printf("%d\n", kmers[i]);
#else
/* binary output in host endianess */
fprintf(stderr, "writing out result...\n");
fwrite(kmers, sizeof *kmers, ilen, stdout);
#endif
return 0;
}
バイナリファイル形式は、次のプログラムで人間が読める出力形式に変換できます。
#include <stdlib.h>
#include <stdio.h>
static int inbuf[BUFSIZ];
static char outbuf[BUFSIZ];
extern int main()
{
int i, n, sum = 1;
setbuf(stdout, outbuf);
while ((n = fread(inbuf, sizeof *inbuf, BUFSIZ, stdin)) > 0)
for (i = 0; i < n; i++)
printf("%d\n", sum += inbuf[i] - 1);
if (ferror(stdin)) {
perror("Error reading input");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
出力例
ファイルのバイナリ形式での出力例をchr22.fa
見つけることができるここに。bzip2 -d
まず解凍してください。人間が読める形式の出力よりもはるかに圧縮率が高い(3.5 kB対260 MB)ため、出力はバイナリ形式でのみ提供されます。ただし、圧縮されていないときの参照出力のサイズは924 MBであることに注意してください。次のようなパイプを使用したい場合があります。
bzip2 -dc chr2.kmerdiffdat.bz2 | ./unbin | less
J
、単純な解決策は単純に `[:〜。]`ですが、それでうまくいくとは限りません。