如何计算文件中的零和1?

时间:2010-10-10 23:15:10

标签: c++ binary

给定一个文件(二进制文本或文本文件),C ++中最快或最优雅的方法是计算该文件的二进制表示中的1和0?

9 个答案:

答案 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);

现在你只需要为计数器定义一个合适的类型,其余的就是历史。