Re: Probability Problem

2006-04-25 Thread Elliot Temple
I think I got it. I noticed my code is essentially the same as Tim  
Peter's (plus the part of the problem he skipped). I read his code 20  
minutes before recreating mine from Alex's hints. Thanks!

def main():
 ways = ways_to_roll()
 total_ways = float(101**10)
 running_total = 0
 for i in range(1000-390+1):
 j = i + 390
 running_total += ways[i] * ways[j]
 print running_total / total_ways**2
 print ways[:10]

def ways_to_roll():
 result = [1]
 for i in xrange(10):
 result = combine([1] * 101, result)
 return result

def combine(a, b):
 results = [0] * (len(a) + len(b) - 1)
 for i, ele in enumerate(a):
 for j, ele2 in enumerate(b):
 results[i+j] += ele * ele2
 return results

main()
# output: 3.21962542309e-05 and
# [1, 10, 55, 220, 715, 2002, 5005, 11440, 24310, 48620]
# 3.21962542309e-05 is 32 out of a million



On Apr 24, 2006, at 9:14 PM, Alex Martelli wrote:

 Elliot Temple [EMAIL PROTECTED] wrote:

 On Apr 24, 2006, at 8:24 PM, Alex Martelli wrote:

 Lawrence D'Oliveiro [EMAIL PROTECTED] wrote:

 In article [EMAIL PROTECTED],
  Elliot Temple [EMAIL PROTECTED] wrote:

 Problem: Randomly generate 10 integers from 0-100 inclusive,  
 and sum
 them. Do that twice. What is the probability the two sums are 390
 apart?

 I think the sum would come close to a normal distribution.

 Yes, very close indeed, by the law of large numbers.

 However, very close (in a math course at least) doesn't get the  
 cigar.

 You can compute the requested answer exactly with no random number
 generation whatsoever: compute the probability of each result from
 0 to
 1000, then sum the probabilities of entries that are exactly 390
 apart.

 That was the plan, but how do I get the probability of any given
 result? (in a reasonable amount of time)

 BTW I'm not in a math course, just curious.

 OK, I'll trust that last assertion (sorry for the hesitation, but it's
 all too easy to ``help'' somebody with a homework assignment and
 actually end up damaging them by doing it FOR them!-).


 I'm still going to present this in a way that stimulates thought,  
 rather
 than a solved problem -- humor me...!-)


 You're generating a uniformly distributed random number in 0..100 (101
 possibilities), 10 times, and summing the 10 results.

 How do you get a result of 0?  Only 1 way: 0 at each attempt --
 probability 1 (out of 1010 possibilities).

 How do you get a result of 1?  10 ways: 1 at one attempt and 0 at each
 of the others - probability 10 (again in 1010'ths;-).

 How do you get a result of 2?  10 ways for '2 at one attempt and 0 at
 each of the others', plus, 10*9/2 ways for '1 at two attempts and 0 at
 each of the others' -- probability 55 (ditto).

 ...and so forth, but you'd rather not work it out...


 So, suppose you start with a matrix of 101 x 10 entries, each of  
 value 1
 since all results are equiprobable (or, 1/1010.0 if you prefer;-).

 You want to compute the number in which you can combine two rows.  How
 could you combine the first two rows (each of 101 1's) to make a  
 row of
 201 numbers corresponding to the probabilities of the sum of two  
 throws?

 Suppose you combine the first entry of the first row with each  
 entry of
 the second, then the second entry of the first row with each entry of
 the second, etc; each time, you get a sum (of two rolls) which  
 gives you
 an index of a entry (in an accumulator row starting at all zeros) to
 increment by the product of the entries you're considering...


 Can you generalize that?  Or, do you need more hints?  Just ask!


 Alex
 -- 
 http://mail.python.org/mailman/listinfo/python-list


-- Elliot Temple
http://www.curi.us/blog/



-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Probability Problem

2006-04-25 Thread Peter Tillotson
I had a possibly similar problem calculating probs related to premium
bond permutation. With 10^12 memory ran out v quickly. In the end I got
round it by writing a recursive function and quantising the probability
density function.

Elliot Temple wrote:
 Problem: Randomly generate 10 integers from 0-100 inclusive, and sum
 them. Do that twice. What is the probability the two sums are 390 apart?
 
 I have code to do part of it (below), and I know how to write code to do
 the rest. The part I have calculates the number of ways the dice can
 come out to a given number. The problem is the main loop has 9
 iterations and it takes about 2.5 minutes to begin the 4th one, and each
 iteration is about 101 times longer than the previous one. So:
 
 x = 2.5 * 101**6
 x /= (60*24*365.25)
 x
 5045631.5622908585
 
 It'd take 5,000 millennia. (If my computer didn't run out of memory
 after about 4 minutes, that is.)
 
 Any suggestions? Either a way to do the same thing much more efficiently
 (enough that I can run it) or a different way to solve the problem.
 
 Code:
 
 li = range(101)
 li2 = []
 range101 = range(101)
 for x in xrange(9):
 print x is %s % x
 li2 = []
 for y in li:
 for z in range101:
 li2 += [y+z]
 li = li2
 print li.count(800)
 # prints how many ways the dice can add to 800
 
 
 This link may help:
 http://www.math.csusb.edu/faculty/stanton/m262/intro_prob_models/calcprob.html
 
 
 -- Elliot Temple
 http://www.curi.us/blog/
 
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Probability Problem

2006-04-25 Thread Tim Peters
[Elliot Temple]
 I think I got it. I noticed my code is essentially the same as Tim
 Peter's (plus the part of the problem he skipped). I read his code 20
 minutes before recreating mine from Alex's hints. Thanks!

 def main():
  ways = ways_to_roll()
  total_ways = float(101**10)
  running_total = 0
  for i in range(1000-390+1):
  j = i + 390
  running_total += ways[i] * ways[j]
  print running_total / total_ways**2
  print ways[:10]

 def ways_to_roll():
  result = [1]
  for i in xrange(10):
  result = combine([1] * 101, result)
  return result

 def combine(a, b):
  results = [0] * (len(a) + len(b) - 1)
  for i, ele in enumerate(a):
  for j, ele2 in enumerate(b):
  results[i+j] += ele * ele2
  return results

 main()
 # output: 3.21962542309e-05 and
 # [1, 10, 55, 220, 715, 2002, 5005, 11440, 24310, 48620]
 # 3.21962542309e-05 is 32 out of a million

You should sanity-check the computation by generalizing it, then
applying it to a case so small you can easily work out the result via
exhaustive enumeration by hand.

For example, suppose you took integers from the much smaller set {0,
1}, and did that only twice.  Then the possible sums and their
probabilities are clearly:

   0  1/4
   1  1/2
   2  1/4

If you did this twice, what's the probability that the sums differ by
1?  Suitably generalized, your program above would compute 1/4.  Is
that actually right?  It depends on what exactly the sums differ by
1 means.  If it means the second sum is one larger than the first
sum, 1/4 is correct.  Ditto if it means the second sum is one smaller
than the first sum.  But if it means 1 is the absolute value of the
difference of the sums, the right answer is 1/2.  I'm not sure which
meaning you have in mind, but the last one was my guess.
-- 
http://mail.python.org/mailman/listinfo/python-list


Probability Problem

2006-04-24 Thread Elliot Temple
Problem: Randomly generate 10 integers from 0-100 inclusive, and sum  
them. Do that twice. What is the probability the two sums are 390 apart?

I have code to do part of it (below), and I know how to write code to  
do the rest. The part I have calculates the number of ways the dice  
can come out to a given number. The problem is the main loop has 9  
iterations and it takes about 2.5 minutes to begin the 4th one, and  
each iteration is about 101 times longer than the previous one. So:

  x = 2.5 * 101**6
  x /= (60*24*365.25)
  x
5045631.5622908585

It'd take 5,000 millennia. (If my computer didn't run out of memory  
after about 4 minutes, that is.)

Any suggestions? Either a way to do the same thing much more  
efficiently (enough that I can run it) or a different way to solve  
the problem.

Code:

li = range(101)
li2 = []
range101 = range(101)
for x in xrange(9):
 print x is %s % x
 li2 = []
 for y in li:
 for z in range101:
 li2 += [y+z]
 li = li2
print li.count(800)
# prints how many ways the dice can add to 800


This link may help:
http://www.math.csusb.edu/faculty/stanton/m262/intro_prob_models/ 
calcprob.html

-- Elliot Temple
http://www.curi.us/blog/

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Probability Problem

2006-04-24 Thread Lawrence D'Oliveiro
In article [EMAIL PROTECTED],
 Elliot Temple [EMAIL PROTECTED] wrote:

Problem: Randomly generate 10 integers from 0-100 inclusive, and sum  
them. Do that twice. What is the probability the two sums are 390 apart?

I think the sum would come close to a normal distribution.
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Probability Problem

2006-04-24 Thread Alex Martelli
Lawrence D'Oliveiro [EMAIL PROTECTED] wrote:

 In article [EMAIL PROTECTED],
  Elliot Temple [EMAIL PROTECTED] wrote:
 
 Problem: Randomly generate 10 integers from 0-100 inclusive, and sum
 them. Do that twice. What is the probability the two sums are 390 apart?
 
 I think the sum would come close to a normal distribution.

Yes, very close indeed, by the law of large numbers.

However, very close (in a math course at least) doesn't get the cigar.

You can compute the requested answer exactly with no random number
generation whatsoever: compute the probability of each result from 0 to
1000, then sum the probabilities of entries that are exactly 390 apart.


Alex
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Probability Problem

2006-04-24 Thread Elliot Temple

On Apr 24, 2006, at 8:24 PM, Alex Martelli wrote:

 Lawrence D'Oliveiro [EMAIL PROTECTED] wrote:

 In article [EMAIL PROTECTED],
  Elliot Temple [EMAIL PROTECTED] wrote:

 Problem: Randomly generate 10 integers from 0-100 inclusive, and sum
 them. Do that twice. What is the probability the two sums are 390  
 apart?

 I think the sum would come close to a normal distribution.

 Yes, very close indeed, by the law of large numbers.

 However, very close (in a math course at least) doesn't get the cigar.

 You can compute the requested answer exactly with no random number
 generation whatsoever: compute the probability of each result from  
 0 to
 1000, then sum the probabilities of entries that are exactly 390  
 apart.

That was the plan, but how do I get the probability of any given  
result? (in a reasonable amount of time)

BTW I'm not in a math course, just curious.

-- Elliot Temple
http://www.curi.us/blog/



-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Probability Problem

2006-04-24 Thread Alex Martelli
Elliot Temple [EMAIL PROTECTED] wrote:

 On Apr 24, 2006, at 8:24 PM, Alex Martelli wrote:
 
  Lawrence D'Oliveiro [EMAIL PROTECTED] wrote:
 
  In article [EMAIL PROTECTED],
   Elliot Temple [EMAIL PROTECTED] wrote:
 
  Problem: Randomly generate 10 integers from 0-100 inclusive, and sum
  them. Do that twice. What is the probability the two sums are 390
  apart?
 
  I think the sum would come close to a normal distribution.
 
  Yes, very close indeed, by the law of large numbers.
 
  However, very close (in a math course at least) doesn't get the cigar.
 
  You can compute the requested answer exactly with no random number
  generation whatsoever: compute the probability of each result from
  0 to
  1000, then sum the probabilities of entries that are exactly 390  
  apart.
 
 That was the plan, but how do I get the probability of any given  
 result? (in a reasonable amount of time)
 
 BTW I'm not in a math course, just curious.

OK, I'll trust that last assertion (sorry for the hesitation, but it's
all too easy to ``help'' somebody with a homework assignment and
actually end up damaging them by doing it FOR them!-).


I'm still going to present this in a way that stimulates thought, rather
than a solved problem -- humor me...!-)


You're generating a uniformly distributed random number in 0..100 (101
possibilities), 10 times, and summing the 10 results.

How do you get a result of 0?  Only 1 way: 0 at each attempt --
probability 1 (out of 1010 possibilities).

How do you get a result of 1?  10 ways: 1 at one attempt and 0 at each
of the others - probability 10 (again in 1010'ths;-).

How do you get a result of 2?  10 ways for '2 at one attempt and 0 at
each of the others', plus, 10*9/2 ways for '1 at two attempts and 0 at
each of the others' -- probability 55 (ditto).

...and so forth, but you'd rather not work it out...


So, suppose you start with a matrix of 101 x 10 entries, each of value 1
since all results are equiprobable (or, 1/1010.0 if you prefer;-).

You want to compute the number in which you can combine two rows.  How
could you combine the first two rows (each of 101 1's) to make a row of
201 numbers corresponding to the probabilities of the sum of two throws?

Suppose you combine the first entry of the first row with each entry of
the second, then the second entry of the first row with each entry of
the second, etc; each time, you get a sum (of two rolls) which gives you
an index of a entry (in an accumulator row starting at all zeros) to
increment by the product of the entries you're considering...


Can you generalize that?  Or, do you need more hints?  Just ask!


Alex
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Probability Problem

2006-04-24 Thread Tim Peters
[Alex Martelli]
 ...
 You can compute the requested answer exactly with no random number
 generation whatsoever: compute the probability of each result from
 0 to 1000, then sum the probabilities of entries that are exactly 390
 apart.

[Elliot Temple]
 That was the plan, but how do I get the probability of any given
 result?

As the link you gave suggests, the number of ways to get a given sum s
is the coefficient of x**s in (1 + x + x**2 + ... +x**100)**10. 
Divide that by 101**10 to compute the probability of getting sum s.

 (in a reasonable amount of time)

 BTW I'm not in a math course, just curious.

Computing exact results is probably too hard for just curious.  This
little Python program computes the exact number of ways to get each
sum in 0 .. 1000 quickly (fraction of a second on my box):


def mul(p1, p2):
result = [0] * (len(p1) + len(p2) - 1)
for i, x1 in enumerate(p1):
for j, x2 in enumerate(p2):
result[i+j] += x1 * x2
while result and result[-1] == 0:
del result[-1]
return result

base = [1] * 101
result = [1]
for dummy in range(10):
result = mul(result, base)

assert len(result) == 1001
assert sum(result) == 101**10


Then result[s] is the exact number of ways to get sum s, for each s in
range(1001).

Figuring out why that works is the probably too hard for 'just
curious' part.  If you want to pursue that, it's a straightforward
implementation of fully multiplying out:

(1 + x + x**2 + ... +x**100)**10

A polynomial here is represented by a list L of coefficients, where
L[i] is the coefficient of x**i.  `mul()` implements polynomial
multiplication using this format.  For example,

 mul([1, 1], [-1, 1])
[-1, 0, 1]

or, IOW, (x+1)*(x-1) = (x**2 - 1).
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-06 Thread Antoon Pardon
On 2006-04-05, Tomi Lindberg [EMAIL PROTECTED] wrote:
 Antoon Pardon wrote:

   def __rmul__(self, num):
 tp = num * [self]
 return reduce(operator.add, tp)
 
 sum3d6 = 3 * D(6)

 One basic question: is there any particular reason not to 
 use __mul__ instead (that would allow me to use both 3 * 
 D(6) and D(6) * 3, while __rmul__ raises an AttributeError 
 with the latter)?

Well 3 * D(6) is similar to the notation used in roleplaying,
while D(6) * 3 would make me think of the distribution
{3:1, 6:1, 9:1, 12:1, 15:1, 18:}

 Difference between the two methods is 
 slightly unclear to me.

I have to look it up myself regularly. But in this case
it was more a matter of some intuition that 3 * D(6)
was not the same as D(6) * 3. You may have a different
intuition.

-- 
Antoon Pardon
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-05 Thread Antoon Pardon
Op 2006-04-04, Tomi Lindberg schreef [EMAIL PROTECTED]:
 First, thanks to Antoon and Alexander for replying.

 Antoon Pardon wrote:

 It would be better to construct distributions for one
 die and make a function that can 'add' two distributions
 together.

 As both replies pointed to this direction, I tried to take 
 that route. Here's the unpolished code I came up with. Does 
 it look even remotely sane way to accomplish my goal?

 -- code begins --

 # A die with n faces
 D = lambda n: [x+1 for x in range(n)]

 # A new die with 6 faces
 d6 = D(6)

 # Adds another die to results.
 def add_dice(sums, die):
  # If first die, all values appear once
  if not sums:
  for face in die:
  sums[face] = 1
  # Calculating the number of appearances for additional
  # dice
  else:
  new_sums = {}
  for k in sums.keys():
  for f in die:
  if new_sums.has_key(k+f):
  new_sums[k+f] += sums[k]
  else:
  new_sums[k+f] = sums[k]
  sums = new_sums
  return sums

 sums = add_dice({}, d6)
 sums = add_dice(sums, d6)
 sums = add_dice(sums, d6)

 -- code ends --

IMO you are making things too complicated and not general
enough. Here is my proposal.

- 

import operator

class Distribution(dict):

  '''A distribution is a dictionary where the keys are dice
 totals and the values are the number of possible ways
 this total can come up '''

  def __add__(self, term):
   
'''Take two distributions and combine them into one.'''

result = Distribution()
for k1, v1 in self.iteritems():
  for k2, v2 in term.iteritems():
k3 = k1 + k2
v3 = v1 * v2
try:
  result[k3] += v3
except KeyError:
  result[k3] = v3
return result

  def __rmul__(self, num):
tp = num * [self]
return reduce(operator.add, tp)

def D(n):

  ''' One die has a distribution where each result has
  one possible way of coming up '''
  return Distribution((i,1) for i in xrange(1,n+1))


sum3d6 = 3 * D(6)
sum2d6p2d4 = 2 * D(6) + 2 * D(4)

-

-- 
Antoon Pardon
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-05 Thread Tomi Lindberg
Antoon Pardon wrote:

 IMO you are making things too complicated and not general
 enough.

I believe that the above is very likely more than just your 
opinion :) Programming is just an occasional hobby to me, 
and I lack both experience and deeper (possibly a good chunk 
of shallow as well) knowledge on the subject. I'll study the 
code you posted, and make further questions if something 
remains unclear afterwards.

Thanks,
Tomi Lindberg
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-05 Thread Alexander Schmolck
Tomi Lindberg [EMAIL PROTECTED] writes:

 # Adds another die to results.
 def add_dice(sums, die):
  # If first die, all values appear once

I'd add something like

   sums = sums or {}

because otherwise your function will sometimes mutate sums and sometimes
return a fresh object, which usually is a very bad thing as it can easily lead
to quite nasty bugs.

  if not sums:
  for face in die:
  sums[face] = 1
  # Calculating the number of appearances for additional
  # dice
  else:
  new_sums = {}
  for k in sums.keys():
  for f in die:
  if new_sums.has_key(k+f):
  new_sums[k+f] += sums[k]
  else:
  new_sums[k+f] = sums[k]
  sums = new_sums
  return sums

'as
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-05 Thread Tomi Lindberg
Antoon Pardon wrote:

   def __rmul__(self, num):
 tp = num * [self]
 return reduce(operator.add, tp)
 
 sum3d6 = 3 * D(6)

One basic question: is there any particular reason not to 
use __mul__ instead (that would allow me to use both 3 * 
D(6) and D(6) * 3, while __rmul__ raises an AttributeError 
with the latter)? Difference between the two methods is 
slightly unclear to me.

Thanks,
Tomi Lindberg
-- 
http://mail.python.org/mailman/listinfo/python-list


Dice probability problem

2006-04-04 Thread Tomi Lindberg
Hi,

I'm trying to find a way to calculate a distribution of 
outcomes with any combination of dice. I have the basics 
done, but I'm a bit unsure how to continue. My main concern 
is how to make this accept any number of dice, without 
having to write a new list comprehension for each case?

Here's a piece of code that shows the way I'm doing things 
at the moment.

-- code begins --

# A die with n faces
D = lambda n: [x+1 for x in range(n)]

# A pool of 3 dice with 6 faces each
pool = [D(6)] * 3

# A List of all outcomes with the current 3d6 pool.
results = [x+y+z for x in pool[0] for y in pool[1]
for z in pool[2]]

# A dictionary to hold the distribution
distribution = {}

# If outcome is already a key, adds 1 to its value.
# Otherwise adds outcome to keys and sets its value
# to 1.
def count(x):
 if distribution.has_key(x): distribution[x] += 1
 else: distribution[x] = 1

# Maps the results with above count function.
map(count, results)

-- code ends --

Thanks,
Tomi Lindberg
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-04 Thread Alexander Schmolck
Tomi Lindberg [EMAIL PROTECTED] writes:

 I'm trying to find a way to calculate a distribution of outcomes with any
 combination of dice. I have the basics done, but I'm a bit unsure how to
 continue. My main concern is how to make this accept any number of dice,
 without having to write a new list comprehension for each case?

You need to think about the right way to break the problem down into some
operation that can be repeated one fewer times than there are dice (if you
just have a single dice, nothing needs to be done) and then repeat it.

An obvious candidate is adding a single dice to the sums computed so far:

def addDice(sums, dice):
return [x+y for x in dice for y in sums]

If you have less than 1 dice the answer is 
# len(pool) == 1
pool[0]

After that, each time you add a dice you need to call addDice on the sum
computed for all the previous dice and the new dice:

# len(pool) == 2
addDice(resultFor1, pool[1])
addDice(pool[0], pool[1])

then

# len(pool) == 3
addDice(resultFor2, pool[2])
addDice(addDice(resultFor1, pool[1]), pool[2])
addDice(addDice(pool[0], pool[1]), pool[2])

finally you get

# len(pool) == n
addDice(addDice(addDice(..., pool[n-3]), pool[n-2]) pool[n-1])

OK, so how do we get the repetition?

Conveniently the pattern f(...f(f(x[0],x[1]),x[2])...,x[n-1]) or equivalently,
if we write the infix operator * for f: x[0]*x[1]*...*x[n-1], can just be 
written as
reduce(f, x) in python. So we get:

reduce(addDice, pool)
== reduce(lambda sums, dice: [x+y for x in dice for y in sums], pool)

You should presumably also try writing this out as a single function, without
using reduce (but recognizing the (left) reduction pattern is useful, even if
you don't use python's reduce).

'as
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-04 Thread Alexander Schmolck
Alexander Schmolck [EMAIL PROTECTED] writes:

 addDice(resultFor1, pool[1])
 addDice(pool[0], pool[1])


sorry should have spelled out that successive lines are meant to be
equivalent, i.e.

 addDice(resultFor1, pool[1])
==   addDice(pool[0], pool[1])

'as
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-04 Thread Antoon Pardon
Op 2006-04-04, Tomi Lindberg schreef [EMAIL PROTECTED]:
 Hi,

 I'm trying to find a way to calculate a distribution of 
 outcomes with any combination of dice. I have the basics 
 done, but I'm a bit unsure how to continue. My main concern 
 is how to make this accept any number of dice, without 
 having to write a new list comprehension for each case?

IMO you are looking at it from the wrong side.

It would be better to construct distributions for one
die and make a function that can 'add' two distributions
together. So for 3D6 you first add the distribution of
a D6 to the distribution of a D6 and to this result
you add the distribution of a D6 again.

If you need more to start, just ask.

 Here's a piece of code that shows the way I'm doing things 
 at the moment.

 -- code begins --

 # A die with n faces
 D = lambda n: [x+1 for x in range(n)]

 # A pool of 3 dice with 6 faces each
 pool = [D(6)] * 3

 # A List of all outcomes with the current 3d6 pool.
 results = [x+y+z for x in pool[0] for y in pool[1]
 for z in pool[2]]

This is very inefficient. I wouldn't want to calculate
the distribution of 10D10 this way.

Try to think how you would do this with only D2's.

(Triangle of Pascal) and generalize it.

-- 
Antoon Pardon
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-04 Thread Tomi Lindberg
First, thanks to Antoon and Alexander for replying.

Antoon Pardon wrote:

 It would be better to construct distributions for one
 die and make a function that can 'add' two distributions
 together.

As both replies pointed to this direction, I tried to take 
that route. Here's the unpolished code I came up with. Does 
it look even remotely sane way to accomplish my goal?

-- code begins --

# A die with n faces
D = lambda n: [x+1 for x in range(n)]

# A new die with 6 faces
d6 = D(6)

# Adds another die to results.
def add_dice(sums, die):
 # If first die, all values appear once
 if not sums:
 for face in die:
 sums[face] = 1
 # Calculating the number of appearances for additional
 # dice
 else:
 new_sums = {}
 for k in sums.keys():
 for f in die:
 if new_sums.has_key(k+f):
 new_sums[k+f] += sums[k]
 else:
 new_sums[k+f] = sums[k]
 sums = new_sums
 return sums

sums = add_dice({}, d6)
sums = add_dice(sums, d6)
sums = add_dice(sums, d6)

-- code ends --

Thanks,
Tomi Lindberg
-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-04 Thread Gerard Flanagan

Tomi Lindberg wrote:

 # A die with n faces
 D = lambda n: [x+1 for x in range(n)]
 

That can be written:

D = lambda n : range(1,n+1)

Gerard

-- 
http://mail.python.org/mailman/listinfo/python-list


Re: Dice probability problem

2006-04-04 Thread Dave Mandelin
That looks reasonable. The operation you are implementing is known as
'convolution' and is equivalent to multiplying polynomials. It would be
a little more general if you had the input 'die' be a sequence of the
count for each outcome, so d6 would be [1]*6 (or [0]+[1]*6 if you
prefer). That would allow allow you to represent unfair dice and also
to add not just a die to a distribution, but to add any two
distributions, so you can play tricks like computing 16d6 as
(d6)*2*2*2*2. (The code above is a convolution that restricts the
second distribution 'die' to have only 0 and 1 coefficients.) The
general convolution can be implemented much like what you have, except
that you need another multiplication (to account for the fact that the
coefficient is not always 0 or 1). My not particularly efficient
implementation:

def vsum(seq1, seq2):
return [ a + b for a, b in zip(seq1, seq2) ]

def vmul(s, seq):
return [ s * a for a in seq ]

# Convolve 2 sequences
# equivalent to adding 2 probabililty distributions
def conv(seq1, seq2):
n = (len(seq1) + len(seq2) -1)
ans = [ 0 ] * n
for i, v in enumerate(seq2):
vec = [ 0 ] * i + vmul(v, seq1) + [ 0 ] * (n - i - len(seq1))
ans = vsum(ans, vec)
return ans

# Convolve a sequence n times with itself
# equivalent to multiplying distribution by n
def nconv(n, seq):
ans = seq
for i in range(n-1):
ans = conv(ans, seq)
return ans

print nconv(3, [ 1 ] * 6)
print nconv(3, [ 1.0/6 ] * 6)
print nconv(2, [ .5, .3, .2 ])

[1, 3, 6, 10, 15, 21, 25, 27, 27, 25, 21, 15, 10, 6, 3, 1]
[0.0046296296296296294, 0.013888, 0.027776,
0.046296296296296294, 0.069448, 0.097238,
0.11574074074074074, 0.125, 0.125, 0.11574074074074073,
0.097224, 0.069448, 0.046296296296296294,
0.027776, 0.013888, 0.0046296296296296294]
[0.25, 0.2, 0.29004, 0.12,
0.040008]

-- 
http://mail.python.org/mailman/listinfo/python-list