secret
Last active

Chris Strom Solution

  • Download Gist
chrisstrom.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
=begin
My solution includes comments to aid in understanding, but I should probably add an overall approach description as well.
 
The problem is solvable without geometry, but not quite as generalizable. Regardless, some extension to the Float and String class will be needed (to convert back and forth between time strings and floats for averaging).
 
== Expected output
 
list = %w{23:15 00:03 23:30 23:23 23:48}
average_time_of_day(list)
=> "23:35"
 
list = %w{06:15 07:03 06:30 06:23 06:48}
average_time_of_day(list)
=> "06:35"
 
== Hints
 
Your digital ways will not help you, time of day is cyclical.
 
== Note
There is a newsgroup that actually discusses this problem and offers some solutions that are very close to being complete. I'd rather not give folks hints that point them to that discussion. Like I said, it is possible to come up with a close approximation of the solution without geometry. Besides, I am somewhat hopeful that someone can surprise me by coming up with an elegant solution without geometry :)
=end
 
class Float
def to_time_string
hours = self.to_i
"%02d:%02d" % [hours, (self-hours)*60]
end
end
 
class String
# Given a time string (e.g. "23:15"), return a numeric
# representation (e.g. 23.25)
def to_time_float
hours, minutes = self.split(":")
(hours.to_i + minutes.to_f/60)
end
end
 
class GeometricTime
# Given a list of 24-hour times (e.g. ["23:50", "00:00", "00:00"]),
# returns the average (e.g. "00:00")
def self.average(list)
vector_sum = list.inject([0,0]) do |memo, time|
x, y = x_y_from_time(time)
[memo[0] + x, memo[1] + y]
end
time_from_x_y(*vector_sum)
end
 
# Convert time on a 24 hour analog clock to x-y coordinates. Align
# midnight with the x axis for easier conversion.
# * midnight => [1, 0]
# * "06:00" => [0, -1]
# * "12:00" => [-1, 0]
# * "18:00" => [0, 1]
def self.x_y_from_time(time)
# negative because angles increase counter-clockwise
radians = - 2 * Math::PI * time.to_time_float / 24
[Math.cos(radians), Math.sin(radians)]
end
 
# Convert from x-y coordinates back to a time string (e.g. [1, 0] =>
# "00:00"
def self.time_from_x_y(x, y)
atan2 = Math.atan2(y, x)
angle = atan2 < 0 ? atan2 + 2*Math::PI : atan2
time = ((2 * Math::PI - angle) / (2*Math::PI)) * 24
time.to_time_string
end
end

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.