How to simplify expression with "int", modulus, and division? - algorithm

The below script calculates the time difference in decimal number, but I get a rounding error, so I guess it is because of my very bad conversion at the end.
Question
Something tells me, that the expression can be simplified, but what are the math rules in this particular situation, when I have int, modulus and division?
#!/usr/bin/perl
print f("9:00", "16:45");
sub f {
my $t1 = shift #_;
my $t2 = shift #_;
my $m1 = $1*60+$2 if $t1 =~ m/(\d\d?):(\d\d?)/;
my $m2 = $1*60+$2 if $t2 =~ m/(\d\d?):(\d\d?)/;
my $h = int(($m2-$m1)/60);
return $h + ($h*60+$m2-$m1)%60/60;
}

You have already correctly calculated $m1 and $m2 as minutes elapsed since midnight. So why not return the time difference in fractional hours as simply:
return ($m2 - $m1) / 60.0;
As far as the "math rules," maybe it will help to look at your return versus mine and see why they are equivalent (ignoring rounding):
$h + ($h * 60 + $m2 - $m1) % 60 / 60
Notice that ($h * 60) % 60 is zero, so this term essentially drops out, leaving:
$h + ($m2 - $m1) % 60 / 60
Now think about how $h was calculated: it is the quotient of ($m2 - $m1) divided by 60, dropping the remainder (because of int()). On the other hand, ($m2 - $m1) % 60 is exactly the remainder of this division. So the expression above is essentially just adding the remainder fraction, which you dropped from $h, back in. Therefore it has the same result as
($m2 - $m1) / 60

You can use Time::Piece to do simple date/time calculations:
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
my $pattern = '%H:%M';
my $start = Time::Piece->strptime('09:00', $pattern);
my $end = Time::Piece->strptime('16:45', $pattern);
my $diff = $end - $start;
print $diff->hours;
Output:
7.75
Note that $diff is actually a Time::Seconds object.

I would say to use Time::Piece, but I assume this is some sort of exercise.
In Unix, all dates are converted to seconds since the epoch. It makes it easy to do arithmetic on stuff when everything is in the same units. Therefore, I would convert the times to minutes, do your operations, then convert the time back to hours and minutes. That would mean you need to subroutines. One to convert the time from hours and minutes to minutes. And one to convert from minutes to hours and minutes.
#! /usr/bin/env perl
#
use warnings;
use strict;
use feature qw(say);
my $time1 = "9:50";
my $time2 = "16:45";
my $time1_in_minutes = convert_to_minutes( $time1 );
my $time2_in_minutes = convert_to_minutes( $time2 );
my $diff_in_minutes = $time2_in_minutes - $time1_in_minutes;
say convert_to_hours( $diff_in_minutes );
sub convert_to_minutes {
my $time = shift;
my ($hours, $minutes) = split /:/, $time;
my $total_minutes = ( $hours * 60 ) + $minutes;
return $total_minutes;
}
sub convert_to_hours {
my $minutes = shift;
my $hours = int $minutes / 60;
$minutes = $minutes % 60;
return sprintf "%d:%02d", $hours, $minutes;
}
By breaking down the work into two subroutines, you can easily see what is going on. Plus, you have a lot more flexibility. What if I gave you times it took for me to run a marathon, and I want the average time? You could use the same two routines. What if I gave you the time I spent at work each day, and I wanted a total? Again, you could use the same subroutines.
And, because it's much easier to see what's going on, it's easier to correct errors that crop up when you program. Even better, because the operations are clean, I can start adding features. For example, it would be nice to check my input. In my convert_to_minutes subroutine, I might want to make sure that the hours and minutes are valid. That there's a colon in the middle. That the minutes aren't greater than 60.

Related

work out how many seconds have expired in total during game play

~Why the hell has this had down votes.... you people are weird!
Ok so this is a very simply HTML5 and jQuery and PHP game. Sorry to the people who have answered, I forgot to say this is a php script, i have updated here to reflect.
the first level takes 1 minute. Every level after that takes an extra 10 seconds than the last level. like so;
level 1 = 60 seconds
level 2 = 70 seconds
level 3 = 80 seconds
level 4 = 90 seconds
and so on infinitely.
I need an equation that can figure out what is the total amount of seconds played based on the users level.
level = n
i started with (n * 10) + (n * 60) but soon realized that that doesn't account for the last level already being 10 seconds longer than the last. I have temporarily fixed it using a function calling a foreach loop stopping at the level number and returning the value. but i really want an actual equation.
SO i know you wont let me down :-)
Thanks in advance.
this is what i am using;
function getnumberofsecondsfromlevel($level){
$lastlevelseconds = 60;
while($counter < $level){
$totalseconds = $lastlevelseconds+$totalseconds;
$lastlevelseconds = $lastlevelseconds + 10;
$counter++;
}
return $totalseconds;
}
$level = $_SESSION['**hidden**']['thelevel'];
$totaldureationinseconds = getnumberofsecondsfromlevel($level);
but i want to replace with an actual equation
like so;(of course this is wrong, this is just the example of the format i want it in i.e an equation)
$n = $_SESSION['**hidden**']['thelevel']; (level to get total value of
in seconds)
$s = 60; (start level)
$totaldureationinseconds = ($n * 10) + ($s * $n);
SOLVED by Gopalkrishna Narayan Prabhu :-)
$totalseconds = 60 * $level + 5* (($level-1) * $level);
var total_secs = 0;
for(var i = 1; i<= n ;i++){
total_secs = total_secs + (i*10) + 50;
}
for n= 1, total_secs = 0 + 10 + 50 = 60
for n= 2, total_secs = 60 + 20 + 50 = 130
and so on...
For a single equation:
var n = level_number;
total_secs = 60 * n + 5* ((n-1) * n);
Hope this helps.
It seems as though you're justing looking for the equation
60 + ((levelN - 1) * 10)
Where levelN is the current level, starting at 1. If you make the first level 0, you can get rid of the - 1 part and make it just
60 + (levelN * 10)
Thought process:
What's the base/first number? What's the lowest it can ever be? 60. That means your equation will start with
60 + ...
Every time you increase the level, you add 10, so at some point you'll need something like levelN * 10. Then, it's just some fiddling. In those case, since you don't add any on the first left, and the first level is level 1, you just need to subtract 1 from the level number to fix that.
You can solve this with a really simple mathematical phrase (with factorial).
((n-1)! * 10) + (60 * n)
n is the level ofcourse.

Convert this Equation for Random

I want to convert this equation "( rand() % ( max-min ) ) + min;" from C++ to the TCL/TK to us it in ns2...I hope if there is anyone can help me.
My Best Regards
The expression can't be translated directly, as Tcl's rand() returns floating point numbers (in the range 0.0 to 1.0) unlike C++'s rand(), which is returning an integer. But a pretty much moral equivalent is this:
expr { int(rand() * ($max - $min)) + $min }
We can wrap this into a little procedure to make it even easier to use (it's just like writing a helper function in C++):
proc randInRange {min max} {
return [expr { int(rand() * ($max - $min)) + $min }]
}
# Example of calling it and using the result:
puts [randInRange 1 10]

Tune my code to solve a puzzle

5 monkey share n peaches, they cannot distribute equally. So the first monkey dump 1 peach, and total number of peaches can be divided by 5, and the first monkey took his part.
Then is the second monkey, -1 peach, can be divided by 5 and took his part.
Until the fifth monkey finished all the steps. There may be some peaches still left.
Give the minimum number of peaches that satisfy this condition.
perl code 1:
#!/usr/bin/perl -w
for $n (0..10000){ #this is basic idea but code is too messy !
if( ($n-1) % 5 == 0 ){
$remain = 4/5 * ($n -1 );
if( ($remain - 1) % 5 == 0){
$remain = 4/5 * ($remain -1 );
if( ($remain - 1) % 5 == 0){
$remain = 4/5 * ($remain -1 );
if( ($remain - 1) % 5 == 0){
$remain = 4/5 * ($remain -1 );
if( ($remain - 1) % 5 == 0){
$remain = 4/5 * ($remain -1 );
print "remain: $remain original: $n\n";
}
}
}
}
}
}
perl code 2:
sub doit($){
($n) = #_;
if( ($n - 1) % 5 ==0 ){ #if can be distributed by 5 monkey
$n = ($n - 1) * 4/5; #commit distribute
return $n;
}else{
return -1; #fail
}
}
for $n (0..10000){ #restriction
$r = $n; #"recursively" find solution
$o = $n; #backup n
$count = 0;
for ($i = 0; $i < 5; $i++){ #assume there is 5 monkey, it can be changed
$r = doit($r);
if($r == -1){ #skip once fail
last;
}
$count++;
}
if($count == 5){ # if pass 5 test, then you found the number !
print "now ".$r."\n";
print "origin ".$o."\n";
}
}
I am thinking to cut some code. But felt hard. Can anyone help ?
First of all, you really should use strict and warnings pragmas at the top of your scripts. Your $n usage is especially worrisome. In the future, if you declare variables with my but use the same name, you convey the fact that they will represent the same quantity, without the fear that they might collide.
Anyway here is a slightly polished, and more importantly strict and warnings safe version:
#!/usr/bin/env perl
use strict;
use warnings;
sub doit {
my ($n) = #_;
if( ($n - 1) % 5 ==0 ){ #if can be distributed by 5 monkey
$n = ($n - 1) * 4/5; #commit distribute
return $n;
} else {
return undef; #fail
}
}
OUTER: for my $n (0..10000){ #restriction
my $r = $n; #"recursively" find solution
for (1..5){ #assume there is 5 monkey, it can be changed
$r = doit($r);
next OUTER unless defined $r;
}
# if code gets here, then it passed 5 test, then you found the number !
print "now: $r\torigin: $n\n";
}
And now, if you really want to be fun with it (don't use this in production, readability first! ):
#!/usr/bin/env perl
use strict;
use warnings;
OUTER: for my $n (0..10000){
my $r = $n;
$r = ($r - 1) % 5 ? next OUTER : 4/5 * ($r - 1) for (1..5);
print "now: $r\torigin: $n\n";
}
or even golfed:
for(0..10000){$r=$n=$_;map$r*=--$r%5?next:4/5,1..5;print"now: $r\torigin: $n\n"}
Consider this solution:
sub share {
($_[0] - 1) % 5 == 0 ? ($_[0]-1)/5*4 : die "unable to share";
}
for my $i (1..10000) {
eval {
share(share(share(share(share($i)))));
};
unless ($#) {
print "solution: $i\n";
last;
}
}
I'm sure there is a monad lurking within.
I'm not 100% sure I understand your question, but instead of searching for the answer, start with the last monkey. The minimum peaches he could take is 1, and even though there could be some left, to get the minimum, assume there are 0 left. Now, calculate how many peaches the second to last monkey saw, and so on.
There is no need to loop, if you start from the last monkey
# set numPeaches to what the last monkey had
$numPeaches = 1;
# then, figure out how many the second to last monkey had, and add to numPeaches
# and, so on ...
# until you get to the first monkey

Non-linear counter

So I have a counter. It is supposed to calculate the current amount of something. To calculate this, I know the start date, and start amount, and the amount to increment the counter by each second. Easy peasy. The tricky part is that the growth is not quite linear. Every day, the increment amount increases by a set amount. I need to recreate this algorithmically - basically figure out the exact value at the current date based on the starting value, the amount incremented over time, and the amount the increment has increased over time.
My target language is Javascript, but pseudocode is fine too.
Based on AB's solution:
var now = new Date();
var startDate1 = new Date("January 1 2010");
var days1 = (now - startDate1) / 1000 / 60 / 60 / 24;
var startNumber1 = 9344747520;
var startIncrement1 = 463;
var dailyIncrementAdjustment1 = .506;
var currentIncrement = startIncrement1 + (dailyIncrementAdjustment1 * days1);
startNumber1 = startNumber1 + (days1 / 2) * (2 * startIncrement1 + (days1 - 1) * dailyIncrementAdjustment1);
Does that look reasonable to you guys?
It's a quadratic function. If t is the time passed, then it's the usual at2+bt+c, and you can figure out a,b,c by substituting the results for the first 3 seconds.
Or: use the formula for the arithmetic progression sum, where a1 is the initial increment, and d is the "set amount" you refer to. Just don't forget to add your "start amount" to what the formula gives you.
If x0 is the initial amount, d is the initial increment, and e is the "set amount" to increase the incerement, it comes to
x0 + (t/2)*(2d + (t-1)*e)
If I understand your question correctly, you have an initial value x_0, an initial increment per second of d_0 and an increment adjustment of e per day. That is, on day one the increment per second is d_0, on day two the increment per second is d_0 + e, etc.
Then, we note that the increment per second at time t is
d(t) = d_0 + floor(t / S) * e
where S is the number of seconds per day and t is the number of seconds that have elapsed since t = t_0. Then
x = x_0 + sum_{k < floor(t / S)} S * d(k) + S * (t / S - floor(t / S)) * d(t)
is the formula that you are seeking. From here, you can simplify this to
x = x_0 + S * floor(t / S) d_0 + S * e * (floor(t / S) - 1) * floor(t / S) / 2.
use strict; use warnings;
my $start = 0;
my $stop = 100;
my $current = $start;
for my $day ( 1 .. 100 ) {
$current += ($day / 10);
last unless $current < $stop;
printf "Day: %d\tLeft %.2f\n", $day, (1 - $current/$stop);
}
Output:
Day: 1 Left 1.00
Day: 2 Left 1.00
Day: 3 Left 0.99
Day: 4 Left 0.99
Day: 5 Left 0.98
...
Day: 42 Left 0.10
Day: 43 Left 0.05
Day: 44 Left 0.01

Returning Time Components with Modulus

Someone does 20 Hours 42 Minutes & 16 Seconds in one shift totaling 74536 seconds.
How do I get the hours from number of seconds the person has done for that shift?
20 * 60 * 60 = 72000
42 * 60 = 2520
16 = 16
+ -----
Total = 74536
____________________________
Total % 60 = Seconds (16)
Total % ? = Minutes (42)
Total % ? = Hours (20)
Tried 84600 already; turns out when a number is lower the modulus, it really is not very helpful, and something I am going to have to catch should someone only sign in for a few seconds ...
You need to use both modulus and division:
t = seconds_in_shift;
secs = t % 60;
t /= 60;
mins = t % 60;
t /= 60;
hour = t;
Or:
secs = ttime % 60;
mins = (ttime / 60) % 60;
hour = ttime / 3600;
One other option uses the div() function from Standard C (<stdlib.h>):
div_t v1 = div(ttime, 60);
div_t v2 = div(v1.quot, 60);
After that, v1.rem contains the seconds; v2.rem contains the minutes, and v2.quot contains the hours based on the number of seconds originally in ttime.
Based on Jonathan's reply, I believe the accurate answer should be this…
$total_time = 61000;
$sec = $total_time % 60;
$total_time = ($total_time - $sec) / 60;
$min = $total_time % 60;
$hour = ($total_time - $min) / 60;
echo "$hour hours, $min minutes and $sec seconds";
But if your server has PHP7 installed, you can use the intdiv() function. An integer division in which the remainder is discarded.
// $hour = ($total_time - $min) / 60; old code but still works
$hour = intdiv($total_time, 60); // PHP7 and above code

Resources