给定一个文件(二进制文本或文本文件),C ++中最快或最优雅的方法是计算该文件的二进制表示中的1和0?
答案 0 :(得分:13)
我建议你使用结果数组:
unsigned char cheating[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3,
4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2,
3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4,
5, 4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2,
3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4,
5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5,
6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3,
4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4, 3, 4, 4, 5, 3,
4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6,
7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4,
5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5,
6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 };
以 unsigned char 数组读取文件后,您可以加总:
for (int i = 0; i < arraysize; i++) {
sum0+=8-cheating[unsignedbytearray[i]];
sum1+=cheating[unsignedbytearray[i]];
}
编写代码非常困难,更快或更优雅:P
答案 1 :(得分:4)
创建256个长度的char数组。一次一个字节地传输文件,增加每个字节的数组位置。硬编码另一个阵列中256字节的每一个中的1的数量。将2个数组乘以和。
不确定优雅并且肯定需要更多内存,但可能比linuxuser27的算法更快。
答案 2 :(得分:4)
每当我想知道特定任务的最佳位操作技巧时,我就从这里开始:http://graphics.stanford.edu/~seander/bithacks.html
在“并行计数位设置”下,它们列出了一种12运算方法,用于对以32位整数设置的位进行计数。它们还显示了大于32位的整数的方法。
答案 3 :(得分:2)
在大多数系统上,主要执行时间都是i / o-bound。如何实现最快的i / o很大程度上取决于系统。所以对“最快”没有单一的答案。
大多数“优雅”取决于看到的眼睛。
总而言之,这两个问题都没有明确的答案;听起来像钓鱼作为家庭作业的解决方案。是作业吗?
答案 4 :(得分:1)
这是一个完整的例子(最后几乎有一个实现者的练习......)它使用来自http://graphics.stanford.edu/~seander/bithacks.html的32字节值的12指令计数。它还使用mmap加载文件,因为它(通常)比其他读取方法更快。我认为唯一要做的就是将速度分成多个线程。但是在我的系统上它已经处理了不到0.03秒的10MB文件,这对我来说似乎没问题。
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <iostream>
#include <unistd.h>
int main()
{
int fd = open("junk.txt",O_RDWR);
struct stat info;
fstat(fd, &info);
void * page = mmap(0, info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
uint64_t bitcount = 0;
//Lets ignore alignment issues for now - I think mmap guarantees that they're OK.
uint32_t * v_ptr = static_cast<uint32_t*>(page);
for(unsigned int i=0; i<info.st_size/4; ++i)
{
uint32_t v = *v_ptr;
v = v - ((v >> 1) & 0x55555555); // reuse input as temporary
v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // temp
bitcount += ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24; // count
++v_ptr;
}
//Need to adjust for the last 0-3 bytes... Exercise for the reader
munmap( page, info.st_size );
std::cout<<"Total of "<<bitcount<<" set bits"<<std::endl;
}
答案 5 :(得分:0)
我会一次在文件中读取一个unsigned int
并计算每个中打开的位数直到EOF。您可以使用经典稀疏计数算法来计算1
值中unsigned int
的数量。
int bitcount (unsigned int n)
{
int count = 0 ;
while (n) {
count++ ;
n &= (n - 1) ;
}
return count ;
}
只需对unsigned int
值中的所有读取值执行以上操作,并保持运行总计。
答案 6 :(得分:0)
我会尝试使用std::bitset
,它通过保留一个长度为256的预先计算的数组来计算所有可能的count()
方法(count interface)。
字节。对于计数零,
std::bitset<N> bs;
size_t zero_count = bs.size() - bs.count();
我会初始化file_zero_count = 0
并打开文件。然后在循环的每次迭代中,我将N
位读入位集,并将zero_count
位的N
添加到file_zero_count
。然后读取下一个N
位,依此类推......
关键的选择是N
的价值。我的第一选择是N
位适合一个存储页面,例如N = 4096
。
答案 7 :(得分:0)
一种简单快捷的方法是建立一个查找表,告诉您任何字符有多少1个,例如带有ASCII 97的'a'有3.您可以将这样的查找表存储在固定长度的数组中,以便进行恒定时间访问。将文件逐页加载到内存中,以最大限度地减少每个字符的I / O数和计数。
如果您有关于正在处理的数据类型的更多信息,可以创建更有趣的解决方案。例如,如果您知道该文件包含自然语言文本数据,则可以为频繁出现的术语构建查找表,例如“the”,“of”和“and”以加速计数计算。这样的查找表可以有效地存储在哈希表中。
答案 8 :(得分:0)
我会逐字节地读取文件并计算每个字节中的1/0的数量:
我选择byte的原因是,手动将一个字节数1的查找表放在一起很容易。注意:我在计算一个字节中的1个。但是我向后构建了表(所以它实际上是0的数量的计数(因为它是1的倒数))。
int countOne[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // Top Line + 1 2^5 (16 set)
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // Top Line + 1 2^6 (32 set)
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // Top Line + 2 2^5 + 2^6 (16/32 set)
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // Top Line + 1 2^7 (64 set)
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // Top Line + 2 2^5 + 2^7 (16/64 set)
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // Top Line + 2 2^6 + 2^7 (32/64 set)
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // Top Line + 3 2^5 + ^6 + 2^7 (16/32/64 set)
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // Top Line + 1 2^8 (128 set)
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // Top Line + 2 2^5 + 2^8 (16/128 set)
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // Top Line + 2 2^6 + 2^8 (32/128 set)
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // Top Line + 3 2^5 + 2^6 + 2^8 (16/32/128 set)
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, // Top Line + 2 2^7 + 2^8 (64/128 set)
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // Top Line + 3 2^5 + 2^7 + 2^8 (16/64/128 set)
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, // Top Line + 3 2^6 + 2^7 + 2^8 (32/64/128 set)
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8};
现在你需要做的就是在流中使用一个std :: for_each迭代器(不要删除空格。
counter = std::for_each(std::istreambuf_iterator<unsigned char>(file),
std::istreambuf_iterator<unsigned char>(),
couter);
现在你只需要为计数器定义一个合适的类型,其余的就是历史。