我搜索了如何计算经常在.txt文件中出现一串字母并查找(以及其他)此帖子的方式:Count the number of times each word occurs in a file
通过计算单词(用空格分隔)来处理问题。
但是,我需要做一些略有不同的事情:
我有.txt file
包含数十亿字母而没有任何格式(没有空格,没有任何空格,没有换行符,没有硬回复等),只是字母a的loooooong线, g,t和c(即:DNA序列;))。
现在我想编写一个贯穿整个序列的程序,并计算每个可能的9个字母连续序列出现在该文件中的频率。
是的,有4^9
种可能的9个字母'字组合组合。由字符A,G,T和C组成,但我只想输出前1000名。
由于没有空格或任何东西,我必须逐个查看文件并检查所有9个字母的单词'出现,即:
ATAGAGCTAGATCCCTAGCTAGTGACTA
包含序列:
ATAGAGCTA, TAGAGCTAG, AGAGCTAGA, etc.
我希望你知道我的意思,我很难用英语描述它,因为它不是我的母语。
致以最诚挚的问候,并提前感谢大家!
答案 0 :(得分:2)
与数十亿相比,2 ^ 18或256k似乎突然变小。好消息是,这意味着您的直方图可以存储在大约1 MB的数据中。一个简单的方法是将每个字母转换为2位表示,假设您的文件只包含AGCT,而没有RYMK ......短号和通配符。
这就是''esquisse'所做的。它将9个字节的文本打包成18位值,并递增相应的直方图bin。为了加速[转换一点,它读取4个字节并使用查找表一次转换4个字形。
我不知道这会有多快,但它应该是合理的。我没有测试它,但我知道它编译,至少在gcc下。没有打印输出,但有一个辅助函数可以将序列打包二进制格式解压缩回文本。
它应该至少给你一个好的起点
#include <vector>
#include <array>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <exception>
namespace dna {
// helpers to convert nucleotides to packed binary form
enum Nucleotide : uint8_t { A, G, C, T };
uint8_t simple_lut[4][256] = {};
void init_simple_lut()
{
for (size_t i = 0 ; i < 4; ++i)
{
simple_lut[i]['A'] = A << (i * 2);
simple_lut[i]['C'] = C << (i * 2);
simple_lut[i]['G'] = G << (i * 2);
simple_lut[i]['T'] = T << (i * 2);
}
}
uint32_t pack4(const char(&seq)[4])
{
return simple_lut[0][seq[0]]
+ simple_lut[1][seq[1]]
+ simple_lut[2][seq[2]]
+ simple_lut[3][seq[3]];
}
// you can use this to convert the historghrtam
// index back to text.
std::string hist_index2string(uint32_t n)
{
std::string result;
result.reserve(9);
for (size_t i = 0; i < 9; ++i, n >>= 2)
{
switch (n & 0x03)
{
case A: result.insert(result.begin(), 'A'); break;
case C: result.insert(result.begin(), 'C'); break;
case G: result.insert(result.begin(), 'G'); break;
case T: result.insert(result.begin(), 'T'); break;
default:
throw std::runtime_error{ "totally unexpected error while unpacking index !!" };
}
}
return result;
}
}
int main(int argc, const char**argv, const char**)
{
if (argc < 2)
{
std::cerr << "Usage: prog_name <input_file> <output_file>\n";
return 3;
}
using dna::pack4;
dna::init_simple_lut();
std::vector<uint32_t> result;
try
{
result.resize(1 << 18);
std::ifstream ifs(argv[1]);
// read 4 bytes at a time, convert to packed bits representation
// then rotate in bits 2 by 2 in our 18 bits buffer.
// increment coresponding bin by 1
const uint32_t MASK{ (1 << 19) - 1 };
const std::streamsize RB{ 4 };
uint32_t accu{};
uint32_t packed{};
// we need to load at least 9 bytes to 'prime' the engine
char buffer[4];
ifs.read(buffer, RB);
accu = pack4(buffer) << 8;
ifs.read(buffer, RB);
accu |= pack4(buffer);
if (ifs.gcount() != RB)
{
throw std::runtime_error{ " input file is too short "};
}
ifs.read(buffer, RB);
while (ifs.gcount() != 0)
{
packed = pack4(buffer);
for (size_t i = 0; i < (size_t)ifs.gcount(); ++i)
{
accu <<= 2;
accu |= packed & 0x03;
packed >>= 2;
accu &= MASK;
++result[accu];
}
ifs.read(buffer.pch, RB);
}
ifs.close();
// histogram is compiled. store data to another file...
// you can crate a secondary table of { index and count }
// it's only 5MB long and use partial_sort to extract the first
// 1000.
}
catch(std::exception& e)
{
std::cerr << "Error \'" << e.what() << "\' while reading file.\n";
return 3;
}
return 0;
}
该算法可以适用于在多个线程上运行,方法是使用正确的共享配置打开多个流中的文件,并在文件的位上运行循环。在过程结束时必须小心处理16字节的接缝。
如果并行运行,内部循环非常短,最好为每个线程提供自己的直方图并在最后合并结果,否则,锁定开销会使事情变得相当缓慢。 / p>
[编辑]傻傻的我把打包的二进制查找错了。
[EDIT2]用更快的版本替换了打包的lut。
答案 1 :(得分:1)
这对你有用,
-- Beginning of script
DECLARE @codes TABLE(code NVARCHAR(8) PRIMARY KEY NOT NULL)
INSERT @codes VALUES ('ABC'), ('FEGHJ')
-- Many more instructions
-- .....