开发者

Random item by weight [closed]

开发者 https://www.devze.com 2023-02-13 23:06 出处:网络
Closed. This question needs to be more focused. It is not currently accepting answers. Want to improve this question? Update the question so it focuses on one problem only by editing this
Closed. This question needs to be more focused. It is not currently accepting answers.

Want to improve this question? Update the question so it focuses on one problem only by editing this post.

Closed 3 years ago.

Improve this question

For example I have this data:

headings = { 
         :heading1 => { :weight => 60, :show_count => 0}
         :heading2 => { :weight => 10, :show_count => 0}
         :heading3 => { :weight => 20, :show_count => 0}
         :heading4 => { :weight => 10, :show_count => 0}
       }
t开发者_如何学Pythonotal_views = 0

Now I want to serve each heading based on their weightages. For instance, for first 10 requests/iterations, heading1, heading3, heading2 and heading4 would be served 6, 2, 1, and 1 times respectively in order (by weight).

For every iteration show_count of served heading will increment by one and total_views will also increment globally.

Could you please suggest an algorithm or some ruby code to handle this.


You can use pickup gem

It accepts hash like this:

require 'pickup'
headings = {
  heading1: 60,
  heading2: 10,
  heading3: 20,
  heading4: 10
}
pickup = Pickup.new(headings)
pickup.pick
#=> heading1
pickup.pick
#=> heading1
pickup.pick
#=> heading3
pickup.pick
#=> heading1
pickup.pick
#=> heading4

So you can do something like this:

require 'pickup'

headings = { 
  heading1: { :weight => 60, :show_count => 0},
  heading2: { :weight => 10, :show_count => 0},
  heading3: { :weight => 20, :show_count => 0},
  heading4: { :weight => 10, :show_count => 0}
}

pickup_headings = headings.inject({}){ |h, (k,v)| h[k] = v[:weight]; h}

pickup = Pickup.new(pickup_headings)

# let's fire it 1000 times
1000.times do
  server = pickup.pick
  headings[server][:show_count] += 1
end

puts headings
#=> {
#=>   :heading1=>{:weight=>60, :show_count=>601},
#=>   :heading2=>{:weight=>10, :show_count=>116},
#=>   :heading3=>{:weight=>20, :show_count=>176},
#=>   :heading4=>{:weight=>10, :show_count=>107}
#=> }


This should work for your basic case and can be modified according to the details of what you need:

 class Heading
   attr_reader :heading, :weight, :show_count

   def initialize(heading,weight=1)
     @heading=heading
     @weight=weight
     @show_count=0
   end

   def serve
     puts "Served #{@heading}! "  
     @show_count += 1
   end
 end

 class HeadingServer
   attr_reader :headings

   def initialize(headings_hash)
     @headings=headings_hash.map {|h, data| Heading.new(h,data[:weight])}
     @total_weight=@headings.inject(0) {|s,h| s+= h.weight}
   end

   def serve(num_to_serve=@total_weight)
     @headings.sort {|a,b| b.weight <=> a.weight}.each do |h| 
       n = (h.weight * num_to_serve) / @total_weight  #possibility of rounding errors
       n.times { h.serve }
     end
   end

   def total_views
     @headings.inject(0) {|s,h| s += h.show_count}
   end
 end

headings = { 
  :heading1 => { :weight => 60, :show_count => 0},
  :heading2 => { :weight => 10, :show_count => 0},
  :heading3 => { :weight => 20, :show_count => 0},
  :heading4 => { :weight => 10, :show_count => 0}
}

# Example Usage:

hs = HeadingServer.new(headings)

hs.serve(10)  

hs.headings.each {|h| puts "#{h.heading} : served #{h.show_count} times"}

puts "Total Views: #{hs.total_views}" 
0

精彩评论

暂无评论...
验证码 换一张
取 消