Computer >> Máy Tính >  >> Lập trình >> Ruby

Cách tạo số ngẫu nhiên có trọng số

Các số ngẫu nhiên thường tuân theo cái mà chúng tôi gọi là "phân phối đồng đều", nghĩa là có cùng cơ hội chọn bất kỳ số nào.

Nhưng nếu bạn muốn một số số được chọn thường xuyên hơn những số khác, bạn sẽ cần một chiến lược khác: trình tạo số ngẫu nhiên có trọng số .

Một số ứng dụng thực tế bao gồm:

  • bàn thắng trong trò chơi điện tử, nơi kẻ thù có thể thả các vật phẩm khác nhau với tỷ lệ rơi khác nhau.
  • một cuộc xổ số, trong đó những người có nhiều vé hơn sẽ có nhiều cơ hội giành chiến thắng hơn.

Chiến lược đơn giản

Nếu bạn nghĩ về ví dụ xổ số, bạn có thể đưa ra một giải pháp rõ ràng:tạo một mảng có một bản sao của mặt hàng cho mỗi "vé".

Ví dụ:nếu John mua 4 vé xổ số và David chỉ mua 1 vé, John sẽ có cơ hội thắng gấp 4 lần so với David.

Đây là cách triển khai đang hoạt động :

users  = { john: 4, david: 1 }
raffle = []

users.map do |name, tickets|
  tickets.times { raffle << name }
end

p raffle
# [:john, :john, :john, :john, :david]

p raffle.sample
# :john

Tôi sẽ thêm tên của người đó một lần cho mỗi vé họ đã mua và sau đó tôi chọn một tên ngẫu nhiên từ danh sách đó. Nhờ có tên trong danh sách nhiều lần hơn, cơ hội chọn tên đó sẽ tăng lên.

Tôi thích cách tiếp cận này vì nó rất đơn giản và khi bạn có danh sách của mình, rất nhanh để chọn người chiến thắng.

Tổng trọng lượng

Có một cách khác mà bạn có thể làm điều này tiết kiệm bộ nhớ hơn, sự cân bằng là việc chọn một giá trị ngẫu nhiên sẽ chậm hơn.

Ý tưởng là chọn một số ngẫu nhiên từ 1 đến tổng của tất cả các trọng số, sau đó lặp lại cho đến khi bạn tìm thấy trọng số nhỏ hơn hoặc bằng số này.

Đây là mã :

def random_weighted(weighted)
  max    = sum_of_weights(weighted)
  target = rand(1..max)

  weighted.each do |item, weight|
    return item if target <= weight
    target -= weight
  end
end

def sum_of_weights(weighted)
  weighted.inject(0) { |sum, (item, weight)| sum + weight }
end

Mã này sử dụng một hàm băm trong đó các khóa là các mục và các giá trị là trọng số. Bạn có thể gọi phương thức này như sau:

random_weighted(cats: 5, dogs: 1)
# :cats

Bạn có thể kiểm tra xem điều này có hoạt động như mong đợi hay không bằng cách xem phân phối kết quả sau khi chạy nó nhiều lần.

Đây là một ví dụ :

counts = Hash.new(0)

def pick_number
  random_weighted(cats: 2, dogs: 1)
end

1000.times { counts[pick_number] += 1 }
p counts

Chạy thao tác này một vài lần và xem kết quả đầu ra để xem liệu tỷ lệ có phải là tỷ lệ hay không.

Kết luận

Mặc dù có nhiều thuật toán phức tạp hơn, hai thuật toán này sẽ phục vụ bạn tốt. Tôi hy vọng bạn thấy bài viết này hữu ích, hãy chia sẻ nó với bạn bè của bạn để tôi có thể tiếp tục viết nhiều hơn nữa!