# [Tutor] generating independent random numbers

Dave Angel davea at ieee.org
Thu Sep 30 06:38:31 CEST 2010

```
On 2:59 PM, Dave Angel wrote:
>  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/
>>>> 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,'=',
>>>>          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>
>>>>
>>>> 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,'=',
>>          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.
>
>
> 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
>
>
Sorry, I mentioned %7, but forgot to include it in my last sample.  It's
still untested, but closer.

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%7]

DaveA

```