On Oct 30, 2006, at 1:30 PM, Tom P. wrote:
and, if the new offset would cause the time to retrogress, I just
leave the reported time unchanged. The offset will still tend to
be positive, however, and more positive the shorter the interval
between updates. I’m interested to see if anyone came up with a
good way to get a balanced distribution of errors over a long run.
I implemented a third method for updating the offset that produces a
relatively balanced distribution of errors, and added some statistics-
gathering to my driver script. Here are some comparisons of the
three update methods, tested by running 100000 60-second ‘advances’
with each method. The numbers represent the percentage of the time
that each error was found. (The ‘error’, again, is the reported time
minus the actual time, in minutes.)
Method 1
- Choose offset uniformly from the set of values that prevent the
reported time from regressing.
-5 -4 -3 -2 -1 0 1 2 3 4 5
0.0 0.0 0.0 0.0 0.1 0.3 1.6 6.2 18.4 36.5 36.9
Method 2
- Choose offset uniformly from the set of all allowable offsets, but
don’t change the reported time if the chosen offset would cause it to
retrogress.
-5 -4 -3 -2 -1 0 1 2 3 4 5
0.0 0.1 0.7 2.2 5.5 10.2 15.7 19.7 20.3 16.6 9.0
Method 3
- Changes offset randomly by +/- one minute on each update, but don’t
change the reported time if the chosen offset would cause it to
retrogress.
-5 -4 -3 -2 -1 0 1 2 3 4 5
9.2 9.4 9.6 9.3 9.1 8.8 9.1 9.0 8.8 8.8 9.0
I’ve attached the updated scripts. The driver has number of new
options (see the usage message in the script).
To generate the statistics shown above, you’d say, for instance.
fuzzyclock -t -q -n 100000 -m 1
To see how the offset changes, you could say
fuzzyclock -d -m 2
Hopefully it’s reasonably self-explanatory.
Tom
On 10/30/06, James Edward G. II [email protected] wrote:
You could shorten that up a bit, though I’m not sure this is as
readable:
@display = @actual.send(%w[+ -][rand(2)], 5 * 60)
I usually do something like @display = @actual + (560)(1 - rand(2)*2)
martin
On Oct 31, 2006, at 1:05 AM, Tom P. wrote:
Here are some comparisons of the three update methods, tested by
running 100000 60-second ‘advances’ with each method. The numbers
represent the percentage of the time that each error was found.
(The ‘error’, again, is the reported time minus the actual time, in
minutes.)
Out of curiosity, I tried translating Cameron’s method into my code.
(This mainly involved working in units of minutes, rather than
seconds). I found it produces the following distribution,
Cameron’s method
- Choose offset uniformly from the set [-5…1], , but don’t change
the reported time if the chosen offset would cause it to retrogress.
-5 -4 -3 -2 -1 0 1 2 3 4 5
0.0 1.2 7.2 18.1 28.4 28.3 16.7 0.0 0.0 0.0 0.0
The results are reasonably balanced, centered more-or-less
symmetrically around minus a half-minute.
Also, in this scheme, the reported time can /never/ be more than one
minute ahead of the actual time.
I wanted to do this with other people’s entries, too, but am out of
time for now…
Cheers,
Tom
Here’s a unit test that does a similar test against my class. It’s a
quick and dirty add on to a previous test, so the code may be
confusing. Anyway it collects the data about the difference among the
time shown by to_s method and the actual time in minutes.
To do this 0 digits are hidden so the distance can be calculated.
There’s a condition to avoid collecting the distance when the hour is
about to change, so the distance is collected in any moments but not
when the printed time or the actual time are in 54 - 59 minute
This is a sample output
-5 -> 0.19824880224682
-4 -> 3.91069363479739
-3 -> 7.90635104198626
-2 -> 11.1160935545538
-1 -> 13.5352009629228
0 -> 14.8497793302023
1 -> 14.4580019352859
2 -> 13.0572797432206
3 -> 10.5945103962616
4 -> 7.28800358735928
5 -> 3.0858370111633
and another one
-5 -> 1.43872994873491
-4 -> 5.60017954593777
-3 -> 9.32693897800562
-2 -> 12.1252569160623
-1 -> 14.1238866970635
0 -> 14.9412932032413
1 -> 14.0482884074748
2 -> 12.1028136738406
3 -> 9.37891280209785
4 -> 5.53875593564696
5 -> 1.37494389189445
both with 100000 iterations.
results are in general more centerd on the positive part of the
offset, like the first one.
I think the trend to go on the positive side is due to the fact in my
code I’m moving (increasing or decreasing) my offset at a constant
speed of 1 unit per each second, but when this speed is composed with
the natural speed of the time, the overall movement of the time I
display is more fast going in the positive direction.
Basicly I’m walking at 2 secs every seconds when I walk on the
positive direction while I just stop the time when I go down. This
makes positive offsets more easy to reach and more hard to leave,
while the opposite happens for negative offsets. Well, maybe… I’m
not 100% sure 
Here’s the code to run the test
require ‘fuzzy_time’
require ‘test/unit’
class TestFuzzyTime < Test::Unit::TestCase
def test_in_range
@bore_level = 100000
@ft = FuzzyTime.new(Time.new, :hidden_digits => 0)
diff_coll = Hash.new(0)
@bore_level.times do
previous = @ft.to_s
@ft.advance(60)
actual = @ft.actual.strftime(’%H%M’).to_i
printed = @ft.to_s.sub(’:’, ‘’).to_i
difference = actual - printed
#puts “actual #{actual} printed #{printed} difference
#{difference}”
unless printed.to_s =~ /\d5[4-9]/ || actual.to_s =~ /\d5[4-9]/
diff_coll[difference] += 1
raise “actual #{actual.to_s} previous #{printed} difference
#{difference}” if difference > 5
assert (difference.abs <= 5)
end
end
total = diff_coll.values.inject(0) {|sum, value| sum + value}
(-5…5).each {|num| puts [num,
(diff_coll[num])*100/total.to_f].join(’ -> ')}
end
end
Tom P. wrote:
0.0 1.2 7.2 18.1 28.4 28.3 16.7 0.0 0.0 0.0 0.0
That part of my code is kind of obsfuscated, but what it actually does
is choose an offset uniformly from the set [-20…5], but don’t change
the reported time if the chosen offset would cause it to retrogress.
This would be roughly equivalent to saying: do not choose a new offset
60% of the time. Otherwise, choose from the set [-5…+5] but don’t
retrogress below the low-water-mark.
The results are reasonably balanced, centered more-or-less
symmetrically around minus a half-minute.
Also, in this scheme, the reported time can /never/ be more than one
minute ahead of the actual time.
I got the following if I keep track of the difference between fuzzy
time and actual time:
-5 -> 2.122
-4 -> 5.981
-3 -> 9.366
-2 -> 11.655
-1 -> 12.912
0 -> 12.757
1 -> 11.935
2 -> 10.175
3 -> 7.987
4 -> 5.873
5 -> 9.237
Method 3
- Changes offset randomly by +/- one minute on each update, but
don’t change the reported time if the chosen offset would cause it
to retrogress.
-5 -4 -3 -2 -1 0 1 2 3 4 5
9.2 9.4 9.6 9.3 9.1 8.8 9.1 9.0 8.8 8.8 9.0
I like that. Simple, with good results.
Cheers!
-Cameron
On Nov 1, 2006, at 5:08 AM, Paolo N. wrote:
any generated target at any update rate, and, in theory, moving the
offset this way is possible to avoid the check against going back with
the time.
That’s interesting. The insight I had, after trying my first two
methods, was that to get the distribution to be balanced, the
distribution for each random change had to be balanced as well.
However, since you can’t reduce the offset arbitrarily without
worrying that the clock will regress, you need to restrict positive
changes in the offset to be no greater than the greatest negative
offset you can choose. Because your method adjusts the offset
gradually to reach a randomly chosen target, you can choose the
target freely from [-5…5] (minutes), whereas I’m restricted to
[-1…1]. You end up with Gaussian-like distribution, rather than a
flat distribution, because the steps you take between targets end up
being part of the distribution as well.
Unlike yours, my method (as currently implemented) only works as
intended when the time is updated once a minute. I should really be
adjusting the range over which the offset is selected based on the
step size. Your method works equally well regardless of the step size.
Tom
On Nov 1, 2006, at 12:05 AM, Cameron Pope wrote:
is choose an offset uniformly from the set [-20…5], but don’t change
the reported time if the chosen offset would cause it to retrogress.
This would be roughly equivalent to saying: do not choose a new offset
60% of the time. Otherwise, choose from the set [-5…+5] but don’t
retrogress below the low-water-mark.
Yes, I see that now. (Thanks for the correction.) I think it’s an
elegant way to deal with the problem, and one that I would probably
never have thought of, myself.
Cheers,
Tom
Method 3
- Changes offset randomly by +/- one minute on each update, but
don’t change the reported time if the chosen offset would cause it
to retrogress.
-5 -4 -3 -2 -1 0 1 2 3 4 5
9.2 9.4 9.6 9.3 9.1 8.8 9.1 9.0 8.8 8.8 9.0
I choose my offset through a dedicated class (WanderingWalker) that
works always in seconds.
Basicly I start from the current offset (or position) and I genereta a
target to reach (by default a random number between -300 + 300) after
that I begin moving my offset of 1 unit per every consumed second in
the direction of the target. So in one second my offset moves of +/- 1
second, in one minute moves of +/- 60 seconds and so on.
When I reach the given target offset I just generate another target to
reach.
I choosed this approach because it forces the clock to reach smoothly
any generated target at any update rate, and, in theory, moving the
offset this way is possible to avoid the check against going back with
the time.
But since I’m lazy I didn’t tested/verified my theory so I had to keep
this check in my code.
cheers
Paolo