SitePoint Sponsor |
|
User Tag List
Results 1 to 2 of 2
Thread: Weighting a list
-
Jun 19, 2009, 14:58 #1
- Join Date
- Jun 2009
- Posts
- 1
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Weighting a list
Hello, I'm new here, and new to Ruby and Rails. I've worked with Ruby some in the past, so I have a bit of prior knowledge, but Rails is all new to me. I just bought Simply Rails 2. My friends and I were trying to think of an application I could build. So we thought of a application that would pick where we eat every day. (Kind of a joke, but we'll use it.) So we started planning it out and how it would work and such. So our database will need 3 columns: Name, weight, and special. Let me explain these:
Name - Name of the restaurant
Weight - How much we like that restaurant, and thus we want the program to be more likely to pick restaurants weighted higher
Special - This is for if a restaurant has a special, or a deal on a certain day. This took me a bit of thinking, but here is how I decided to use this: Because it is possible for a restaurant to have more than one special per week, I needed something more than just mon, tue, wed, ect. So I've assigned each day a number:
Mon - 1
Tue - 2
Wed - 4
Thurs - 8
Fri - 16
Here is how this will work: If a restaurant has a special on Monday, the column will get a 1. If it has a special on Tues, it will get a 2. If it has a special on Mon and Tues, it will get a 3 (it will get a 3 because that is Monday and Tuesday added -> 1 + 2 = 3).
So the app needs to look at the day, and look at the special column, and see if the number means that it has a special today. If it does, then it needs to multiply the weight by 10.
Now the part that I can't figure out, is how to weight the restaurants. My friend suggested that I use an array, and have the restaurant put in the array the number of times that it is weighted (So if it had a weight of 5, it would be put in the array 5 times). This would work, but I figured that there must be a better way. This way just seems kinda hacky. But maybe I'm wrong and it is the exact way a expert programmer would do it.
All that being said, here's what I've done so far:
Set up the database using SQLite, by creating a model, and then migrating it. Then I created a Restaurant controller which will have an index method that will basically do everything I've said above. It will have a new method that will let us add restaurants to the list. And here we need some check boxes for the days of the weeks, which the program can use to figure out the special number.
If you've read all of this, I greatly thank you, and I look forward to hearing your suggestions.
-
Jun 21, 2009, 09:08 #2
- Join Date
- Jun 2009
- Posts
- 1
- Mentioned
- 0 Post(s)
- Tagged
- 0 Thread(s)
Your friend's solution with adding multiple copies into a list is not a bad one but you're also right in that it can be done a little more elequently.
Here's a method that will generate a random selection with a weighting per element. It basically sums all the weights, picks a random number from 1 to that sum and then scans through the elements until it finds one that matches the random 'point'
Code Ruby:def get_weighted_random(hash) total = hash.inject(0) {|total, item_and_weight| total += item_and_weight[1]} target = rand(total) hash.each do |item, weight| return item if target <= weight target -= weight end end
You can then setup your weighted hash like:
Code Ruby:weighted = { :one => 3, :two => 6, :three => 9 }
And call it:
Code Ruby:result = get_weighted_random(weighted)
It isn't very effective with small weights like, 1, 2 & 3 but works pretty well on 3, 6, 9
Quick test (using the above weighted hash):
Code Ruby:result = Hash.new{|h,k| h[k] = 0} (1..10000).each do result[get_weighted_random(weighted)] += 1 end #=> {:three=>5719, :one=>1574, :two=>2707}
The results with weightings of 1, 2 & 3 are:
Code Ruby:{:three=>6761, :one=>1619, :two=>1620} #See that the :one and :two where actually evenly weighted.
Bookmarks