У меня есть arr[N]
, и вам нужно реализовать массив поиска для всех возможных наборов значений, которые могут принять arr
, например, 2 ^ N возможных значений для простейшего случая, когда массив является bool arr[N]
.
Это можно сделать, определив N-мерный логический массив поиска. Например, для N = 4 и arr
булев, это будет bool lookup[2][2][2][2]
. lookup
может затем сохранять и извлекать любые возможные значения arr
путем lookup[arr[0]][arr[1]][arr[2]][arr[3]]
.
Это неудобно писать и, возможно, производительность неэффективно, а также, потому N
меняется, так что фактическая реализация придется использовать for
цикла для хранения и поиска. Это проблема, так как поиск является очень распространенной операцией, и сделать их как можно быстрее - это весь смысл этого упражнения.
Есть ли другие способы реализации этой идеи? Меня бы интересовало решение для булевых arr
, возможно, используя какое-то битовое представление, а также более общее решение, в котором диапазон значений в arr
шире, чем просто 2.
В случае булевых значений вы действительно должны использовать биты: любой N
который может обрабатывать, должен вписывать N^2
записи в память, что на современных машинах составляет порядка 2 ^ 32 до 2 ^ 38, поэтому вы не можете достичь N = 64
любом случае.
Тем не менее, вы можете использовать младшие значащие биты для каждой записи массива и просто выделить хранилище значения 2^N
Бит-представления вашего массива могут просто служить в качестве индексов в этом магазине.
Что-то вроде этого:
uint64_t compressArray(long length, bool* array) {
uint64_t result = 0;
for(long i = length; i--; ) result = (result << 1) | (array[i] ? 1 : 0);
return result;
}
...
int* store = malloc(sizeof(*store) * (1 << N));
bool* array = ...;
// Now you can access the ints in store like this:
store[compressArray(N, array)] = 3;
array
является логическим). Хотелось бы еще рассмотреть случай, когда он шире, например 3 возможных значения.
(result << 1) | (array[i] ? 1 : 0)
по result*3 + array[i]
(конечно, предполагается, что вы используете диапазон значений 0, 1, 2) и вычислите размер хранилища как uint64_t storeSize = 1; for(long i = N; i--; ) storeSize *= 3;
Я бы не использовал функцию pow()
для вычисления размера хранилища, она обеспечивает меньшую точность, чем целочисленные вычисления.
Если я правильно вас понял... Для логического типа достаточно, если вы используете индекс элемента в одномерном массиве для представления ваших битов. Например, для arr [4]
ar[0] = 0, ar[1] = 0, ar[2] = 0, ar[3] = 0 0000
ar[0] = 1, ar[1] = 0, ar[2] = 0, ar[3] = 0 0001
ar[0] = 0, ar[1] = 1, ar[2] = 0, ar[3] = 0 0010
ar[0] = 1, ar[1] = 1, ar[2] = 0, ar[3] = 0 0011
и т.д...
Например, для arr, содержащих значения от 1 до 3, вы можете использовать два бита на одно значение.
Следующее может помочь:
Итак, из вашего примера ValueRange = 2
поскольку bool
может принимать 2 значения; Size = N
#include <cassert>
#include <cstddef>
#include <vector>
template<typename T, int ValueRange, int Size>
class MultiArray
{
static_assert(ValueRange > 1, "Need at least 2 or more values");
static_assert(Size > 0, "Size should be strictly positive");
public:
MultiArray() : values(computeTotalSize())
{
assert(!values.empty());
}
const T& get(const std::vector<size_t>& indexes) const
{
return values[computeIndex(indexes)];
}
T& get(const std::vector<size_t>& indexes)
{
return values[computeIndex(indexes)];
}
size_t computeIndex(const std::vector<size_t>& indexes) const
{
assert(indexes.size() == Size);
size_t index = 0;
size_t mul = 1;
for (size_t i = 0; i != Size; ++i) {
assert(indexes[i] < ValueRange);
index += indexes[i] * mul;
mul *= ValueRange;
}
assert(index < values.size());
return index;
}
std::vector<size_t> computeIndexes(size_t index) const
{
assert(index < values.size());
std::vector<size_t> res(Size);
size_t mul = values.size();
for (size_t i = Size; i != 0; --i) {
mul /= ValueRange;
res[i - 1] = index / mul;
assert(res[i - 1] < ValueRange);
index -= res[i - 1] * mul;
}
return res;
}
private:
size_t computeTotalSize() const
{
size_t totalSize = 1;
for (int i = 0; i != Size; ++i) {
totalSize *= ValueRange;
}
return totalSize;
}
private:
std::vector<T> values;
};
Поэтому используйте его так:
int main()
{
MultiArray<int, 2, 4> m;
m.get({0, 0, 1, 0}) = 42;
m.get({1, 1, 0, 0}) = 42;
// Just for test purpose:
for (size_t i = 0; i != 16; ++i) {
assert(m.computeIndex(m.computeIndexes(i)) == i);
}
return 0;
}
std::vector<bool>
специализируется ...