假设您想以随机顺序迭代序列[0到n],只访问每个元素一次。 有没有办法在O (1)内存中执行此操作,即不使用std::iota创建[1..n]序列并通过std::random_shuffle运行它?

某种迭代器以随机顺序吐出序列将是最佳的。

要求是应该可以通过选择另一个种子来获得另一个随机顺序。

#1楼 票数:8

如果你可以就地改变序列,你可以简单地从0-N重复绘制一个随机数,然后擦除你访问过的元素,或者将它交换到最后,或者这样的方案。

#2楼 票数:6 已采纳

从理论上讲,如果你构建了一个周期正好为n的随机数生成器,并覆盖了0..n中的所有值,那么运行这一次就可以得到你喜欢的内容。

当然,这可能不是一般解决方案,至少如果你正在寻找动态的东西,因为你必须预先创建PRNG以及你如何做到这一点取决于n。

#3楼 票数:1

好吧......想一想。 你怎么知道以前访问过哪些元素?

简短的回答:你做不到。 编辑嗯,除非你计算无状态的伪随机生成器,但正如你在命令中声明的那样,这对于一般情况似乎不可行)

然而,根据实际的顺序,将元素“标记”为访问 _in-place_可能是可行的,因此技术上需要O(n)存储,但算法没有额外的存储空间

例:

const int VISITED_BIT = 0x8000; // arbitrary example

bool extract(int i) { return (i & ~VISITED_BIT); }    
bool visited(int i) { return (i & VISITED_BIT); }    
bool markvisited(int& i) { i |= VISITED_BIT); }

int main()
{
    std::vector<int> v = {2,3,4,5,6};

    int remain = v.size();
    while (remain>0)
    {
        size_t idx = rand(); // or something
        if (visited(v[idx]))
            continue;

        std::cout << "processing item #" << idx << ": " << extract(v[idx]) << "\n";
        markvisited(v[idx]);
        remain--;
    }
}

#4楼 票数:1

与大多数算法问题一样,存在时空权衡; 如果您乐意使用O(n ^ 2)时间生成所有排列,则可以在O(1)空间中解决此问题。 除了几个临时变量之外,这需要的唯一存储是随机数种子本身(或者,在这种情况下,PRNG对象),因为这足以重新生成伪随机数序列。

请注意,您必须在每次通话时为此功能提供相同的PRNG,并且您不能将其用于任何其他目的。

#include <random>

template<typename PRNG, typename INT>
INT random_permutation_element(INT k, INT n, PRNG prng) {
  typedef std::uniform_int_distribution<INT> dis;
  INT i = 0;
  for (; i < k; ++i) dis(0, i)(prng);
  INT result = dis(0, i)(prng);
  for (++i; i < n; ++i) if (dis(0, i)(prng) <= result) ++result;
  return result;
}

这是一个快速而肮脏的线束。 ./test 1000 3生成1000个完整的长度为3的排列; ./test 10 1000000 0 5生成长度为100万的10个排列中的每一个的前五个元素。

#include <iostream>

int main(int argc, char** argv) {
  std::random_device rd;
  std::mt19937 seed_gen(rd());
  int count = std::stoi(argv[1]);
  int size = std::stoi(argv[2]);
  int seglow = 0;
  int seglim = size;
  if (argc > 3) seglow = std::stoi(argv[3]);
  if (argc > 4) seglim = std::stoi(argv[4]);
  while (count-- > 0) {
    std::mt19937 prng(seed_gen());
    for (int i = seglow; i < seglim; ++i)
      std::cout << random_permutation_element(i, size, prng)
                << (i < seglim - 1 ? ' ' : '\n');
  }
  return 0;
}

如果您不太可能完成任何给定的排列,有一种更快的方法可以做到这一点,但这种编写方式看起来更好,并且可能更容易理解。 (另一种方法是以相反的顺序生成数字,这意味着你可以在生成k之后停止但是你必须做两次,首先得到结果然后调整它。)

#5楼 票数:0

没有,想一想,程序必须记住它访问过的地方。 如果有一个可以随机访问它们的迭代器,迭代器内部必须以某种方式跟踪它,你仍然会使用内存。

#6楼 票数:0

我刚刚为这种事物构建了一个结构 - 我生成了一个Heap结构(min或max,无关紧要)。 但是为了比较,我使用随机数而不是使用键值。 因此,插入堆中的项目以随机顺序放置。 然后你可以返回形成堆的基本结构的数组(将随机排序),或者你可以逐个弹出元素,然后以随机顺序返回它们。 如果将这种类型的容器用作主存储(而不是将数组与堆分开),则不存在额外的内存复杂性,因为无论如何它只是一个数组。 时间复杂度是插入的O(log N),弹出顶部元素的O(log N)。 改组就像弹出和重新插入每个元素一样简单,O(N log N)。

我甚至构建了一个花哨的Enumerator(它是C#,但你也可以用C ++ Iterator做同样的事情),在你迭代到最后之后自动改组。 这意味着每次你可以多次遍历列表(没有弹出)并且每次都获得不同的顺序,这是以每次完整迭代后的O(N log N)shuffle为代价的。 (想想就像一副纸牌。在每张卡片进入弃牌堆后,你重新洗牌,以便下次不能以相同的顺序进行。)

  ask by 4ZM translate from so

未解决问题?本站智能推荐:

1回复

随机访问时至少为O(lnN)且删除时至少为O(lnN)的数据结构[NOTDUPLICATE][重复]

这个问题在这里已有答案: 标准容器的复杂性保证是什么? 2个答案 没有标准容器从盒子中提供这样的保证,需要一些额外的操作(例如,像Jerry Coffin建议的那样), 它不是重复的 。 是否存在随机访问时至少为O(ln N)且删除时为O(ln N)的现成数据结构/容器?
1回复

创建一个随机迭代器(置换)

这个项目的最后一个大障碍,希望你们能帮助我,因为我再次陷入困境。 我正在研究的是一个动态分配的模板容器,其中所有代码都是使用常量迭代器从头开始编写的,并且是一个随机迭代器,它以索引数组的形式生成排列。 到目前为止,我已经知道它可以正确生成索引了,但是我不确定如何使用该索引数组使随机迭代器遍历
3回复

std::map中的内存分配

我正在撰写有关各种C ++词典实现(地图,词典,向量等)的报告。 使用std :: map插入的结果说明性能为O(log n)。 性能上也有持续的峰值。 我不是100%知道是什么原因造成的; 我认为它们是由内存分配引起的,但是我一直没有找到任何文献/文档来证明这一点。 谁能解决这个
3回复

size()G++中STL容器的复杂性:哪些容器是O(n)?

我想大多数人都明白size()函数的复杂性并不能保证不变。 虽然在某些实现中,它是恒定的。 G ++编译器可能是最常用的编译器。 那么,在G ++的实现中, size()的复杂性是什么? 如果它因不同的容器而异,那么哪些容器具有线性复杂性? 对于最常用的(例如list,vector,
7回复

list::size()真的是O(n)吗?

最近,我注意到有人提到std::list::size()具有线性复杂性。 根据一些消息来源,这实际上取决于实现,因为标准没有说明复杂性是什么。 此博客条目中的评论说: 实际上,这取决于您使用的是哪种 STL。 Microsoft Visual Studio V6 将 size() 实现为 {r
1回复

如何迭代哈希表实现?

我试图了解如何迭代实现的哈希表。 我无法想象它。 我对这种迭代的速度特别感兴趣。 例如: 这是一个O(hashTable.size())操作吗? 我试图挖掘源代码,但找不到合适的定义。
1回复

为什么矢量的随机访问迭代器提供了不同的指针相同的内存地址?

它输出: 注意地址是一样的,每次,问题是为什么呢?
1回复

内存使用对算法复杂性的影响

我正在阅读关于C ++ STL算法的Nicolai Josuttis一书。 对于许多算法,例如stable_sort(),他提到算法的复杂性n * log(n),如果有足够的内存可用,否则它是n * log(n)* log(n)。 我的问题是内存使用情况如何影响复杂性? STL如何检测这种