How do I generate a list of n unique random numbers in Ruby?

后端 未结 15 1731
鱼传尺愫
鱼传尺愫 2020-11-28 22:27

This is what I have so far:

myArray.map!{ rand(max) }

Obviously, however, sometimes the numbers in the list are not unique. How can I mak

相关标签:
15条回答
  • 2020-11-28 22:58
    [*1..99].sample(4) #=> [64, 99, 29, 49]
    

    According to Array#sample docs,

    The elements are chosen by using random and unique indices

    If you need SecureRandom (which uses computer noise instead of pseudorandom numbers):

    require 'securerandom'
    
    [*1..99].sample(4, random: SecureRandom) #=> [2, 75, 95, 37]
    
    0 讨论(0)
  • 2020-11-28 22:59

    As far as it is nice to know in advance the maxium value, you can do this way:

    class NoLoopRand
      def initialize(max)
        @deck = (0..max).to_a
      end
    
      def getrnd
        return @deck.delete_at(rand(@deck.length - 1))
      end
    end
    

    and you can obtain random data in this way:

    aRndNum = NoLoopRand.new(10)
    puts aRndNum.getrnd
    

    you'll obtain nil when all the values will be exausted from the deck.

    0 讨论(0)
  • 2020-11-28 23:00

    If you have a finite list of possible random numbers (i.e. 1 to 100), then Kent's solution is good.

    Otherwise there is no other good way to do it without looping. The problem is you MUST do a loop if you get a duplicate. My solution should be efficient and the looping should not be too much more than the size of your array (i.e. if you want 20 unique random numbers, it might take 25 iterations on average.) Though the number of iterations gets worse the more numbers you need and the smaller max is. Here is my above code modified to show how many iterations are needed for the given input:

    require 'set'
    
    def rand_n(n, max)
        randoms = Set.new
        i = 0
        loop do
            randoms << rand(max)
            break if randoms.size > n
            i += 1
        end
        puts "Took #{i} iterations for #{n} random numbers to a max of #{max}"
        return randoms.to_a
    end
    

    I could write this code to LOOK more like Array.map if you want :)

    0 讨论(0)
  • 2020-11-28 23:01

    Here is one solution:

    Suppose you want these random numbers to be between r_min and r_max. For each element in your list, generate a random number r, and make list[i]=list[i-1]+r. This would give you random numbers which are monotonically increasing, guaranteeing uniqueness provided that

    • r+list[i-1] does not over flow
    • r > 0

    For the first element, you would use r_min instead of list[i-1]. Once you are done, you can shuffle the list so the elements are not so obviously in order.

    The only problem with this method is when you go over r_max and still have more elements to generate. In this case, you can reset r_min and r_max to 2 adjacent element you have already computed, and simply repeat the process. This effectively runs the same algorithm over an interval where there are no numbers already used. You can keep doing this until you have the list populated.

    0 讨论(0)
  • 2020-11-28 23:02

    This uses Set:

    require 'set'
    
    def rand_n(n, max)
        randoms = Set.new
        loop do
            randoms << rand(max)
            return randoms.to_a if randoms.size >= n
        end
    end
    
    0 讨论(0)
  • 2020-11-28 23:02

    How about a play on this? Unique random numbers without needing to use Set or Hash.

    x = 0
    (1..100).map{|iter| x += rand(100)}.shuffle
    
    0 讨论(0)
提交回复
热议问题