Related
I am new to Python and currently practicing online resources. In this question, I have to take user input for today's date, birth date -> calculate the total days for each and then tell the difference between the days left in birthday.
The challenge is to split this into 3 functions and solve it. I have split it into 3 functions this way:
to get actual date days
to get birth date days
to calculate the difference
Can someone please help me how to reference the return values of actualDates() and birthDay() in the diff() function, and return the actual difference?
Also, if someone can help me on how to make the monthDays() list global, that would be helpful as well.
def actualDates():
actual_months =[]
#monthDays = monthDays()
monthDays = [31, 28, 31, 30, 31, 30,31, 31, 30, 31, 30, 31]
today_months= int(input("What is the month (1-12)?"))
if today_months in ( 1, 3,5, 7, 8,10, 12):
today_days= int(input("What is the day (1-30)?"))
else:
today_days= int(input("What is the day (1-31)?"))
for i in range(0, today_months-1):
actual_months.append(monthDays[i])
if today_months <= 1:
today = today_days
else:
today = sum(actual_months) + today_days
return today
def birthDay():
actual_months =[]
monthDays = [31, 28, 31, 30, 31, 30,31, 31, 30, 31, 30, 31]
today_days= int(input("Please enter your birthday:"))
today_months= int(input("What is the month (1-12)?"))
if today_months in ( 1, 3,5, 7, 8,10, 12):
today_days= int(input("What is the day (1-30)?"))
else:
today_days= int(input("What is the day (1-31)?"))
for i in range(0, today_months-1):
actual_months.append(monthDays[i])
if today_months <= 1:
birth = today_days
else:
birth = sum(actual_months) + today_days
return birth
def diff(today , birth):
pass
diff()
You can pass the dates to the diff() like this:
def diff(today , birth):
return birth - today
today = actualDates()
birth = birthDay()
diff = diff(today, birth)
print(diff)
You can make the list monthDays global by putting
monthDays = [31, 28, 31, 30, 31, 30,31, 31, 30, 31, 30, 31]
at the top of the code outside of the function.
Then inside of any function monthDays is being used, you can declare it global:
def actualDates():
global monthDays
Also, you may wish to investigate the Python module datetime in the Python documentation. You can calculate the time difference between two dates using datetime.timedelta.
I take the time now, and I need to know how many minutes left for the next multiple minute of 5, for example, I use the function now = datetime.now() and returns datetime.datetime(2019, 8, 2, 9, 33, 5, 478647), in this example, left 2 minutes for the 35 minutes, I need the program do that automatically.
time = datetime.datetime.now()
min = time.minute
left = 5-(min%5)
This will return 5 if the minute is a multiple of 5.
Your task is to write and test a function which takes two arguments (a year and a month) and returns the number of days for the given month/year pair (yes, we know that only February is sensitive to the year value, but we want our function to be universal). Now, convince the function to return None if its arguments don't make sense.
Use a list filled with the months' lengths. You can create it inside the function - this trick will significantly shorten the code.
I have got the code down but not the 'none' part. Can someone help me with this?
def IsYearLeap(year):
if (year%4==0):
return True
if (year%4!=0):
return False
def DaysInMonth(year,month):
if month in {1, 3, 5, 7, 8, 10, 12}:
return 31
elif month==2:
if IsYearLeap(year):
return 29
else:
return 28
elif month in {4,6,8,9,11}:
return 30
else:
return none
testyears = [1900, 2000, 2016, 1987,2019]
testmonths = [ 2, 2, 1, 11,4]
testresults = [28, 29, 31, 30,33]
for i in range(len(testyears)):
yr = testyears[i]
mo = testmonths[i]
print(yr,mo,"->",end="")
result = DaysInMonth(yr,mo)
if result == testresults[i]:
print("OK")
else:
print("Failed")
It seems that you have rather made a simple mistake. If you are not used the case-sensitive programming languages or have no experience in programming languages, this is understandable.
The keyword None is being misspelled as the undefined word none.
I think your testresults is wrong. February of 1900 should be 29 days also April of 2019 30 days. Also its None instead none. Another things also its better to using list on months list so you could using [1, 3, 5, 7, ...] instead {1, 3, 5, 7, ...}.
Also from your test cases you won't got None, in case you want check this case you could check with month = 13, and you will cover this case
As a further comment to the other good answers to this question, the correct rule for leap years should be something like:
def is_leap_year(year):
""" is it a leap year?
>>> is_leap_year(1984)
True
>>> is_leap_year(1985)
False
>>> is_leap_year(1900)
False
>>> is_leap_year(2000)
True
"""
return (year % 4 == 0 and
(year % 100 != 0 or year % 400 == 0))
Similarly, the test cases need to be clear that 1900 was not a leap year, 2000 was. I recommend writing a separate set of test cases for is_leap_year. Ultimately, in production code, you will be better off to use one of the many time/date libraries. The comments that I've provided make use of doctest to provide this unit test quickly.
A function which does not explicitly return anything implicitly returns None.
In addition to the spelling error (none vs None) you are using this by accident here:
def IsYearLeap(year):
if (year%4==0):
return True
if (year%4!=0):
return False
Can you see what will happen if neither of the conditions is true? It won't return either False or True, which presumably the caller expects. (Though if you check whether None == True you will get False, and not None is True, so you won't get a syntax error, just a result which might be different from what you expect - the worst kind of bug!)
def IsYearLeap(year):
return year % 4 == 0 & (year % 400 == 0 | year % 100 != 0)
def DaysInMonth(year,month):
if month in [1, 3, 5, 7, 8, 10, 12]:
return 31
elif month==2:
if IsYearLeap(year):
return 29
else:
return 28
elif month in [4,6,8,9,11]:
return 30
else:
return None
#
testYears = [1900, 2000, 2016, 1987]
testMonths = [2, 2, 1, 11]
testResults = [28, 29, 31, 30]
for i in range(len(testYears)):
yr = testYears[i]
mo = testMonths[i]
print(yr, mo, "->", end="")
result = DaysInMonth(yr, mo)
if result == testResults[i]:
print("OK")
else:
print("Failed")
def is_year_leap(year):
return year % 4 == 0 and year % 100 != 0 or year % 400 == 0
def days_in_month(year, month):
days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
if type(year) != int or year < 1582 or\
type(month) != int or month < 1 or month > 12:
return None
elif is_year_leap(year):
del days[1]
days.insert(1, 29)
return days[month - 1]
test_years = [1900, 2000, 2016, 1987]
test_months = [2, 2, 1, 11]
test_results = [28, 29, 31, 30]
for i in range(len(test_years)):
yr = test_years[i]
mo = test_months[i]
print(yr, mo, "->", end="")
result = days_in_month(yr, mo)
if result == test_results[i]:
print("OK")
else:
print("Failed")
A few minor remarks:
You should remove the duplicate 8th month (August is listed for 30 and 31 days),
Better replace brackets {} with the list [],
Replace none with None (Python is case-sensitive, None is the keyword),
Add one more condition for a leap year:
(year % 400 == 0) and (year % 100 == 0) -> return True
(year % 4 == 0) and (year % 100 != 0) -> return True
Basically trying to figure out how to create a for loop that generates a range around a the main number "x" based on "n"
x = 10 # x = Actual
n = 5
because
Actual = input("What's the Actual") # Enter 10
Target = input("What's the Target") # Enter 15
n = Target - Actual # 5 = 15 - 10
Since Actual is 10
I would like to see..
5, 6, 7, 8, 9 , 10, 11, 12, 13, 14, 15
The code is:
n = 2
def price(sprice):
for i in range(n*2):
sprice = sprice + 1
print(sprice)
price(200)
This code shows 201,202,203,204 and the actual is 200.
I want to see 198,199,200,201,202 because n = 2 and when multiply by 2 = 4 which shows a range of 4 values around 200
According to the docs, range can accept two argument that specify the start (inclusive) and end (exclusive) of the interval. So you can get an interval in the form [start, stop).
You would like to create the interval [Actual - n, Actual + n], so just translate it almost literally to Python, bearing in mind that range excludes the second argument from that range, so you should add one to it:
>>> list(range(Actual - n, Actual + n + 1))
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
ForceBru has already shown a pythonic solution to your problem. I only want to add that your original code works as intended after some minor tweaks:
n = 2
def price(sprice):
sprice -= n # short way to say: sprice = sprice - n
for i in range(n*2+1): # +1 required as in 1-argument range it is exclusive
sprice = sprice + 1
print(sprice)
price(200)
Output:
199
200
201
202
203
Note that Python recognizes * is to be executed before + independently from its order. Hence you might write 1+n*2 in place of n*2+1.
This is what I have so far but it breaks if I go past plus or minus 11 the previous number. So if the previous number is 1 the next number has to be more than 16. Also I am trying to only use each number once.
Example output:
[3,45, 1, 16, 33, 3, 23.....]
My script so far is:
import random
new_array=[]
counter = 0
array=range(51)
array=array[1:51]
while len(new_array)<50:
y=random.choice(array)
if y not in new_array and counter!=0 and y not in (range(new_array[counter-1]-11,new_array[counter-1]+11)):
new_array.append(y)
counter+=1
elif counter == 0:
new_array.append(y)
counter+=1
else:
pass
How about the following approach. Start with a shuffled list. For each pair, if the distance is less than 15 rotate the remaining numbers.
import random
def get_shuffled_list(length):
output = range(1, length+1)
i = 0
sanity = 0
while i < length and sanity == 0:
random.shuffle(output)
for i in range(1, length):
sanity = length - i + 1
while abs(output[i-1] - output[i]) <= 15 and sanity:
output.append(output[i])
del output[i]
sanity -= 1
if sanity == 0:
break
return output
print get_shuffled_list(50)
Giving the following type of output:
[1, 38, 3, 33, 16, 43, 10, 41, 9, 35, 4, 50, 29, 8, 44, 28, 5, 32, 14, 40, 21, 47, 25, 48, 30, 12, 34, 18, 42, 15, 36, 13, 31, 2, 24, 7, 23, 39, 22, 6, 26, 49, 27, 11, 45, 19, 46, 17, 37, 20]
The issue comes at the end if you fail to have any remaining numbers that satisfy the criteria, in which case, start again. For a test of 10,000 shuffles, the worst case I managed was 196 attempts. Not ideal but it does work.
Try somethings like this:
Start with an initial value for last.
This value should be a string ('start').
Loop for however many times you need:
Check if last == 'start'.
If it is, add a random number to the list.
If it isn’t, create a random number.
Until abs(number - last) is less than 15, create a new random number.
Add the number to the list.
Set last to the last element of the list.
edit: does not answer the question, as it CAN and WILL pick some numbers several times. Leaving it around in case this variant is of use to someone else.
The algorithm you use, and the one suggested by JF share an interesting flaw: there is no limit to the time they take. If you are really unlucky, the function could not return before the end of the universe.
Here is one that will execute in bound time, namely O(n):
import random
def spaced_randoms(min_val, max_val, number, space):
if max_val - min_val <= 2 * space:
raise ValueError('interval not large enough for space')
last = random.randint(min_val, max_val)
result = [last]
for _ in range(number - 1):
skip_low = max(0, min(space, last - min_val))
skip_high = max(0, min(space, max_val - last))
value = random.randint(min_val + skip_low, max_val - skip_high - 1)
last = value - skip_low if value < last else value + skip_high + 1
result.append(last)
return result
>>> print(spaced_randoms(0, 50, 50, 15))
[35, 10, 44, 26, 47, 22, 49, 29, 3, 28, 44, 17, 40, 3, 39, 7, 48, 22, 41, 5, 21, 4, 32, 9, 34, 3, 46, 3, 20, 38, 10, 49, 32, 16, 38, 5, 26, 45, 26, 49, 13, 44, 1, 30, 12, 30, 0, 46, 15, 42]
The idea is simple : we reduce the possible interval for the new number to account for forbidden values. The we remap the result. For instance, assuming for 15 space at some point last == 32, then:
we pick a number between 15 and 34.
if picked number is in [15 to 31], we remap to [0 to 16]
if picked number is in [32 to 34], we remap to [48 to 50]
Well, something along the line, in pseudocode
sampled = []
n = 50
x = 15
curr = n + x + 1
for k in range(1, 1000):
# build exclusion range tuple, where you cannot sample,
# say for calc_ex_range(50, 15, 27) it should return
# (12, 42)
exclude_range = calc_ex_range(n, x, curr)
# build new sampling array, given the exclusion range
# result should be [1,2,3,4,5,6,7,8,9,10,11,43,44,45,46,47,48,49,50]
sampling_array = calc_sarray(n, exclude_range)
# remove already sampled numbers from sampling array
sampling_array = filter_sarray(sampling_array[:], sampled)
if len(sampling_array) == 0:
break
# sample one item from the array
curr = random.sample(sampling_array, 1)
sampled.append(curr)
Complexity would be O(N^2), I guess, but it always finishes
UPDATE
In order to sample number once, filter out sampled array
After the other answer that failed to meet the "each item must appear exactly once" requirement, here are some thoughts:
If I call spacing the minimum absolute difference between two consecutive numbers, then:
Selecting the beginning of the array is easy. You can do that with Severin Pappadeux's code.
Once you are left with 2 * spacing unchosen items, you must switch algorithms.
The problem of choosing the last items seems to be P-complete.
Unfortunately I am not good enough at algorithm theory to build a formal proof. Also, maybe it's just I failed to find a better way. But at this point I don't see any way but implementing picking of the last items as a graph search.
As interesting as this is, however, I cannot afford more time on it. But if you come back and answer your own question I'll love reading it.
If it's acceptable to arbitrarily exclude many permutations that fit your criteria, you can quite easily create an algorithm for generating permutations that will work.
The simplest example I can think of is the following:
Partition the range {1,50} into 10 sub-ranges of five numbers: {1,5},{6,10},{11,15}....
Pick a sequence of these sub-ranges such that for any two sub-ranges next to each other, the closest elements are at least 15 apart. The simplest way to do this is to pick every fourth sub-range: {1,5},{21,25},{41,45},{11,15}...
The output sequence starts with one randomly-chosen number from each range: 3,22,41,15...
Continue picking numbers from the sub-ranges in order until you've picked all 5 numbers from each sub-range (i.e. until you've picked every number in the full range from 0 to 50): 3,22,41,15...1,24,42,14...4,21...
You can do simple variations of this by picking different sub-range sizes (e.g. 15 or 7, although these introduce an additional complication of not evenly partitioning the original range). Or you can randomly transpose elements of the output sequence (or otherwise mutate the sequence) following the rule that you can never invalidate the sequence (i.e. you cannot introduce a pairing of elements that are less than 15 apart). You could also make the ordering of sub-ranges quasi-random, or change the ordering on ach iteration through the sub-ranges.
Even with these variations, this obviously won't be a fully random selection of any possible valid permutation. But in practice, I expect there's a good chance that that won't matter.
How about this? I use randint to easily get an integer within the range you require:
import random
my_list = []
# This number should not be within 15 units from each
# new random integer:
previous = random.randint(1, 50)
# Loop until my_list' length is 50; see at the bottom
while True:
# Get a new random integer from 1 to 50
rand_int = random.randint(1, 50)
# If the new integer is NOT at a range within 15 of the previous number,
# append to the list; otherwise skip and try again
if not rand_int - 15 < previous < rand_int + 15:
my_list.append(rand_int)
# This new integer becomes the new previous
previous = rand_int
if len(my_list) >= 50:
# Break from the loop and print the new list
break
print(my_list)