C взломать для хранения бит, который занимает 1 бит пространства?

У меня длинный список чисел от 0 до 67600. Теперь я хочу сохранить их, используя массив длиной 67600. Элементу присваивается значение 1, если число было в наборе, и оно установлено в 0, если число не находится в наборе. то есть. каждый раз мне нужна только 1-битная информация для хранения присутствия числа. Есть ли какой-либо взлом в C/С++, который помогает мне достичь этого?

Ответ 1

В С++ вы можете использовать std::vector<bool>, если размер является динамическим (это частный случай std::vector, см. this) в противном случае существует std::bitset (предпочтительнее std::bitset, если это возможно.) Также существует boost::dynamic_bitset, если вам нужно установить/изменить размер во время выполнения. Вы можете найти информацию об этом здесь, это довольно круто!

В C (и С++) вы можете вручную реализовать это с помощью побитовых операторов. Хорошее резюме общих операций здесь. Одна вещь, которую я хочу упомянуть, - это хорошая идея использовать целые числа без знака, когда вы выполняете бит-операции. << и >> являются undefined при смещении отрицательных целых чисел. Вам нужно будет выделить массивы некоторого целостного типа типа uint32_t. Если вы хотите сохранить бит N, это займет N/32 этих uint32_t s. Бит i хранится в i % 32 'th бит i / 32' th uint32_t. Возможно, вы захотите использовать интегральный тип разного размера в зависимости от вашей архитектуры и других ограничений. Примечание: предпочитайте использовать существующую реализацию (например, как описано в первом абзаце для С++, искать решения Google для C) по сравнению с вашим собственным (если вы специально этого не хотите, и в этом случае я предлагаю узнать больше о бинарные/бит-манипуляции из других источников, прежде чем решать это.) Такие вещи были сделаны до смерти, и есть "хорошие" решения.

Существует несколько трюков, которые, возможно, будут потреблять только один бит: например. массивы битовых полей (применимы также к C), но используется ли меньшее пространство для компилятора. См. эту ссылку.

Обратите внимание: что бы вы ни делали, вы почти наверняка никогда не сможете использовать точно N бит для хранения N бит информации - ваш компьютер, скорее всего, не сможет выделить менее 8 бит: if вам нужно 7 бит, вам придется потратить 1 бит, и если вам нужно 9, вам придется взять 16 бит и отбросить 7 из них. Даже если ваш компьютер (CPU + RAM и т.д.) Может "работать" на одном бите, если вы работаете в ОС с помощью malloc/new, для вашего распределителя не будет разумно отслеживать данные до такого небольшого точность из-за накладных расходов. Эта последняя квалификация была довольно глупой - вы не найдете используемую архитектуру, которая позволяет вам работать на менее чем 8 бит за один раз, когда я представляю себе:)

Ответ 2

Вы должны использовать std::bitset.

std::bitset функционирует как массив bool (фактически как std::array, поскольку он копирует по значению), но использует только 1 бит памяти для каждого элемента.

Другая опция vector<bool>, которую я не рекомендую, потому что:

  • Он использует медленную указательную область и память кучи, чтобы разрешить изменение размера, которое вам не нужно.
  • Этот тип часто замалчивается стандартами-пуристами, потому что он утверждает, что является стандартным контейнером, но не может придерживаться определения стандартного контейнера *.

* Например, стандартно-совместимая функция может ожидать, что &container.front() создаст указатель на первый элемент любого типа контейнера, который не с std::vector<bool>. Возможно, это ниппель для вашего случая использования, но все же стоит знать о нем.

Ответ 4

Если вы хотите записать его на C, укажите массив из char длиной 67601 бит (67601/8 = 8451), а затем включите/выключите соответствующий бит для каждого значения.

Ответ 5

Другие дали правильную идею. Вот моя собственная реализация bitsarr, или 'array' бит. Беззнаковый char представляет собой один байт, поэтому он представляет собой массив беззнаковых символов, который хранит информацию в отдельных битах. Я добавил возможность хранения двоичных значений TWO или FOUR в дополнение к значениям бит ONE, поскольку они делят 8 (размер байта) и будут полезны, если вы хотите сохранить огромное количество целых чисел, которое будет находиться в диапазоне от 0 -3 или 0-15.

При настройке и получении математика выполняется в функциях, поэтому вы можете просто дать ей индекс, как если бы это был обычный массив - он знает, где искать.

Кроме того, пользователь несет ответственность за то, чтобы не передать значение, чтобы установить слишком большое значение, иначе оно приведет к другим значениям. Он может быть изменен таким образом, что переполнение будет повторяться до 0, но это просто сделает его более запутанным, поэтому я решил доверять себе.

#include<stdio.h>
#include <stdlib.h>
#define BYTE 8

typedef enum {ONE=1, TWO=2, FOUR=4} numbits;

typedef struct bitsarr{
    unsigned char* buckets;
    numbits n;
} bitsarr;


bitsarr new_bitsarr(int size, numbits n)
{
    int b = sizeof(unsigned char)*BYTE;
    int numbuckets = (size*n + b - 1)/b;
    bitsarr ret;  
    ret.buckets = malloc(sizeof(ret.buckets)*numbuckets);
    ret.n = n;
    return ret;
}
void bitsarr_delete(bitsarr xp)
{
    free(xp.buckets);
}

void bitsarr_set(bitsarr *xp, int index, int value)
{
    int buckdex, innerdex;
    buckdex = index/(BYTE/xp->n);
    innerdex = index%(BYTE/xp->n);
    xp->buckets[buckdex] = (value << innerdex*xp->n) | ((~(((1 << xp->n) - 1) << innerdex*xp->n)) & xp->buckets[buckdex]);

    //longer version

    /*unsigned int width, width_in_place, zeros, old, newbits, new;
    width = (1 << xp->n) - 1; 
    width_in_place = width << innerdex*xp->n;
    zeros = ~width_in_place;
    old = xp->buckets[buckdex];
    old = old & zeros;
    newbits = value << innerdex*xp->n;
    new = newbits | old;
    xp->buckets[buckdex] = new; */

}

int bitsarr_get(bitsarr *xp, int index)
{
    int buckdex, innerdex;
    buckdex = index/(BYTE/xp->n);
    innerdex = index%(BYTE/xp->n);
    return ((((1 << xp->n) - 1) << innerdex*xp->n) & (xp->buckets[buckdex])) >> innerdex*xp->n;

    //longer version

    /*unsigned int width = (1 << xp->n) - 1; 
    unsigned int width_in_place = width << innerdex*xp->n;
    unsigned int val = xp->buckets[buckdex];
    unsigned int retshifted = width_in_place & val;
    unsigned int ret = retshifted >> innerdex*xp->n;
    return ret; */
}

int main()
{
    bitsarr x = new_bitsarr(100, FOUR);
    for(int i = 0; i<16; i++)
        bitsarr_set(&x, i, i);
    for(int i = 0; i<16; i++)
        printf("%d\n", bitsarr_get(&x, i));
    for(int i = 0; i<16; i++)
        bitsarr_set(&x, i, 15-i);
    for(int i = 0; i<16; i++)
        printf("%d\n", bitsarr_get(&x, i));
    bitsarr_delete(x);
}