2

I'm trying to figure out a random amount calculator but by rarity for example:

choices = [10,100,1000,10000]

10 being the most common, 100 more common, 1000 rare, and 10000 extremely rare

I've tried this

import random
def getAmmounts():
    choices = [10, 100, 1000, 10000]
    values = [random.choice(choices) for i in range(10)]
    return values

Which returns a good amount of values but they're not that random 10000 appears quite often when it should almost hardly ever appear when I called on it the data what I received was:

[1000, 10000, 100, 1000, 10000, 10, 1000, 10000, 100, 100]

Two 10000's are in there and hardly any 10 values when 10s and 100s should be most common then an occasional 1000 in the mix but hardly ever a 10000 value. Is there any way to set up a priority type function that does this? Some good example data of what this should return after all said in done would be:

[10,10,100,10,10,100,1000]

And the occasional 10000 but it should be extremely rare, any idea on how to set this up?

Ernesto
  • 295
  • 2
  • 13
user2925490
  • 512
  • 2
  • 5
  • 11
  • Exactly how common (i.e., with what probability) should each number be? – jwodder Nov 18 '13 at 19:59
  • @jwodder the probability of each is not an exact amount but `10s`should be the most common so out of data with 20 values `10` should appear about 6-10 times but never exact the 100 value probably 3-5 the thousand once or twice but hardly ever the occurrence of a 10000 value – user2925490 Nov 18 '13 at 20:01
  • @user2925490 What jwodder asks is actually a hint at the overarching solution. You want to assign probability to these values, where, say, `10` appears 80% of the time, `100` appears 10% of the time, `1000` appears 8% of the time, and `10000` appears 2% of the time. When assigning actual percentages to this, however, keep in mind how many times `getAmounts()` will run in real time, so that if a user can run `getAmounts()` 1000 times in a minute per your implementation of it, you will still have `10000` be a rare number (once or twice). This will require prob/stats math. – jwarner112 Nov 18 '13 at 20:01
  • 2
    Also a dup of [this](http://stackoverflow.com/questions/19871608/generating-weighted-random-numbers), [this](http://stackoverflow.com/questions/9259989/select-random-item-with-weight), [this](http://stackoverflow.com/questions/13047806/weighted-random-sample-in-python), [this](http://stackoverflow.com/questions/1056151/random-python-dictionary-key-weighted-by-values), [this](http://stackoverflow.com/questions/2073235/random-weighted-choice), and probably dozens of other questions. – abarnert Nov 18 '13 at 20:03
  • @user2925490 In addition to what I just said, you said you want `10` to appear `6 out of 20 times`. Readjusting this to be a statistics problem, notice that `6 of 20` can be expressed as a percentage (`(6/20)*100 = 30%!`) – jwarner112 Nov 18 '13 at 20:03

6 Answers6

4

Your code does not assign any probabilities. You might intend for 10 to be less rare than 10000 but Python isn't going to know that.

You can simulate probability using the random.random to generate a random number between 0 and 1 and returning the appropriate number depending on the number generated.

For example

import random

def make_number():
  val = random.random()
  if val < 0.1: #10%
    return 10000
  elif val < 0.3: # 20%
    return 1000
  elif val < 0.6: # 30%
    return 100
  else: # 40%
    return 10

values = [make_number() for i in range(10)]
print (values)
MxLDevs
  • 19,048
  • 36
  • 123
  • 194
  • This is good but be careful with the logic here when you implement it, OP. If you change the operator sign, you could run into problems because of the stacked `elif` structure. For instance, you can't flip the signs to `>` or you'll have problems. – jwarner112 Nov 18 '13 at 20:14
  • 1
    After writing it, it also looked pretty difficult to maintain. I'd use a hash to store probabilities which would be much easier to maintain. Or something clean like the answer in [this question](http://stackoverflow.com/questions/19871608/generating-weighted-random-numbers), since you could easily add or remove cases. – MxLDevs Nov 18 '13 at 20:17
  • @Keikoku Modified it a little bit and it worked great, and thanks to all the others that gave an answer all of them helped when I was modifying it to fit my needs. – user2925490 Nov 18 '13 at 21:35
2

The input list gets equal probability for each item. Feed it a list with the proportions you want.

choices = [10, 10, 10, 100]

would make 10 three times as likely as 100.

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Manually adjusting probability like this is a good way to start, but i gets messy when you have the implementation scale upwards even a little. In the long run it's more helpful to study up on probability/statistics (very basic versions) to understand how to have the computer generate at proportion for you. – jwarner112 Nov 18 '13 at 20:06
2

You can produce a random number

import random
x = random.random() % this produces a random number between 0 and 1

and you can use the randomly generated number to choose between 10, 100, 1000 and 10000:

if x < 0.5:        # x is between 0 and .50, so 50% chance
    choice = 10
else if x < 0.75:  # x is between .50 and .75, so 75-50 = 25% chance
    choice = 100
else if x < 0.9:   # x is between .75 and .90, so 90-75 = 15% chance
    choice = 1000
else:
    choice = 10000 # x is between .90 and 1, so 100-90 = 10% chance (5 times less likely than the first one)

this outputs 10 on 50% of the runs, 100 on 25% of the runs, 1000 on 15% of the runs and 10000 on 10% of the runs. You can customize the ranges to whatever distribution over [10,100,1000,10000] you'd like!

arturomp
  • 28,790
  • 10
  • 43
  • 72
  • Never thought about using a random decimal number to do it, I want to edit my answer now to use that, but I don't want to be stealing your idea ;) I like my use of a list rather than straight up assigning a value, since you would have to go in a change the code if you wanted to change the values, if you aren't using a list of values. – Ernesto Nov 18 '13 at 20:37
2

Why don't you generate a number and then assign based on a range:

x = randint(1,11)
if x == 10:
    #This is the rarest, occurring only 10% of the time
elif x < 10 and x >= 8:
    #9 or 8 aka 20%
elif x < 8 and x >= 5:
    #7, 6, 5 aka 30%

You can tweak it how ever you like or make the range longer to get more specific chances.

Tyler
  • 17,669
  • 10
  • 51
  • 89
  • 3
    This solution is a stage more elegant than Jud's or triplee's, because where they have you manually add entries to desired probability, this uses a range to do it for you. It's not quite as elegant as a direct implementation, but I'd choose this over anything else I've seen. – jwarner112 Nov 18 '13 at 20:08
  • If I understand correctly, the first part of each elif line is superfluous, as the elif is not looked at if the if/elif above it is true. So `if x == 10, elif x >= 8, elif x >= 5, else` Am I correct? – Ernesto Nov 18 '13 at 20:33
0

A quick (though not necessarily the best) solution would be to stick multiple copies of the numbers you wish to appear more often in the list:

import random
def getAmmounts():
    choices = [10] * 80 + [100] * 15 + [1000] * 4 + [10000] * 1
    values = [random.choice(choices) for i in range(10)]
    return values
Jud
  • 1,158
  • 1
  • 8
  • 17
  • 1
    This is technically a solution, but a *really* messy one that can't really scale to any application. It's a lot more useful to put the time into learning a statistics-based answer. – jwarner112 Nov 18 '13 at 20:05
  • 1
    Hence my "quick but not the best". I don't know how flexible a solution the OP needs. – Jud Nov 18 '13 at 20:07
  • Perfectly true, I'm reviewing them all to see how close to a direct implementation we can get OP to use; the more direct, the (harder) better it will be, probably. – jwarner112 Nov 18 '13 at 20:09
0
import random

def getAmounts():
  amount = random.random(1,10)
  if amount == 10:
    return 10000
  elif amount > 8:
    return 1000
  elif amount > 5:
    return 100
  else:
    return 10

Or, if you want to use a list, you could do something like (I do not have IDLE open, and it takes a long time to load, so if I'm off a little, I apologize.)

import random

def getAmounts():
    values = [10, 100, 1000, 10000]
    amount = random.random(1,10)
    if amount == 10:
        return values[3]
    elif amount > 8:
        return values[2]
    elif amount > 5:
    return values [1]
  else:
    return values[0]

Using lists, you could always change the actual values you return, so if instead of 10, 100, 1000, 10000, you want to return 50, 500, 5000, and 50000 you could just reassign the values list, and the code still works, without having to go through and change every line.

Ernesto
  • 295
  • 2
  • 13