Как считать нули и единицы в файле?

учитывая файл (двоичный или текстовый), каков самый быстрый или элегантный способ в C++ для подсчета единиц и нулей в двоичном представлении этого файла?

9 ответов


Я бы рекомендовал вам использовать массив результатов:

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 };

после того, как вы прочитали файл в as неподписанные символ массив, вы можете просто подвести:

    for (int i = 0; i < arraysize; i++) {
        sum0+=8-cheating[unsignedbytearray[i]];
        sum1+=cheating[unsignedbytearray[i]];
    }

очень трудно написать код, который был бы быстрее или элегантнее :P


создать 256 длина char массив. Поток через файл байт за раз, увеличивая позицию массива каждого байта. Жесткий код число 1s в каждом из 256 байтов в другом массиве. Перемножить 2 массива и сумму.

Не уверен в элегантности и определенно требует больше памяти, но может быть быстрее, чем алгоритм linuxuser27.


всякий раз, когда я хочу знать лучший трюк манипуляции битом для конкретной задачи, я начинаю здесь:http://graphics.stanford.edu / ~seander/bithacks.html

В разделе "подсчет битов, параллельно" они перечисляют 12 метод операции для подсчета битов, установленных в 32-битном целочисленном. Они также показывают методы для целых чисел больше 32 бит.


в любой системе основное время выполнения ввода-вывода. И как достичь быстрого ввода / вывода, очень зависит от системы. Таким образом, нет единого ответа на "самый быстрый".

самый "элегантный" зависит от глаз, которые видят.

таким образом, в целом, ни один вопрос не имеет окончательного ответа; это звучит как рыбалка для решений домашнего задания. Это домашнее задание?


вот полный пример (ну, почти есть упражнение для исполнителя в конце...) Он использует количество команд 12 для 32 байтовых значений изhttp://graphics.stanford.edu / ~seander/bithacks.html . Он также загружает файл с помощью mmap, поскольку это (часто) быстрее, чем другие методы чтения. Я думаю, что единственное, что нужно сделать, чтобы сделать это быстрее, - это разделить количество на несколько потоков. Но в моей системе он уже обрабатывает файлы 10MB в пределах 0.03 s, что кажется нормальным мне.

#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;
}

Я бы прочитал в файле один unsigned int одновременно и подсчитайте количество битов, включенных в каждом до EOF. Вы можете использовать классический алгоритм разреженного подсчета для подсчета числа 1s в unsigned int значение.

int bitcount (unsigned int n)  
{
   int count = 0 ;
   while (n)  {
      count++ ;
      n &= (n - 1) ;
   }
   return count ;
}

просто сделать выше для всех читать в unsigned int значениями и сохранить текущую сумму.


я бы попытался использовать std::bitset, Он имеет хорошую реализацию count() метод (граф интерфейс), сохраняя предварительно вычисленный массив длиной 256 для подсчета всех возможных байты. Для подсчета нулей,

std::bitset<N> bs;
size_t zero_count = bs.size() - bs.count();

я бы инициализировать file_zero_count = 0 и откройте файл. Тогда в каждой итерации цикла я бы читал N биты в bitset, и добавить zero_count из этих N бита file_zero_count. Тогда прочтите следующее N биты и так далее ...

решающим выбором является значение N. Мой первый выбор будет таким, что N биты помещаются в одну страницу памяти, например N = 4096.


один простой и быстрый способ-построить таблицу поиска, которая сообщает вам, сколько 1 имеет любой из символов, например, " a " с ASCII 97 имеет 3. Вы можете хранить такую таблицу поиска в массиве фиксированной длины для постоянного доступа. Загрузите файл постранично в память, чтобы свести к минимуму количество ввода-вывода и подсчитать для каждого символа последовательно.

Если вы больше информации о типе данных, которые вы обрабатываете, могут быть созданы более интересные решения. Например, если вы знаете чтобы файл содержал текстовые данные на естественном языке, вы можете создавать таблицы поиска для часто встречающихся терминов, таких как "the", " of " И "и", чтобы ускорить подсчет вычислений. Такая таблица может эффективно храниться в хэш-таблице.


Я бы прочитал в файле байт за байтом и подсчитал количество 1/0 в каждом байте:

причина, по которой я бы выбрал byte, заключается в том, что легко собрать lookuptable для числа 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);

теперь вам просто нужно определить соответствующий тип для счетчика, а остальное-история.