[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