Random sequence iteration in O(1) memory?

前端 未结 6 1476
[愿得一人]
[愿得一人] 2021-02-19 07:58

Say you want to iterate over a sequence [0 to n] in a random order, visiting every element exactly once. Is there any way to do this in O(1) memory, i.e. without creati

6条回答
  •  走了就别回头了
    2021-02-19 08:20

    As with most algorithmic problems, there is a time-space trade-off; this can be solved in O(1) space if you're happy to use O(n^2) time to generate all the permutations. Aside from a couple of temporary variables, the only storage this requires is the random number seed itself (or, in this case, the PRNG object), since that is sufficient to regenerate the sequence of pseudo-random numbers.

    Note that you have to give this function the same PRNG on every call, and you can't use it for any other purpose.

    #include 
    
    template
    INT random_permutation_element(INT k, INT n, PRNG prng) {
      typedef std::uniform_int_distribution 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;
    }
    

    Here's a quick and dirty harness. ./test 1000 3 generates 1000 complete permutations of length three; ./test 10 1000000 0 5 generates the first five elements of each of 10 permutations of length one million.

    #include 
    
    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;
    }
    

    There is a faster way to do this if you're unlikely to finish any given permutation, but this way of writing it looks nicer, and is maybe easier to understand. (The other way is to generate the numbers in the opposite order, which means you can stop after you've generated k of them but you have to do it twice, first to get result and then to adjust it.)

提交回复
热议问题