to generate random permutation of a array in O(n) time and O(1) space

余生长醉 提交于 2019-12-01 09:08:19

问题


We have to generate Array {1,2,3,..,n} in O(1) space.
I am able to do it in O(n) space.

I did O(n) space solution by first storing the array and then randomizing it in place. But how to do it without storing array in O(1) space.

I m just generating random number and instead of storing them I need to print them as storing would require O(n) space but I need to do it in O(1) space and what my doubt is if we go on generating random number and print them there may be some numbers between 1 to n which may be generated more than once and some may not be generated. So How do I manage to print all numbers exactly once in O(1) space?

P.S.- I am not given any array. Input is just 'n' and I have to print the permutation of array {1,2,3,...,n} in O(n) time and in O(1) space.


回答1:


I've built a linear-feedback-shift-register generator solution which I think meets your requirements. The implementation is based on Fibonacci LFSRs, so it achieves full cycle for the given number of bits. I went ahead and put in the polynomial coefficients for up to 19 bits and select the appropriate coefficient set based on the specified value of N. Generated values greater than N get chucked overboard, but the total number of values in the full cycle is less than 2N so it will produce your N values in O(N) time. The LFSR preserves one word of state, so it's O(1) space.

Here is the implementation in Ruby:

#!/usr/bin/env ruby -w

# Linear Feedback Shift Register generator which operates on smallest bit
# range possible for a specified integer range, and skips over values outside
# the specified range. Since this attains full cycle length for the specified
# number of bits, and the number of bits is minimized relative to the specified
# N, the total number of iterations is bounded by 2N and is therefore O(N).
class LFSR
  # Polynomials for maximal LFSRs determine shift amounts for up to 19 bits.
  # See https://en.wikipedia.org/wiki/Linear_feedback_shift_register for
  # details. Add more entries if you need more than 19 bits.
  SHIFT_AMT = [
    [], [], [1], [1], [1], [2], [1], [1], [2, 3, 4], [4], [3], [2],
    [1, 2, 8], [1, 2, 5], [1, 2, 12], [1], [2, 3, 5], [3], [7], [1, 2, 5]
  ]

  # Constructor for the LFSR.  Specify the N and seed value.
  def initialize(n, seed)
    @n = n
    @state =  (seed % n) + 1
    @num_bits = Math.log2(n).floor + 1
  end

  # Generate the next iterate of the LFSR.  If it's above the specified N,
  # keep trying until you're done.
  def next_value
    loop do
      bit = @state
      SHIFT_AMT[@num_bits].each { |amt| bit ^= (@state >> amt) }
      @state = ((@state >> 1) | ((bit & 1) << (@num_bits - 1)))
      return @state if @state <= @n
    end
  end
end

N = (ARGV.shift || 100).to_i # Specify the 'N' value on cmd line. Default = 100
SEED = (ARGV.shift || 0x7FFF).to_i # Optionally specify a "seed" for the LFSR
my_lfsr = LFSR.new(N, SEED)  # Instantiate an LFSR object
N.times { p my_lfsr.next_value }   # Invoke it N times, print the results



回答2:


If n is a power of 2, a block cipher with a block size of n bits could be used to generate a permutation on n elements - just write out Encrypt(0), Encrypt(1)... Encrypt(n-1).

If n is not a power of 2, let m be the first power of 2 above n. Encrypt 0..n-1, and if the result is >= n, Encrypt it again, until you get a value within range. This amounts to writing out the permutation on m elements as a cycle, and then deleting the elements >= n.

If you don't have a standard block cipher of the required size lying around, you could use the Luby-Rackoff aka Feistel construction to create one using a hash function as the F operation in https://en.wikipedia.org/wiki/Feistel_cipher. A peculiarity of Feistel networks where F() produces more than one bit, treated as permutations, is that they never produce odd permutations: if the Feistel output is k bits wide each round produces some multiple of 2^(k-1) 2-cycles, which produces an even permutation for k > 1, so you may want to think about this a bit and/or use multiple Feistel rounds with different styles of feedback to get reasonably random permutations out of this. A suitably elaborate system of Fiestel rounds with 1 bit of Feistel output can be viewed as an implicit construction of an exchange network as might be used to implement arbitrary permutations in networks.




回答3:


Strictly speaking an O(1) solution is impossible, because a number n itself takes log(n) bits to store.

On the other hand, if the goal of exercise is to avoid an array of n integers, and you are willing to sacrifice some rigor - namely assume that n! can be represented in O(1) memory - the solution is to generate a random number k in range [0, n!), and compute the k-th permutation, printing numbers as you calculate them.




回答4:


In general, this is an impossible problem. Although it is possible to reorganize a list without any memory beyond the list and be left with a statistically random list, a true random list is impossible.

If the problem is interpreted as a number stream filter, at any point in time we can see only one element of the stream. It helps that we can see the part of the stream that has yet to be processed, but if we cannot alter that portion, we are stuck.

The two basic ways to perfectly mix a list (given a perfect random number generator):

for each element in the list:
  select a random element later in the list.
  swap.

for each element in the list:
  select a random element earlier in the list.
  swap.

A truly random resort of a list will be reducible to one of these two methods. Unfortunately, both of these methods cannot work with a one pass filter that has no random write permission. The first one needs to modify part of the list that has not been created yet. The second one needs to modify part of the list already printed out.



来源:https://stackoverflow.com/questions/32182120/to-generate-random-permutation-of-a-array-in-on-time-and-o1-space

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!