[Tutor] generating independent random numbers

Dave Angel davea at ieee.org
Thu Sep 30 06:03:57 CEST 2010


  On 9/29/2010 9:17 PM, Carter Danforth wrote:
> On Wed, Sep 29, 2010 at 1:53 PM, Dave Angel<davea at ieee.org>  wrote:
>
>>   On 9/28/2010 5:11 PM, Carter Danforth wrote:
>>
>>> Thanks for the replies, Dave and Joel. The reason I'm not just using the
>>> time or datetime modules for a random date is because it's restricted to
>>> 1970-2038; I'm pulling dates from 1600-3099. Thanks a lot for the pointer
>>> about the leap years, Dave, as well the class instances; just updated it
>>> and
>>> it's all working now, and also included the rest of the code too w/ answer
>>> verification and time tracking.
>>>
>>> I want to start using this program to test myself for speed calculation
>>> using Zeller's formula, it's pretty cool for determining the days of dates
>>> -
>>> http://mathforum.org/dr/math/faq/faq.calendar.html
>>>
>>> Because of the way variables C and D are split up from the year in the
>>> formula, I split up the year for self.c and self.y.
>>>
>>> ------------------------
>>>
>>> import random, time, datetime, calendar
>>>
>>> class Date:
>>>      def __init__(self):
>>>          self.c = random.randint(16,30)
>>>          self.y = random.randint(0,99)
>>>          self.month = random.randint(1,12)
>>>          self.year = self.c*100 + self.y
>>>
>>>          apr = [4,6,9,11]
>>>          feb = [2]
>>>          notleap = [1700, 1800, 1900, 3000]
>>>
>>>          if self.month in feb:
>>>              if self.year%4 == 0:
>>>                  if self.year in notleap:
>>>                      self.k = random.randint(1,28)
>>>                  else:
>>>                      self.k = random.randint(1,29)
>>>              else:
>>>                  self.k = random.randint(1,28)
>>>          elif self.month in apr:
>>>              self.k = random.randint(1,30)
>>>          else:
>>>              self.k = random.randint(1,31)
>>>
>>>          if self.month in [1,2]:
>>>              d = self.y - 1
>>>              m = self.month + 10
>>>          else:
>>>              d = self.y
>>>              m = self.month - 2
>>>
>>>          z = self.k + (13*m-1)/5 + d + d/4 + self.c/4 - 2*self.c
>>>
>>>          if z<   0:
>>>              r = (abs(z)/7)*7 + z + 7
>>>          else:
>>>              r = z%7
>>>
>>>          dict = { 0: 'Sunday', 1: 'Monday', 2: 'Tuesday', 3: 'Wednesday',
>>> 4:
>>> 'Thursday', 5: 'Friday', 6: 'Saturday' }
>>>          self.day = dict[r]
>>>
>>> t1m = time.localtime().tm_min
>>> t1s = time.localtime().tm_sec
>>> t1 = t1m + t1s/100.0
>>> n = 0
>>> x = 0
>>>
>>> while n<   10:
>>>      newdate = Date()
>>>
>>>      print '\n',calendar.month_name[newdate.month], newdate.k,',',
>>> newdate.year,'=',
>>>      answer = raw_input()
>>>      if answer.capitalize() == newdate.day:
>>>          pass
>>>      else:
>>>          x += 1
>>>      n += 1
>>>
>>> t2m = time.localtime().tm_min
>>> t2s = time.localtime().tm_sec
>>> t2 = t2m + t2s/100.0
>>> td = t2 - t1
>>>
>>> print '\n',x,'out of 10 wrong\nAvg time/question:',td/10,'\nTotal
>>> time:',td
>>>
>>> <snip>
>>>
>>>   You top-posted again.  Put your comments after the part you're quoting,
>> not before.
>>
>> You still have a problem in the code, and I still think it's in the
>> 2*self.c, though I don't have time to debug it.
>>
>> Look up the day for  1/1/2099, and for 1/1/2100  and it comes out the same.
>>   That's not correct.  No adjacent years start on the same day, it's always
>> either one day or two.
>>
>> You have too much in one function (method), which makes it hard to debug
>> it.  Factor it into separate functions, and then test each independently.
>>   And using k  for day and d for year make no sense to me, though perhaps it
>> does in some other language.
>>
>> DaveA
>>
>>
> Hey Dave, you probably left c and y alone when comparing the years. If the
> date's 1/1/2099, then self.c = 20 and self.y=99. If you try doing it again
> while changing those values, for 1/1/2099, the day comes out to be Thursday,
> and for 1/1/2100 you'll get Wednesday.
>
> Glad you pointed out the 2100 date though, there actually was a problem in
> it, but it's not the 2*self.c; I had to account for d = y - 1 when y = 00
> (zeller subtracts months by 2, so it needs to be the previous yr for
> jan/feb).
>
> Below is the updated code, I put in a few comments to make it read easier.
>
> ----------------------
>
> import random, time, datetime, calendar
>
> class Date:
>      def __init__(self):
>          self.c = random.randint(16,30) # first two digits in a year
>          self.y = random.randint(0,99)  # last two digits in a year
>          self.month = random.randint(1,12)
>          self.year = self.c*100 + self.y
>
>          apr = [4,6,9,11]
>          feb = [2]
>          notleap = [1700, 1800, 1900, 2100, 2200, 2300, 2500, 2600, 2700,
> 2900, 3000]
>
>          if self.month in feb:          # assigns days, given the month
>              if self.year%4 == 0:
>                  if self.year in notleap:
>                      self.k = random.randint(1,28)
>                  else:
>                      self.k = random.randint(1,29)
>              else:
>                  self.k = random.randint(1,28)
>          elif self.month in apr:
>              self.k = random.randint(1,30)
>          else:
>              self.k = random.randint(1,31)
>
>          if self.month in [1,2]:        # months in zeller's rule are
> subtracted by 2
>              if self.y == 0:            # need to acct for jan/feb year
> change
>                  d = 99
>                  m = self.month + 10
>              else:
>                  d = self.y - 1
>                  m = self.month + 10
>          else:
>              d = self.y
>              m = self.month - 2
>
>          z = self.k + (13*m-1)/5 + d + d/4 + self.c/4 - 2*self.c  # -->
> Zeller's formula
>
>          if z<  0:                      # the remainder of z/7 determines the
> day of the week
>              r = (abs(z)/7)*7 + z + 7   # need to account for negative z
> values by adding 7 to remainder
>          else:
>              r = z%7
>
>          week =
> ('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday')
>          self.day = week[r]
>
> t1m = time.localtime().tm_min
> t1s = time.localtime().tm_sec
> t1 = t1m + t1s/100.0
> n = 0
> x = 0
>
> while n<  10:
>      newdate = Date()
>      print '\n  ',calendar.month_name[newdate.month], str(newdate.k) +
> ',',newdate.year,'=',
>      answer = raw_input()
>      if answer.capitalize() == newdate.day:
>          pass
>      else:
>          x += 1
>      n += 1
>
> t2m = time.localtime().tm_min
> t2s = time.localtime().tm_sec
> t2 = t2m + t2s/100.0
> td = t2 - t1
>
> print '\n',x,'out of 10 wrong\nAvg time/question:',td/10,'\nTotal time:',td
>
In order to check it out, I refactored the zeller portion into a 
separate function.  Since there was no reason to make it a class member, 
I had to do a lot of editing.  And since I couldn't understand the 
one-letter symbols, I changed them as well.

Your code is much more verbose than needed.  If you just used 4 digit 
years, that last bug wouldn't have happened.

I think you have another bug, the logic for z<0 is incorrect, but also 
unnecessary.  For some negative values, it'll produce a 7, which would 
give you a subscript error in your week[r] expression.  But Python does 
the right thing with remainders of negative values, so all you need is 
the %7.

Consider using 306/10 for the month, and 1461/4 for the year, and then 
-year/100, and + year/400.  Then you get actual number of days, Make 
that a function, and when you call it here, you use modulo 7 for day of 
week.  But you can then test it independently.

How about:

def getDay2(year, month, day):
     if month < 3:
         year -= 1
         month += 12
     z = day + (month-4)*306/10 + year*1461/4 - year/100 + year/400
     return z- 275

Or of course:

def getDay3(year, month, day):
     mydate = datetime.date(year, month, day)
     return mydate.toordinal()

which gives identical results for all dates in that range.

And of course if you write one more function:

def getDate(days):
     mydate = datetime.date.fromordinal(days)
     return mydate.year, mydate.month, mydate.day

then you can make your random date much simpler as well:

year, month, day = getDate(random.randint(584023, 1095727))

assuming you want to fix the other bug:  date range from 1600 through 
3000.  Your present code goes to 3099.

The body of your method then looks something like (untested)

     self.year, self.month, self.day = getDate(random.randint(584023, 
1095727))
     r = getDay2(self.year, self.month, self.day)

   week = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
   self.dayname = week[r]

Incidentally, there's another problem in your code -- when comparing the day string, you uppercase one of them, but not the other.



DaveA



More information about the Tutor mailing list