Реализация массива поиска для массива из N целых чисел ограниченного диапазона

0

У меня есть 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.

  • 0
    @FredOverflow: я верю в это. Я хожу по графику перестановок, и обязательно, чтобы узлы не посещались дважды, иначе алгоритм не завершит работу в разумные сроки. Кроме того, я хожу по графику особым образом, и в большинстве случаев не придется проходить все перестановки или что-то подобное.
  • 0
    Может быть связано с stackoverflow.com/questions/19709529/…, но должно быть адаптировано для bool, так как std::vector<bool> специализируется ...
Теги:
arrays
algorithm
data-structures

3 ответа

1
Лучший ответ

В случае булевых значений вы действительно должны использовать биты: любой 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;
  • 0
    Спасибо, я думаю, что это довольно элегантное решение для случая, когда диапазон возможных значений имеет ширину 2 (то есть, когда array является логическим). Хотелось бы еще рассмотреть случай, когда он шире, например 3 возможных значения.
  • 1
    В этом случае вам нужно только заменить (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() для вычисления размера хранилища, она обеспечивает меньшую точность, чем целочисленные вычисления.
1

Если я правильно вас понял... Для логического типа достаточно, если вы используете индекс элемента в одномерном массиве для представления ваших битов. Например, для 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, вы можете использовать два бита на одно значение.

  • 0
    Да, для логического случая и эффективно, и сжато просто использовать битовое представление индексов в одномерном логическом массиве. Но я не совсем уверен, что это все-таки лучшее решение, когда может быть более двух возможных значений. Например, для 3 возможных значений вы будете тратить 1 бит на каждое сохраняемое значение (поскольку вы будете использовать два бита для хранения только 3 возможных значений).
  • 0
    Не совсем потеряно, но я понимаю вашу точку зрения. Можно было бы использовать какой-то способ сжатия данных, но это требует знаний о самих данных. Если, например, 90% данных имеют значение «ложь», вы можете хранить только истинные значения в некоторой хэш-таблице.
0

Следующее может помочь:

Итак, из вашего примера 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;
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню