c++ - 如何将 O(1) 中的数组置零?

标签 c++ time-complexity

有没有办法将时间复杂度为 O(1) 的数组置零?很明显这可以通过for-loop,memset来完成。但是他们的时间复杂度不是O(1)。

最佳答案

但不是任何数组。它需要一个为此精心设计的数组才能工作。

template <typename T, size_t N>
class Array {
public:
    Array(): generation(0) {}

    void clear() {
        // FIXME: deal with overflow
        ++generation;
    }

    T get(std::size_t i) const {
        if (i >= N) { throw std::runtime_error("out of range"); }

        TimedT const& t = data[i];
        return t.second == generation ? t.first : T{};
    }

    void set(std::size_t i, T t) {
        if (i >= N) { throw std::runtime_error("out of range"); }

        data[i] = std::make_pair(t, generation);
    }


private:
    typedef std::pair<T, unsigned> TimedT;

    TimedT data[N];
    unsigned generation;
};

原理很简单:

  • 我们使用generation属性定义一个epoch
  • 当一个项目被设置时,它被设置的时代被记录下来
  • 只能看到当前时代的项目
  • 因此,清除等同于增加纪元计数器

该方法有两个问题:

  • 存储增加:对于每个项目,我们存储一个纪元
  • 世代计数器溢出:存在最大纪元数

后者可以使用一个真正的大整数来阻止(uint64_t 以更多存储为代价)。

前者是自然结果,一种可能的解决方案是使用存储桶来淡化该问题,例如将多达 64 个项目与单个计数器相关联,并使用位掩码标识哪些项目在此计数器内有效。


编辑:只是想回到水桶的想法上。

原始解决方案每个元素的开销为 8 字节(64 位)(如果已经 8 字节对齐)。根据存储的元素,这可能是也可能不是什么大问题。

如果是大事,思路就是用桶;当然,就像所有权衡一样,它会进一步减慢访问速度。

template <typename T>
class BucketArray {
public:
     BucketArray(): generation(0), mask(0) {}
     
     T get(std::size_t index, std::size_t gen) const {
         assert(index < 64);

         return gen == generation and (mask & (1 << index)) ?
                data[index] : T{};
     }

     void set(std::size_t index, T t, std::size_t gen) {
         assert(index < 64);

         if (generation < gen) { mask = 0; generation = gen; }

         mask |= (1 << index);
         data[index] = t;
     }

private:
     std::uint64_t generation;
     std::uint64_t mask;
     T data[64];
};

请注意,这个由固定数量的元素组成的小数组(我们实际上可以对其进行模板化并静态检查它是否小于或等于 64)只有 16 个字节的开销。这意味着我们有一个每个元素 2 位的开销

template <typename T, size_t N>
class Array {
    typedef BucketArray<T> Bucket;
public:
    Array(): generation(0) {}
    
    void clear() { ++generation; }

    T get(std::size_t i) const {
        if (i >= N) { throw ... }

        Bucket const& bucket = data[i / 64];
        return bucket.get(i % 64, generation);
    }

    void set(std::size_t i, T t) {
        if (i >= N) { throw ... }

        Bucket& bucket = data[i / 64];
        bucket.set(i % 64, t, generation);
    }

private:
    std::uint64_t generation;
    Bucket data[N / 64 + 1];
};

我们将空间开销降低了 32 倍。例如,现在数组甚至可以用来存储 char,而以前它是禁止的。代价是访问变得更慢,因为我们得到一个除法 模(我们什么时候会得到一个标准化的操作,一次返回两个结果?)。

关于c++ - 如何将 O(1) 中的数组置零?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10797284/

相关文章:

具有更新/验证循环的 C++ FLTK 的 Fl_Input 数据

php - MySQL - 在数据库复杂性中查找表

c++ - 为什么 string::resize 和 string::substr O(1)

algorithm - 为什么求算法的时间复杂度总是取对数的底数为2?

c - C语言的简单时间复杂度

python - 线性时间与二次时间

c++ - 有没有办法使用winapi打开蓝牙?

java - 学习用于 Android 游戏开发的 OpenGL ES

c++ - 如何显示cmake的gcc版本?

c++ - 什么是 undefined reference /未解析的外部符号错误,我该如何解决?