python tuple over writing previous data - python

I am trying to create a function that will start the loop and add a day to current day count, it will ask 3 questions then combine that data to equal Total_Output. I then want 'n' to represent the end of the tuple, and in the next step add the Total_Output to the end of the tuple. But when I run the function it seems like it is creating a new tuple.
Example:
Good Morninghi
This is Day: 1
How much weight did you use?40
How many reps did you do?20
How many sets did you do?6
Day: 1
[4800.0]
This is Day: 2
How much weight did you use?50
How many reps did you do?20
How many sets did you do?6
Day: 2
[6000.0, 6000.0]
This is Day: 3
How much weight did you use?40
How many reps did you do?20
How many sets did you do?6
Day: 3
[4800.0, 4800.0, 4800.0]
failed
Here is the function:
def Start_Work(x):
Num_Days = 0
Total_Output = 0
Wght = 0
Reps = 0
Sets = 0
Day = []
while x == 1 and Num_Days < 6: ##will be doing in cycles of 6 days
Num_Days += 1 ##increase day count with each loop
print "This is Day:",Num_Days
Wght = float(raw_input("How much weight did you use?"))
Reps = float(raw_input("How many reps did you do?"))
Sets = float(raw_input("How many sets did you do?"))
Total_Output = Wght * Reps * Sets
n = Day[:-1] ##go to end of tuple
Day = [Total_Output for n in range(Num_Days)] ##add data (Total_Output to end of tuple
print "Day:",Num_Days
print Day
else:
print "failed"
Input = raw_input("Good Morning")
if Input.lower() == str('hi') or str('start') or str('good morning'):
Start_Work(1)
else:
print "Good Bye"

n = Day[:-1] ##go to end of tuple
Day = [Total_Output for n in range(Num_Days)] ##add data (Total_Output to end of tuple
Does not do what you think it does. You assign n but never use it (the n in the loop is assigned by the for n in), and it only hold a list of the end of the Day variable.
You then set Day to be [Total_Output] * Num_Days, so you make a new list of Num_Days occurrences of Total_Output.
You want:
Day.append(Total_Output)
to replace both of those lines.

Related

Paradox python algorithm

I am trying to solve a version of the birthday paradox question where I have a probability of 0.5 but I need to find the number of people n where at least 4 have their birthdays within a week of each other.
I have written code that is able to simulate where 2 people have their birthdays on the same day.
import numpy
import matplotlib.pylab as plt
no_of_simulations = 1000
milestone_probabilities = [50, 75, 90, 99]
milestone_current = 0
def birthday_paradox(no_of_people, simulations):
global milestone_probabilities, milestone_current
same_birthday_four_people = 0
#We assume that there are 365 days in all years.
for sim in range(simulations):
birthdays = numpy.random.choice(365, no_of_people, replace=True)
unique_birthdays = set(birthdays)
if len(unique_birthdays) < no_of_people:
same_birthday_four_people += 1
success_fraction = same_birthday_four_people/simulations
if milestone_current < len(milestone_probabilities) and success_fraction*100 > milestone_probabilities[milestone_current]:
print("P(Four people sharing birthday in a room with " + str(no_of_people) + " people) = " + str(success_fraction))
milestone_current += 1
return success_fraction
def main():
day = []
success = []
for i in range(1, 366): #Executing for all possible cases where can have unique birthdays, i.e. from 1 person to a maximum of 365 people in a room
day.append(i)
success.append(birthday_paradox(i, no_of_simulations))
plt.plot(day, success)
plt.show()
main()
I am looking to modify the code to look for sets of 4 instead of 2 and then calculate the difference between them to be less than equal to 7 in order to meet the question.
Am I going down the right path or should I approach the question differently?
The key part of your algorithm is in these lines:
unique_birthdays = set(birthdays)
if len(unique_birthdays) < no_of_people:
same_birthday_four_people += 1
Comparing the number of unique birthdays to the number of people did the work when you tested if two different people had the same birthday, but It wont do for your new test.
Define a new function that will receive the birthday array and return True or False after checking if indeed 4 different people had the a birthday in a range of 7 days:
def four_birthdays_same_week(birthdays):
# fill this function code
def birthday_paradox(no_of_people, simulations):
...
(this function can be defined outside the birthday_paradox function)
Then switch this code:
if len(unique_birthdays) < no_of_people:
same_birthday_four_people += 1
into:
if four_birthdays_same_week(birthdays):
same_birthday_four_people += 1
Regarding the algorithm for checking if there 4 different birthday on the same week: a basic idea would be to sort the array of birthdays, then for every group of 4 birthdays check if the day range between them is equal or lower to 7:
if it is, the function can immediately return True.
(I am sure this algorithm can be vastly improved.)
If after scanning the whole array we didn't return True, the function can return False.

Counting string occurrences in list

I'm trying to work on a simple python problem hosted on hackerrankteam but I'm having difficulty with the count function for lists in Python. I've tried multiple test cases but my count function always returns 0.
Objective: count the number of occurrences that consecutive squares equals the number of days.
Is this an issue with the type of list? Is there an easier way for me count the values in one line instead of having to check the valued pairs and then count the sums?
import sys
def solve(size, squares, day, month):
check = [sum(squares[nums:nums+month]) == day for nums in range(0,len(squares))]
print (check) #Test list output
count = check.count('True')
return count
#Test Cases 1
# size = 6
# squares = [1,1,1,1,1,1]
# day, month = (3,2)
#Output 0
#Test Cases 2
# size = 1
# squares = [4]
# day, month = (4,1)
#Output 1
#Test Cases 3
size = 5
squares = [1,2,1,3,2]
day, month = [3,2]
#Output 2
#Custom User Input:
# size = int(input().strip())
# squares = list(map(int, input().strip().split(' ')))
# day, month = input().strip().split(' ')
# day, month = [int(day), int(month)]
result = solve(size, squares, day, month)
print(result)
check.count('True')
This code is counting the number of occurrences of the string 'True'.
It should instead be:
check.count(True)
You could also simply use this:
sum(check)

How to prevent 36k x 36k x 36k permutations to be calculated

I have a nice planning exercise, that I just can't get my head around (except for brute force, which is too big to handle). We are organizing a golf trip for 12 persons. We play 4 days of golf. With 3 flights every day. (so 12 flights in total).
We want to:
maximize the number of unique players that play each other in a flight,
but also want to minimize the number of double occurences (2 players playing each other more than once).
Since with 12 players and 4 players per flight I can roughly create 36k player combinations per flight per day, it becomes pretty compute intense. Is there any smarter way to solve this? My gut feeling says fibonacci can help out, but not sure how exactly.
This is the code I have so far:
import random
import itertools
import pandas as pd
def make_player_combi(day):
player_combis = []
for flight in day:
#print flight
for c in itertools.combinations(flight,2):
combi = list(sorted(c))
player_combis.append('-'.join(combi))
return player_combis
def make_score(a,b,c):
df = pd.DataFrame(a + b + c,columns=['player_combi'])['player_combi']
combi_counts = df.value_counts()
pairs_playing = len(combi_counts)
double_plays = combi_counts.value_counts().sort_index()
return pairs_playing, double_plays
players = ['A','B','C', 'D', 'E', 'F', 'G', 'H', 'I', 'J','K','L']
available_players = players[:]
n = 0
combinations_per_day = []
for players_in_flight_1 in itertools.combinations(players,4):
available_players_flight_1 = players[:]
available_players_flight_2 = players[:]
available_players_flight_3 = players[:]
for player in players_in_flight_1:
# players in flight1 can no longer be used in flight 2 or 3
available_players_flight_2.remove(player)
available_players_flight_3.remove(player)
for players_in_flight_2 in itertools.combinations(available_players_flight_2,4):
players_in_flight_3 = available_players_flight_3[:]
for player in players_in_flight_2:
players_in_flight_3.remove(player)
n = n + 1
print str(n), players_in_flight_1,players_in_flight_2,tuple(players_in_flight_3)
combinations_per_day.append([players_in_flight_1,players_in_flight_2,tuple(players_in_flight_3)])
n_max = 100 # limit to 100 entries max per day to save calculations
winning_list = []
max_score = 0
for day_1 in range(0,len(combinations_per_day[0:n_max])):
print day_1
for day_2 in range(0,len(combinations_per_day[0:n_max])):
for day_3 in range(0,len(combinations_per_day[0:n_max])):
a = make_player_combi(combinations_per_day[day_1])
b = make_player_combi(combinations_per_day[day_2])
x,y = make_score(a,b,c)
if x >= max_score:
max_score = x
my_result = {'pairs_playing' : x,
'double_plays' : y,
'max_day_1' : day_1,
'max_day_2' : day_2,
'max_day_3' : day_3
}
winning_list.append(my_result)
I chose the suboptimal ( but good enough) solution by running 5 mio samples and taking the lowest outcome... Thanks all for thinking along
You can brute-force this by eliminating symmetries. Let's call the 12 players a,b,c,d,e,f,g,h,i,j,k,l, write a flight with 4 players concatenated together: degj, and a day's schedule with the three flights: eg. abcd-efgh-ijkl.
The first day's flights are arbitrary: say the 3 flights are abcd-efgh-ijkl.
On the second and third day, you have fewer than (12 choose 4) * (8 choose 4) possibilities, because that counts each distinct schedule 6 times. For example, abcd-efgh-ijkl, efgh-abcd-ijkl and ijkl-efgh-abcd are all counted as separate, but they are essentially the same. In fact, you have (12 choose 4) * (8 choose 4) / 6 = 5775 different schedules.
Overall, this gives you 5775 * 5775 = 33350625, a manageable 33 million to check.
We can do a little better: we might as well assume that day 2 and day 3 schedules are different, and not count schedules that are the same but day 2 and day 3's are swapped over. This gives us another factor of very almost 2.
Here's code that does all that:
import itertools
import collections
# schedules generates all possible schedules for a given day, up
# to symmetry.
def schedules(players):
for c in itertools.combinations(players, 4):
for d in itertools.combinations(players, 4):
if set(c) & set(d):
continue
e = set(players) - set(c) - set(d)
sc = ''.join(sorted(c))
sd = ''.join(sorted(d))
se = ''.join(sorted(e))
if sc < sd < se:
yield sc, sd, se
# all_schedules generates all possible (different up to symmetry) schedules for
# the three days.
def all_schedules(players):
s = list(schedules(players))
d1 = s[0]
for d2, d3 in itertools.combinations(s, 2):
yield d1, d2, d3
# pcount returns a Counter that records how often each pair
# of players play each other.
def pcount(*days):
players = collections.Counter()
for d in days:
for flight in d:
for p1, p2 in itertools.combinations(flight, 2):
players[p1+p2] += 1
return players
def score(*days):
p = pcount(*days)
return len(p), sum(-1 for v in p.itervalues() if v > 1)
best = None
for days in all_schedules('abcdefghijkl'):
s = score(*days)
if s > best:
best = s
print days, s
It still takes some time to run (about 10-15 minutes on my computer), and produces this as the last line of output:
abcd-efgh-ijkl abei-cfgj-dhkl abhj-cekl-dfgi (48, -3)
That means there's 48 unique pairings over the three days, and there's 3 pairs who play each other more than once (ab, fg and kl).
Note that each of the three pairs who play each other more than once play each other on every day. That's unfortunate, and probably means you need to tweak your idea of how to score schedules. For example, excluding schedules where the same pair plays more than twice, and taking the soft-min of the number of players each player sees, gives this solution:
abcd-efgh-ijkl abei-cfgj-dhkl acfk-begl-dhij
This has 45 unique pairings, and 9 pairs that play each other more than once. But every player meets at least 7 different players and might be preferable in practice to the "optimal" solution above.

Holiday program error in Python

I was asked to make a program that takes the day that I start my travel length of my stay as an input and give the day that I return in as an output so I tried this and I keep getting an error that says (KeyError = -3)
Note: I'm a beginner, so be easy on me :)
dect = {0:'Sunday', 1: 'Monday', 2:'Tuesday',3:'Wednesday', 4:'Thursday',
5:'Friday',6:'Saturday'
}
def day(x):
print( dect[x])
def holiday(start,length):
length = length + start
while True:
if length <= 0:
break
print(length)
else:
length = length - 7
day(length)
s = int(input('Enter the start day: '))
l = int(input('Enter the length of your stay: '))
holiday(s,l)
The value of length can become negative and this is why you're looking up -3 in the dictionary.
For example, if my holiday is 4 days (length = 4) then you're doing length = length - 7 which means you're calling day() with -3 as value.
Your dictionary only has values for the keys 0 - 6 and that's why you're getting a KeyError as the key -3 is not in the dictionary.
You can fix it by changing the check into if length < 7.
What you can do is, simply use datetime module.
from datetime import date,timedelta
d = date.today() + timedelta(days=2)
print d
You can use timedelta to add as many days/hours/whatever you want. See here for the documentation.
Replace your holiday function as -
def holiday(start,length):
length = length + start
length = length % 7
day(length)

Python interval interesction

My problem is as follows:
having file with list of intervals:
1 5
2 8
9 12
20 30
And a range of
0 200
I would like to do such an intersection that will report the positions [start end] between my intervals inside the given range.
For example:
8 9
12 20
30 200
Beside any ideas how to bite this, would be also nice to read some thoughts on optimization, since as always the input files are going to be huge.
this solution works as long the intervals are ordered by the start point and does not require to create a list as big as the total range.
code
with open("0.txt") as f:
t=[x.rstrip("\n").split("\t") for x in f.readlines()]
intervals=[(int(x[0]),int(x[1])) for x in t]
def find_ints(intervals, mn, mx):
next_start = mn
for x in intervals:
if next_start < x[0]:
yield next_start,x[0]
next_start = x[1]
elif next_start < x[1]:
next_start = x[1]
if next_start < mx:
yield next_start, mx
print list(find_ints(intervals, 0, 200))
output:
(in the case of the example you gave)
[(0, 1), (8, 9), (12, 20), (30, 200)]
Rough algorithm:
create an array of booleans, all set to false seen = [False]*200
Iterate over the input file, for each line start end set seen[start] .. seen[end] to be True
Once done, then you can trivially walk the array to find the unused intervals.
In terms of optimisations, if the list of input ranges is sorted on start number, then you can track the highest seen number and use that to filter ranges as they are processed -
e.g. something like
for (start,end) in input:
if end<=lowest_unseen:
next
if start<lowest_unseen:
start=lowest_unseen
...
which (ignoring the cost of the original sort) should make the whole thing O(n) - you go through the array once to tag seen/unseen and once to output unseens.
Seems I'm feeling nice. Here is the (unoptimised) code, assuming your input file is called input
seen = [False]*200
file = open('input','r')
rows = file.readlines()
for row in rows:
(start,end) = row.split(' ')
print "%s %s" % (start,end)
for x in range( int(start)-1, int(end)-1 ):
seen[x] = True
print seen[0:10]
in_unseen_block=False
start=1
for x in range(1,200):
val=seen[x-1]
if val and not in_unseen_block:
continue
if not val and in_unseen_block:
continue
# Must be at a change point.
if val:
# we have reached the end of the block
print "%s %s" % (start,x)
in_unseen_block = False
else:
# start of new block
start = x
in_unseen_block = True
# Handle end block
if in_unseen_block:
print "%s %s" % (start, 200)
I'm leaving the optimizations as an exercise for the reader.
If you make a note every time that one of your input intervals either opens or closes, you can do what you want by putting together the keys of opens and closes, sort into an ordered set, and you'll be able to essentially think, "okay, let's say that each adjacent pair of numbers forms an interval. Then I can focus all of my logic on these intervals as discrete chunks."
myRange = range(201)
intervals = [(1,5), (2,8), (9,12), (20,30)]
opens = {}
closes = {}
def open(index):
if index not in opens:
opens[index] = 0
opens[index] += 1
def close(index):
if index not in closes:
closes[index] = 0
closes[index] += 1
for start, end in intervals:
if end > start: # Making sure to exclude empty intervals, which can be problematic later
open(start)
close(end)
# Sort all the interval-endpoints that we really need to look at
oset = {0:None, 200:None}
for k in opens.keys():
oset[k] = None
for k in closes.keys():
oset[k] = None
relevant_indices = sorted(oset.keys())
# Find the clear ranges
state = 0
results = []
for i in range(len(relevant_indices) - 1):
start = relevant_indices[i]
end = relevant_indices[i+1]
start_state = state
if start in opens:
start_state += opens[start]
if start in closes:
start_state -= closes[start]
end_state = start_state
if end in opens:
end_state += opens[end]
if end in closes:
end_state -= closes[end]
state = end_state
if start_state == 0:
result_start = start
result_end = end
results.append((result_start, result_end))
for start, end in results:
print(str(start) + " " + str(end))
This outputs:
0 1
8 9
12 20
30 200
The intervals don't need to be sorted.
This question seems to be a duplicate of Merging intervals in Python.
If I understood well the problem, you have a list of intervals (1 5; 2 8; 9 12; 20 30) and a range (0 200), and you want to get the positions outside your intervals, but inside given range. Right?
There's a Python library that can help you on that: python-intervals (also available from PyPI using pip). Disclaimer: I'm the maintainer of that library.
Assuming you import this library as follows:
import intervals as I
It's quite easy to get your answer. Basically, you first want to create a disjunction of intervals based on the ones you provide:
inters = I.closed(1, 5) | I.closed(2, 8) | I.closed(9, 12) | I.closed(20, 30)
Then you compute the complement of these intervals, to get everything that is "outside":
compl = ~inters
Then you create the union with [0, 200], as you want to restrict the points to that interval:
print(compl & I.closed(0, 200))
This results in:
[0,1) | (8,9) | (12,20) | (30,200]

Categories