C++ - 输出给定长度的所有可能的DNA kmers

发布于 2024-10-30 22:49:38 字数 1218 浏览 1 评论 0原文

“kmer”是长度为 K 的 DNA 序列。有效的 DNA 序列(就我的目的而言)只能包含以下 4 个碱基:A、C、T、G。我正在寻找一种 C++ 算法,它可以简单地将这些基数的所有可能组合按字母顺序输出到字符串数组中。例如,如果 K = 2,则程序应生成以下数组:

kmers[0]  = AA
kmers[1]  = AC
kmers[2]  = AG
kmers[3]  = AT
kmers[4]  = CA
kmers[5]  = CC
kmers[6]  = CG
kmers[7]  = CT
kmers[8]  = GA
kmers[9]  = GC
kmers[10] = GG
kmers[11] = GT
kmers[12] = TA
kmers[13] = TC
kmers[14] = TG
kmers[15] = TT

如果我正确地考虑了这一点,则问题实际上可以分解为将十进制整数转换为基数 4,然后替换适当的基数。我以为我可以使用 itoa 来实现这一点,但 itoa 不是 C 标准,而且我的编译器不支持它。我欢迎任何聪明的想法。这是我的示例代码:

#include <iostream>
#include <string>
#include <math.h>

#define K 3

using namespace std;

int main() {

  int num_kmers = pow(4,K);
  string* kmers = NULL;

  /* Allocate memory for kmers array */
  kmers = new string[num_kmers];

  /* Populate kmers array */
  for (int i=0; i< pow(4,K); i++) {

    // POPULATE THE kmers ARRAY HERE                                                                                                                                                         

  }

  /* Display all possible kmers */
  for (int i=0; i< pow(4,K); i++)
    cout << kmers[i] << "\n";

  delete [] kmers;
}

A "kmer" is a DNA sequence of length K. A valid DNA sequence (for my purposes) can only contain the 4 following bases: A,C,T,G. I am looking for a C++ algorithm that simply outputs all possible combinations of these bases in alphabetical order into a string array. For example, if K = 2, The program should generate the following array:

kmers[0]  = AA
kmers[1]  = AC
kmers[2]  = AG
kmers[3]  = AT
kmers[4]  = CA
kmers[5]  = CC
kmers[6]  = CG
kmers[7]  = CT
kmers[8]  = GA
kmers[9]  = GC
kmers[10] = GG
kmers[11] = GT
kmers[12] = TA
kmers[13] = TC
kmers[14] = TG
kmers[15] = TT

If I'm thinking about this correctly, the problem really breaks down to converting a decimal integer to base 4 then substituting the appropriate bases. I thought I could use itoa for this, but itoa is not C standard, and my compiler did not support it. I welcome any clever ideas. Here is my sample code:

#include <iostream>
#include <string>
#include <math.h>

#define K 3

using namespace std;

int main() {

  int num_kmers = pow(4,K);
  string* kmers = NULL;

  /* Allocate memory for kmers array */
  kmers = new string[num_kmers];

  /* Populate kmers array */
  for (int i=0; i< pow(4,K); i++) {

    // POPULATE THE kmers ARRAY HERE                                                                                                                                                         

  }

  /* Display all possible kmers */
  for (int i=0; i< pow(4,K); i++)
    cout << kmers[i] << "\n";

  delete [] kmers;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

明媚殇 2024-11-06 22:49:38

您需要使用递归来保持灵活性(即,以便您可以轻松更改 K)。

void populate(int depth, string base, string* kmers, int* kmers_offset)
{
    if(depth == K)
    {
        kmers[*kmers_offset].assign(base);
        (*kmers_offset)++;
    }
    else
    {
        static char bases[] = { 'A', 'C', 'G', 'T' };
        for(int i = 0; i < 4; ++i)
            populate(depth + 1, base + bases[i], kmers, kmers_offset);
    }
}

然后这样称呼它:

int kmers_offset = 0;
populate(0, "", kmers, &kmers_offset);

干杯。

You would need to use recursion to be flexible (i.e. so that you could change K easily).

void populate(int depth, string base, string* kmers, int* kmers_offset)
{
    if(depth == K)
    {
        kmers[*kmers_offset].assign(base);
        (*kmers_offset)++;
    }
    else
    {
        static char bases[] = { 'A', 'C', 'G', 'T' };
        for(int i = 0; i < 4; ++i)
            populate(depth + 1, base + bases[i], kmers, kmers_offset);
    }
}

And then call it like this:

int kmers_offset = 0;
populate(0, "", kmers, &kmers_offset);

Cheers.

围归者 2024-11-06 22:49:38

一旦用户输入文本被接受,您不需要将十进制转换为任何内容。

创建字符串数组也可能是一个错误,该数组随着 K 呈指数增长。只需打印输出即可。

char bases[] = { 'A', 'C', 'T', 'G' };
std::vector< int > sequence( K ); // allow dynamic K
std::vector< char > output( K * ( 2 << K * 2 ) ); // flat sequence of sequences
std::vector< char >::iterator out_it = output.begin();

int i;
do {
    // print current sequence
    for ( i = 0; i < K; ++ i ) std::cout << bases[ sequence[ i ] ];
    std::cout << '\n';

    // store current sequence
    for ( i = 0; i < K; ++ i ) * out_it ++ = bases[ sequence[ i ] ];

    // advance to next sequence
    for ( i = K; i > 0; -- i ) {
        // increment the last base that we can
        if ( sequence[ i - 1 ] != sizeof bases - 1 ) {
            ++ sequence[ i - 1 ];
            break;
        }
        // reset bases that can't be incremented
        sequence[ i - 1 ] = 0;
    }
} while ( i > 0 ); // if i <= 0, failed to increment anything, we're done.

You don't need to convert decimal to anything, once the user input text has been accepted.

It's also probably a mistake to create an array of strings, which grows exponentially with K. Just print the output.

char bases[] = { 'A', 'C', 'T', 'G' };
std::vector< int > sequence( K ); // allow dynamic K
std::vector< char > output( K * ( 2 << K * 2 ) ); // flat sequence of sequences
std::vector< char >::iterator out_it = output.begin();

int i;
do {
    // print current sequence
    for ( i = 0; i < K; ++ i ) std::cout << bases[ sequence[ i ] ];
    std::cout << '\n';

    // store current sequence
    for ( i = 0; i < K; ++ i ) * out_it ++ = bases[ sequence[ i ] ];

    // advance to next sequence
    for ( i = K; i > 0; -- i ) {
        // increment the last base that we can
        if ( sequence[ i - 1 ] != sizeof bases - 1 ) {
            ++ sequence[ i - 1 ];
            break;
        }
        // reset bases that can't be incremented
        sequence[ i - 1 ] = 0;
    }
} while ( i > 0 ); // if i <= 0, failed to increment anything, we're done.
月棠 2024-11-06 22:49:38

在我看来,这非常适合自定义迭代器。这样你的主程序就可以很简单:

std::vector<std::string> v;
v.reserve(kmerIterator<4>::end() - kmerIterator<4>::begin());
std::copy(kmerIterator<4>::begin(), kmerIterator<4>::end(),
    std::back_inserter(v));

但是,由于我们已经将 kmer 概念实现为迭代器,所以我们也可以使用所有其他通用算法。由于我们将 kmer 迭代器实现为随机访问迭代器,因此找到第 i 个 kmer 很简单:

kmerIterator<4>::begin()[i]

这是我的完整程序:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>

template<unsigned int n>
class kmerIterator :
    public std::iterator<std::random_access_iterator_tag,
        std::string,
        unsigned int>
{
private:
    typedef kmerIterator k; // 'cause I'm lazy
    difference_type it;
    kmerIterator(difference_type i) : it(i) {}
public:
    kmerIterator() : it() {}
    static k begin() {
        return 0;
    }
    static k end() {
        return difference_type(1) << n*2;
    }
    k& operator++() { ++it; return *this; }
    k operator++(int) { return it++; }
    k& operator--() { --it; return *this; }
    k operator--(int) { return it--; }
    k operator+(difference_type delta) { return it + delta; }
    k operator-(difference_type delta) { return it - delta; }
    difference_type operator-(const k& rhs) { return it - rhs.it; }
    bool operator<(const k& rhs) const { return it < rhs.it; }
    bool operator>(const k& rhs) const { return it > rhs.it; }
    bool operator<=(const k& rhs) const { return it <= rhs.it; }
    bool operator>=(const k& rhs) const { return it >= rhs.it; }
    k operator+=(difference_type delta) { return it+=delta; }
    k operator-=(difference_type delta) { return it-=delta; }
    std::string operator[](difference_type delta) const { return *k(it+delta); }
    std::string operator*() const {
        std::string rz;
        int i = 2*n;
        do {
            i -= 2;
            rz += "ACGT"[(it>>i)&3];
        } while(i);
        return rz;
    }
};

int main() {
    std::copy(kmerIterator<2>::begin(),
        kmerIterator<2>::end(),
        std::ostream_iterator<std::string>(std::cout, "\n"));

    std::vector<std::string> v;
    v.reserve(kmerIterator<4>::end() - kmerIterator<4>::begin());
    std::copy(kmerIterator<4>::begin(),
        kmerIterator<4>::end(),
        std::back_inserter(v));
    std::cout << v[42] << "\n";
    std::cout << kmerIterator<4>::begin()[56] << "\n";
}

This seems to me to be a perfect fit for a custom iterator. That way your main program can be simple:

std::vector<std::string> v;
v.reserve(kmerIterator<4>::end() - kmerIterator<4>::begin());
std::copy(kmerIterator<4>::begin(), kmerIterator<4>::end(),
    std::back_inserter(v));

But, since we have implemented the kmer concept as an interator, we get to use all of the other generic algorithms, too. Since we implemented the kmer iterator as a Random Access Iterator, then finding the i-th kmer is trivial:

kmerIterator<4>::begin()[i]

Here is my complete program:

#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>

template<unsigned int n>
class kmerIterator :
    public std::iterator<std::random_access_iterator_tag,
        std::string,
        unsigned int>
{
private:
    typedef kmerIterator k; // 'cause I'm lazy
    difference_type it;
    kmerIterator(difference_type i) : it(i) {}
public:
    kmerIterator() : it() {}
    static k begin() {
        return 0;
    }
    static k end() {
        return difference_type(1) << n*2;
    }
    k& operator++() { ++it; return *this; }
    k operator++(int) { return it++; }
    k& operator--() { --it; return *this; }
    k operator--(int) { return it--; }
    k operator+(difference_type delta) { return it + delta; }
    k operator-(difference_type delta) { return it - delta; }
    difference_type operator-(const k& rhs) { return it - rhs.it; }
    bool operator<(const k& rhs) const { return it < rhs.it; }
    bool operator>(const k& rhs) const { return it > rhs.it; }
    bool operator<=(const k& rhs) const { return it <= rhs.it; }
    bool operator>=(const k& rhs) const { return it >= rhs.it; }
    k operator+=(difference_type delta) { return it+=delta; }
    k operator-=(difference_type delta) { return it-=delta; }
    std::string operator[](difference_type delta) const { return *k(it+delta); }
    std::string operator*() const {
        std::string rz;
        int i = 2*n;
        do {
            i -= 2;
            rz += "ACGT"[(it>>i)&3];
        } while(i);
        return rz;
    }
};

int main() {
    std::copy(kmerIterator<2>::begin(),
        kmerIterator<2>::end(),
        std::ostream_iterator<std::string>(std::cout, "\n"));

    std::vector<std::string> v;
    v.reserve(kmerIterator<4>::end() - kmerIterator<4>::begin());
    std::copy(kmerIterator<4>::begin(),
        kmerIterator<4>::end(),
        std::back_inserter(v));
    std::cout << v[42] << "\n";
    std::cout << kmerIterator<4>::begin()[56] << "\n";
}
拥醉 2024-11-06 22:49:38

哇,这不应该那么难。

std::string kmers(int i, int K) {
  static const char* DNA = "ACGT";
  if (K==0) return std::string();
  return DNA[i%4] + kmers(i / 4, K-1);
}

const int K = 2;
int main () {
  for (int i = 0; i != 1<<(2*K); ++i)
    std::cout << "kmers[" << i << "] = " << kmers(i,K) << std::endl;
}

Whoa, this shouldn't be so hard.

std::string kmers(int i, int K) {
  static const char* DNA = "ACGT";
  if (K==0) return std::string();
  return DNA[i%4] + kmers(i / 4, K-1);
}

const int K = 2;
int main () {
  for (int i = 0; i != 1<<(2*K); ++i)
    std::cout << "kmers[" << i << "] = " << kmers(i,K) << std::endl;
}
全部不再 2024-11-06 22:49:38
#include <iostream>
#include <string>
#include <string_view>
#include <vector>

class kmer
{
public:
    kmer();
    kmer(std::string &str, int &k) : k_(k), str_(str)
    {
        for(int i = 0; i <= str_.size() - k_; ++i)
            value_.push_back(str_.substr(i, k_));
    }
    auto value() { return value_; }
    auto size() { return value_.size(); }
private:
    std::vector<std::string_view> value_;
    std::string_view str_;
    int k_;
};

int main()
{
    std::string seq{"AGCTAGCT"};
    kmer k4_mer(seq, 4);
    
    std::cout << "Total " << k4_mer.size() << " mers\n";
    for(auto &i : k4_mer.value())
        std::cout << i << "\n";

    return 0;
}
#include <iostream>
#include <string>
#include <string_view>
#include <vector>

class kmer
{
public:
    kmer();
    kmer(std::string &str, int &k) : k_(k), str_(str)
    {
        for(int i = 0; i <= str_.size() - k_; ++i)
            value_.push_back(str_.substr(i, k_));
    }
    auto value() { return value_; }
    auto size() { return value_.size(); }
private:
    std::vector<std::string_view> value_;
    std::string_view str_;
    int k_;
};

int main()
{
    std::string seq{"AGCTAGCT"};
    kmer k4_mer(seq, 4);
    
    std::cout << "Total " << k4_mer.size() << " mers\n";
    for(auto &i : k4_mer.value())
        std::cout << i << "\n";

    return 0;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文