|
@@ -0,0 +1,116 @@
|
|
1
|
+# Lily Carpenter
|
|
2
|
+# lily-redditchallenge@azrazalea.net
|
|
3
|
+# 2049 South Florence Apt. 8
|
|
4
|
+# Springfield, MO
|
|
5
|
+# 417-379-3326
|
|
6
|
+#
|
|
7
|
+# Premise:
|
|
8
|
+# While I'm sure there is a fancy algorithm to do this very efficiently, I have not gotten far enough into
|
|
9
|
+# math/computer science theory in order to be able to come up with it effectively.
|
|
10
|
+#
|
|
11
|
+# Therefore, here is how my script works:
|
|
12
|
+# 1. Sorts all panels in descending order based on value
|
|
13
|
+# 2. Starting with the person with the highest yield (me) and going from there
|
|
14
|
+# it adds the highest value panels to the set panel list until we reach or surpass goal
|
|
15
|
+# 3. If we managed to hit goal exactly, we are done. Otherwise, start second process
|
|
16
|
+# 4. In the second process we resort data in ascending order and try until we reach or surpass goal
|
|
17
|
+#
|
|
18
|
+# This should theoretically result in using the lower yield people the least, as well as getting as close as possible
|
|
19
|
+# to the goal without going over.
|
|
20
|
+
|
|
21
|
+import argparse
|
|
22
|
+import sys
|
|
23
|
+import locale
|
|
24
|
+
|
|
25
|
+# Setup command line arguments
|
|
26
|
+# I did goal and days partly just because I wanted to play with the values
|
|
27
|
+parser = argparse.ArgumentParser(description='Determine panel distribution')
|
|
28
|
+parser.add_argument('file_name', metavar='<File Name>', type=str, help='file containing panel data')
|
|
29
|
+parser.add_argument('-g', '--goal', metavar='<Goal>', type=int, help='Goal we are trying to reach (default 250000)', default=250000)
|
|
30
|
+parser.add_argument('-d', '--days', metavar='<Days>', type=int, help='Number of days to reach goal (default 7)', default=7)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+# A list of dictionaries containing the necessary data on each person
|
|
34
|
+# Also stores the set of panels to be assigned to each person
|
|
35
|
+# This is pulled from the problem description, of course
|
|
36
|
+people = [
|
|
37
|
+ {'name': 'M', 'yield': 1, 'pan_a_day': 1, 'panels': [] },
|
|
38
|
+ {'name': 'GM', 'yield': .95, 'pan_a_day': 2, 'panels': [] },
|
|
39
|
+ {'name': '?', 'yield': .85, 'pan_a_day': 3, 'panels': [] },
|
|
40
|
+ {'name': 'GB', 'yield': .6, 'pan_a_day': 4, 'panels': [] },
|
|
41
|
+ ]
|
|
42
|
+
|
|
43
|
+# Simple function, simply loads data from file_name and converts it to int, puts in list data, and returns data
|
|
44
|
+# The data list contains tuples of (index, value) to preserve original index later when sorting
|
|
45
|
+# If it gets something that isn't a number it'll tell you about it, make that entry 0, then continue like nothing happened
|
|
46
|
+def get_data(file_name):
|
|
47
|
+ data = []
|
|
48
|
+ with open(file_name, 'r') as _file:
|
|
49
|
+ for line_num, line in enumerate(_file, 1):
|
|
50
|
+ try:
|
|
51
|
+ line = int(line)
|
|
52
|
+ except ValueError:
|
|
53
|
+ sys.stderr.write('Skipping invalid data at line: ' + str(line_num) + '\n')
|
|
54
|
+ # This is so that we don't miss a panel index
|
|
55
|
+ data.append(0)
|
|
56
|
+ else:
|
|
57
|
+ data.append((line_num - 1, int(line)))
|
|
58
|
+ return data
|
|
59
|
+
|
|
60
|
+# Grab the data and arguments
|
|
61
|
+args = parser.parse_args()
|
|
62
|
+data = get_data(args.file_name)
|
|
63
|
+days = args.days
|
|
64
|
+goal = args.goal
|
|
65
|
+
|
|
66
|
+# Grab replacement cost of panels
|
|
67
|
+replace_cost = data.pop(0)[1]
|
|
68
|
+
|
|
69
|
+# Sort by the 'value' column, descending, to get list ready for processing
|
|
70
|
+data.sort(key=lambda datem: datem[1], reverse=True)
|
|
71
|
+
|
|
72
|
+current_money = 0
|
|
73
|
+panel = 0
|
|
74
|
+# This loop goes through each person, people is already setup to be descending by yield
|
|
75
|
+for person in people:
|
|
76
|
+ for x in range(0, person['pan_a_day'] * days):
|
|
77
|
+ # This is how much the current panel is worth, taking into account yield and
|
|
78
|
+ # replacement cost
|
|
79
|
+ additional_amt = data[panel][1] * person['yield'] - replace_cost
|
|
80
|
+
|
|
81
|
+ # If the next panel in list does not meet or go past the goal, add to person's panel set
|
|
82
|
+ # and increment current_money by additional_amt
|
|
83
|
+ if current_money + additional_amt <= goal:
|
|
84
|
+ current_money += additional_amt
|
|
85
|
+ person['panels'].append((data[panel][0], data[panel][1]))
|
|
86
|
+ panel += 1
|
|
87
|
+ # Otherwise, we need to switch gears and look for the smallest possible panel to finish
|
|
88
|
+ else:
|
|
89
|
+ data.sort(key=lambda datem: datem[1])
|
|
90
|
+
|
|
91
|
+ # Have to have this up here in case there was a perfect match
|
|
92
|
+ if current_money >= goal:
|
|
93
|
+ break;
|
|
94
|
+
|
|
95
|
+ for y in range(0, len(data) - panel):
|
|
96
|
+ additional_amt = data[y][1] * person['yield'] - replace_cost
|
|
97
|
+ if current_money + additional_amt >= goal:
|
|
98
|
+ current_money += additional_amt
|
|
99
|
+ person['panels'].append((data[y][0], data[y][1]))
|
|
100
|
+ break;
|
|
101
|
+
|
|
102
|
+ if current_money >= goal:
|
|
103
|
+ break;
|
|
104
|
+
|
|
105
|
+# Print the output, all pretty like
|
|
106
|
+for person in people:
|
|
107
|
+ print ' '.join([person['name'], '=', repr(person['panels'])])
|
|
108
|
+
|
|
109
|
+# Add up how much money is left
|
|
110
|
+money_left = 0
|
|
111
|
+for x in range(panel, len(data)):
|
|
112
|
+ money_left += data[x][1]
|
|
113
|
+
|
|
114
|
+# Print out how much money is left
|
|
115
|
+locale.setlocale(locale.LC_ALL, '')
|
|
116
|
+print 'Total cash left in bannana stand: $' + format(money_left, "n")
|