I have a string as follows:
"00:48:22"
From right to left, I am working with a power of 60, because I want to get the total number of seconds from hours, minutes, seconds.
This is what I have tried:
clock
=> "00:48:22"
i = 0
result = clock.split(":").reverse.reduce(0) do |acc, segment|
acc += segment.to_i + (60 ** i)
i += 1
acc
end
=> 3731
The result is off. It should be 2902. Any idea what I am doing wrong?
Your algorithm is a little messy and error prone.
This will give you the right answer:
clock = '00:48:22'
clock.split(':').map(&:to_i).reduce(0) do |acc, segment|
acc * 60 + segment
end
You are adding where you should be multiplying
acc += segment.to_i * (60 ** i)
require 'date'
d = Date.today
(Time.new(d.year,d.month,d.day,*str.split(':').map(&:to_i))-d.to_time).to_i
#=> 2902
Related
I am trying to do Ruby codewars challenge and I am stuck since I pass sample tests but can't pass final one. I am getting error Expected: [8, 597], instead got: [8, 563].
Instructions :
A man has a rather old car being worth $2000. He saw a secondhand car
being worth $8000. He wants to keep his old car until he can buy the
secondhand one.
He thinks he can save $1000 each month but the prices of his old car
and of the new one decrease of 1.5 percent per month. Furthermore the
percent of loss increases by a fixed 0.5 percent at the end of every
two months.
Example of percents lost per month:
If, for example, at the end of first month the percent of loss is 1,
end of second month percent of loss is 1.5, end of third month still
1.5, end of 4th month 2 and so on ...
Can you help him? Our man finds it difficult to make all these
calculations.
How many months will it take him to save up enough money to buy the
car he wants, and how much money will he have left over?
def nbMonths(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth)
months = 0
leftover = 0
currentSavings = 0
until (currentSavings + startPriceOld) >= (startPriceNew)
months += 1
months.even? ? percentLossByMonth = percentLossByMonth + 0.5 : percentLossByMonth
startPriceNew = startPriceNew * (1 - (percentLossByMonth/100))
startPriceOld = startPriceOld * (1 - (percentLossByMonth/100))
currentSavings = currentSavings + savingperMonth
end
leftover = currentSavings + startPriceOld - startPriceNew
return [months, leftover.abs.to_i]
end
I don't want to look at solutions and I don't need one here just a nudge in the right direction would be very helpful.
Also, I get that code is probably sub-optimal in a lot of ways but I have started coding 2 weeks ago so doing the best I can.
Tnx guys
Your algorithm is good. But you have two coding errors:
1) percentLossByMonth needs to be converted to float before dividing it by 100 ( 5 / 100 = 0 while (5.to_f) / 100 = 0.05 )
2) It's said in the instructions that you need to return the nearest integer of the leftover, which is leftover.round
def nbMonths(startPriceOld, startPriceNew, savingperMonth, percentLossByMonth)
months = 0
leftover = 0
currentSavings = 0
until (currentSavings + startPriceOld) >= (startPriceNew)
months += 1
percentLossByMonth += months.even? ? 0.5 : 0
startPriceNew = startPriceNew * (1 - (percentLossByMonth.to_f/100))
startPriceOld = startPriceOld * (1 - (percentLossByMonth.to_f/100))
currentSavings += savingperMonth
end
leftover = currentSavings + startPriceOld - startPriceNew
return [months, leftover.round]
end
The problem with your code has been identified, so I will just offer an alternative calculation.
r = 0.015
net_cost = 8000-2000
n = 1
months, left_over = loop do
r += 0.005 if n.even?
net_cost *= (1-r)
tot = n*1000 - net_cost
puts "n=#{n}, r=#{r}, net_cost=#{net_cost.to_i}, " +
"savings=#{(n*1000).to_i}, deficit=#{-tot.to_i}"
break [n, tot] if tot >= 0
n += 1
end
#=> [6, 766.15...]
months
#=> 6
left_over
#=> 766.15...
and prints
n=1, r=0.015, net_cost=5910, savings=1000, deficit=4910
n=2, r=0.020, net_cost=5791, savings=2000, deficit=3791
n=3, r=0.020, net_cost=5675, savings=3000, deficit=2675
n=4, r=0.025, net_cost=5534, savings=4000, deficit=1534
n=5, r=0.025, net_cost=5395, savings=5000, deficit=395
n=6, r=0.030, net_cost=5233, savings=6000, deficit=-766
I have the following program which should count the number of years it should take a population to grow to the desired size. Whenever I run this I get an infinite loop. Can someone help me identify my error?
def pop_growth(start, percent, desired)
year_count = 0
while start <= desired
year_count += 1
start = start + (start * (percent / 100))
end
return year_count
end
I'm sure that you are trying with Integers (instead floats), so you are losing precision try this
def pop_growth(start, percent, desired)
year_count = 0
while start <= desired
year_count += 1
start = start + (start * (percent.to_f / 100))
end
return year_count
end
and let me know if it works for you. if not can you send me your start, percent and desired values?
The proper answer is given by Horacio, let me rewrite this in idiomatic ruby:
def pop_growth start, percent, desired
(0..Float::INFINITY).inject(start) do |memo, years|
break years if memo > desired
memo *= (1.0 + percent / 100.0)
end
end
or, with infinite loop:
def pop_growth start, percent, desired
loop.each_with_object(years: 0, count: start) do |_, memo|
break memo[:years] if memo[:count] > desired
memo[:years] += 1
memo[:count] *= (1.0 + percent / 100.0)
end
end
Three ways.
#1 Solve equation
Solve desired = start * (1.0 + 0.01 * percent)**n for n:
def pop_growth(start, percent, desired)
Math.log(desired.to_f/start)/Math.log(1.0 + percent/100.0)
end
years = pop_growth(100, 10, 200)
#=> 7.272540897341713
years.ceil #=> 8 if desired.
#2 Compound until desire met
def pop_growth(start, percent, desired)
return 0 if start >= desired
alpha = 1.0 + 0.01 * percent
1.step.find { (start *= alpha) >= desired }
end
pop_growth 100, 10, 200
#=> 8
#3 Use recursion
def pop_growth(start, percent, desired, years=0)
return years if start >= desired
pop_growth(start*(1.0+0.01*percent), percent, desired, years+1)
end
pop_growth 100, 10, 200
#=> 8
Just add .to_f method to percent or divide by 100.0, which will convert the integer into float.
start + (start * (percent / 100))
When you are dividing, you need at least one float number in order to return the exact division answer, else Ruby will round it down to nearest whole number, which in this case percent / 100 will result in 0, assuming that the value in percent is less than 100. This will cause this statement start + (start * (percent / 100)) to become start = start + 0, which is why you are seeing the infinite loop.
I'm trying to solve this problem:
Two clocks, which show the time in hours and minutes using the 24 hour clock, are running at different
speeds. Each clock is an exact number of minutes per hour fast. Both clocks start showing the same time
(00:00) and are checked regularly every hour (starting after one hour) according to an accurate timekeeper.
What time will the two clocks show on the first occasion when they are checked and show the same time?
NB: For this question we only care about the clocks matching when they are checked.
For example, suppose the first clock runs 1 minute fast (per hour) and the second clock runs 31 minutes
fast (per hour).
• When the clocks are first checked after one hour, the first clock will show 01:01 and the second clock
will show 01:31;
• When the clocks are checked after two hours, they will show 02:02 and 03:02;
• After 48 hours the clocks will both show 00:48.
Here is my code:
def add_delay(min,hash)
hash[:minutes] = (hash[:minutes] + min)
if hash[:minutes] > 59
hash[:minutes] %= 60
if min < 60
add_hour(hash)
end
end
hash[:hour] += (min / 60)
hash
end
def add_hour(hash)
hash[:hour] += 1
if hash[:hour] > 23
hash[:hour] %= 24
end
hash
end
def compare(hash1,hash2)
(hash1[:hour] == hash2[:hour]) && (hash1[:minutes] == hash2[:minutes])
end
#-------------------------------------------------------------------
first_clock = Integer(gets) rescue nil
second_clock = Integer(gets) rescue nil
#hash1 = if first_clock < 60 then {:hour => 1,:minutes => first_clock} else {:hour => 1 + (first_clock/60),:minutes => (first_clock%60)} end
#hash2 = if second_clock < 60 then {:hour => 1,:minutes => second_clock} else {:hour => 1 + (second_clock/60),:minutes => (second_clock%60)} end
hash1 = {:hour => 0, :minutes => 0}
hash2 = {:hour => 0, :minutes => 0}
begin
hash1 = add_hour(hash1)
hash1 = add_delay(first_clock,hash1)
hash2 = add_hour(hash2)
p hash2.to_s
hash2 = add_delay(second_clock,hash2)
p hash2.to_s
end while !compare(hash1,hash2)
#making sure print is good
if hash1[:hour] > 9
if hash1[:minutes] > 9
puts hash1[:hour].to_s + ":" + hash1[:minutes].to_s
else
puts hash1[:hour].to_s + ":0" + hash1[:minutes].to_s
end
else
if hash1[:minutes] > 9
puts "0" + hash1[:hour].to_s + ":" + hash1[:minutes].to_s
else
puts "0" + hash1[:hour].to_s + ":0" + hash1[:minutes].to_s
end
end
#-------------------------------------------------------------------
For 1 and 31 the code runs as expected. For anything bigger, such as 5 and 100, it seems to get into an infinite loop and I don't see where the bug is. What is going wrong?
The logic in your add_delay function is flawed.
def add_delay(min,hash)
hash[:minutes] = (hash[:minutes] + min)
if hash[:minutes] > 59
hash[:minutes] %= 60
if min < 60
add_hour(hash)
end
end
hash[:hour] += (min / 60)
hash
end
If hash[:minutes] is greater than 60, you should increment the hour no matter what. Observe that an increment less than 60 can cause the minutes to overflow.
Also, you may have to increment the hour more than once if the increment exceeds 60 minutes.
Finally, it is wrong to do hash[:hour] += (min / 60) because min is not necessarily over 60 and because you have already done add_hour(hash).
Here is a corrected version of the function:
def add_delay(minutes, time)
time[:minutes] += minutes
while time[:minutes] > 59 # If the minutes overflow,
time[:minutes] -= 60 # subtract 60 minutes and
add_hour(time) # increment the hour.
end # Repeat as necessary.
time
end
You can plug this function into your existing code. I have merely taken the liberty of renaming min to minutes and hash to time inside the function.
Your code
Let's look at your code and at the same time make some small improvements.
add_delay takes a given number of minutes to add to the hash, after converting the number of minutes to hours and minutes and then the number of hours to the number of hours within a day. One problem is that if a clock gains more than 59 minutes per hour, you may have to increment hours by more than one. Try writing it and add_hours like this:
def add_delay(min_to_add, hash)
mins = hash[:minutes] + min_to_add
hrs, mins = mins.divmod 60
hash[:minutes] = mins
add_hours(hash, hrs)
end
def add_hours(hash, hours=1)
hash[:hours] = (hash[:hours] + hours) % 24
end
We do not necessarily care what either of these methods returns, as they modify the argument hash.
This uses the very handy method Fixnum#divmod to convert minutes to hours and minutes.
(Aside: some Rubiests don't use hash as the name of a variable because it is also the name of a Ruby method.)
Next, compare determines if two hashes with keys :hour and :minutes are equal. Rather than checking if both the hours and minutes match, you can just see if the hashes are equal:
def compare(hash1, hash2)
hash1 == hash2
end
Get the minutes per hour by which the clocks are fast:
first_clock = Integer(gets) rescue nil
second_clock = Integer(gets) rescue nil
and now initialize the hashes and step by hour until a match is found, then return either hash:
def find_matching_time(first_clock, second_clock)
hash1 = {:hours => 0, :minutes => 0}
hash2 = {:hours => 0, :minutes => 0}
begin
add_delay(first_clock, hash1)
add_hours(hash1)
add_delay(second_clock, hash2)
add_hours(hash2)
end until compare(hash1, hash2)
hash1
end
Let's try it:
find_matching_time(1, 31)
# => {:hours=>0, :minutes=>48}
find_matching_time(5, 100)
#=> {:hours=>0, :minutes=>0}
find_matching_time(5, 5)
#=> {:hours=>1, :minutes=>5}
find_matching_time(0, 59)
#=> {:hours=>0, :minutes=>0}
These results match those I obtained below with an alternative method. You do not return the number hours from the present until the times are the same, but you may not need that.
I have not identified why you were getting the infinite loop, but perhaps with this analysis you will be able to find it.
There are two other small changes I would suggest: 1) incorporating add_hours in add_delay and renaming the latter, and 2) getting rid of compare because it so simple and only used in one place:
def add_hour_and_delay(min_to_add, hash)
mins = hash[:minutes] + min_to_add
hrs, mins = mins.divmod 60
hash[:minutes] = mins
hash[:hours] = (hash[:hours] + 1 + hrs) % 24
end
def find_matching_time(first_clock, second_clock)
hash1 = {:hours => 0, :minutes => 0}
hash2 = {:hours => 0, :minutes => 0}
begin
add_hour_and_delay(first_clock, hash1)
add_hour_and_delay(second_clock, hash2)
end until hash1 == hash2
hash1
end
Alternative method
Here's anther way to write the method. Let:
f0: minutes per hour the first clock is fast
f1: minutes per hour the second clock is fast
Then we can compute the next time they will show the same time as follows.
Code
MINS_PER_DAY = (24*60)
def find_matching_time(f0, f1)
elapsed_hours = (1..Float::INFINITY).find { |i|
(i*(60+f0)) % MINS_PER_DAY == (i*(60+f1)) % MINS_PER_DAY }
[elapsed_hours, "%d:%02d" % ((elapsed_hours*(60+f0)) % MINS_PER_DAY).divmod(60)]
end
Examples
find_matching_time(1, 31)
#=> [48, "0:48"]
After 48 hours both clocks will show a time of "0:48".
find_matching_time(5, 100)
#=> [288, "0:00"]
find_matching_time(5, 5)
#=> [1, "1:05"]
find_matching_time(0, 59)
#=> [1440, "0:00"]
Explanation
After i hours have elapsed, the two clocks will respectively display a time that is the following number of minutes within a day:
(i*(60+f0)) % MINS_PER_DAY # clock 0
(i*(60+f1)) % MINS_PER_DAY # clock 1
Enumerable#find is then used to determine the first number of elapsed hours i when these two values are equal. We don't know how long that may take, so I've enumerated over all positive integers beginning with 1. (I guess it could be no more than 59 hours, so I could have written (1..n).find.. where n is any integer greater than 58.) The value returned by find is assigned to the variable elapsed_hours.
Both clocks will display the same time after elapsed_hours, so we can compute the time either clock will show. I've chosen to do that for clock 0. For the first example (f0=1, f1=31)
elapsed_hours #=> 48
so
mins_clock0_advances = elapsed_hours*(60+1)
#=> 2928
mins_clock_advances_within_day = mins_clock0_advances % MINS_PER_DAY
#=> 48
We then convert this to hours and minutes:
mins_clock_advances_within_day.divmod(60)
#=> [0, 48]
which we can then the method String#% to format this result appropriately:
"%d:%02d" % mins_clock_advances_within_day.divmod(60)
#=> "0:48"
See Kernel#sprintf for information on formatting when using %. In "%02d", d is for "decimal", 2 is the field width and 0 means pad left with zeroes.
Basically I am trying to solve a 2nd order differential equation with the forward euler method. I have some for loops inside my code, which take considerable time to solve and I would like to speed things up a bit. Does anyone have any suggestions how could I do this?
And also when looking at the time it takes, I notice that my end at line 14 takes 45 % of my total time. What is end actually doing and why is it taking so much time?
Here is my simplified code:
t = 0:0.01:100;
dt = t(2)-t(1);
B = 3.5 * t;
F0 = 2 * t;
BB=zeros(1,length(t)); % Preallocation
x = 2; % Initial value
u = 0; % Initial value
for ii = 1:length(t)
for kk = 1:ii
BB(ii) = BB(ii) + B(kk) * u(ii-kk+1)*dt; % This line takes the most time
end % This end takes 45% of the other time
x(ii+1) = x(ii) + dt*u(ii);
u(ii+1) = u(ii) + dt * (F0(ii) - BB(ii));
end
Running the code it takes me 8.552 sec.
You can remove the inner loop, I think:
for ii = 1:length(t)
for kk = 1:ii
BB(ii) = BB(ii) + B(kk) * u(ii-kk+1)*dt; % This line takes the most time
end % This end takes 45% of the other time
x(ii+1) = x(ii) + dt*u(ii);
u(ii+1) = u(ii) + dt * (F0(ii) - BB(ii));
end
So BB(ii) = BB(ii) (zero at initalisation) + sum for 1 to ii of BB(kk)* u(ii-kk+1).dt
but kk = 1:ii, so for a given ii, ii-kk+1 → ii-(1:ii) + 1 → ii:-1:1
So I think this is equivalent to:
for ii = 1:length(t)
BB(ii) = sum(B(1:ii).*u(ii:-1:1)*dt);
x(ii+1) = x(ii) + dt*u(ii);
u(ii+1) = u(ii) + dt * (F0(ii) - BB(ii));
end
It doesn't take as long as 8 seconds for me using either method, but the version with only one loop is about 2x as fast (the output of BB appears to be the same).
Is the sum loop of B(kk) * u(ii-kk+1) just conv(B(1:ii),u(1:ii),'same')
The best way to speed up loops in matlab is to try to avoid them. Try if you are able to perform a matrix operation instead of the inner loop. For example try to break the calculation you do there in small parts, then decide, if there are parts you can perform in advance without knowing the results of the next iteration of the loop.
to your secound part of the question, my guess:: The end contains the check if the loop runs for another round and this check by it self is not that long but called 50.015.001 times!
I have the following method for doing a check digit on a tracking number, but it just feels lengthy/sloppy. Can it be refactored and just generally cleaned up?
I'm running Ruby 1.8.7.
def is_fedex(number)
n = number.reverse[0..14]
check_digit = n.first.to_i
even_numbers = n[1..1].to_i + n[3..3].to_i + n[5..5].to_i + n[7..7].to_i + n[9..9].to_i + n[11..11].to_i + n[13..13].to_i
even_numbers = even_numbers * 3
odd_numbers = n[2..2].to_i + n[4..4].to_i + n[6..6].to_i + n[8..8].to_i + n[10..10].to_i + n[12..12].to_i + n[14..14].to_i
total = even_numbers + odd_numbers
multiple_of_ten = total + 10 - (total % 10)
remainder = multiple_of_ten - total
if remainder == check_digit
true
else
false
end
end
EDIT: Here are valid and invalid numbers.
Valid: 9612019950078574025848
Invalid: 9612019950078574025847
def is_fedex(number)
total = (7..20).inject(0) {|sum, i| sum + number[i..i].to_i * ( i.odd? ? 1 : 3 ) }
number[-1].to_i == (total / 10.0).ceil * 10 - total
end
I believe you should keep your code. While it's not idiomatic or clever, it's the one you will have the least trouble to understand a few months from now.
I'm not a ruby programmer, so if any of the syntax is off, I apologize but you should get the general idea. A few things I see: First, you don't need to slice the array, a single index should be sufficient. Second, Instead of splitting even and odd, you could do something like this:
total = 0
for i in (1..14)
total += n[i].to_i * ( i % 2 == 1 ? 1 : 3 )
end
Third, remainder could be simplified to 10 - (total % 10).
I realize you're running 1.8.7, but here's my attempt using each_slice and inject in conjunction, a 1.9.2 feature:
def is_fedex(number)
total = number.reverse[1..14].split(//).map(&:to_i).each_slice(2).inject(0) do |t, (e,o)|
t += e*3 + o
end
10 - (total % 10) == number[-1].to_i
end
It passes both tests
Give this a try:
#assuming number comes in as a string
def is_fedex(number)
n = number.reverse[0..14].scan(/./)
check_digit = n[0].to_i
total = 0
n[1..14].each_with_index {|d,i| total += d.to_i * (i.even? ? 3 : 1) }
check_digit == 10 - (total % 10)
end
> is_fedex("12345678901231") => true
edit incorporating simplified remainder logic as Kevin suggested
Something like this?
def is_fedex(number)
even_arr, odd_arr = number.to_s[1..13].split(//).map(&:to_i).partition.with_index { |n, i| i.even? }
total = even_arr.inject(:+) * 3 + odd_arr.inject(:+)
number.to_s.reverse[0..0].to_i == (total + 10 - (total % 10)) - total
end
If you can give me a valid and invalid number I can test if it works and maybe tweak it further :)
This function should to:
def is_fedex(number)
# sanity check
return false unless number.length == 15
data = number[0..13].reverse
check_digit = number[14..14].to_i
total = (0..data.length-1).inject(0) do |total, i|
total += data[i..i].to_i * 3**((i+1)%2)
end
(10 - total % 10) == check_digit
end
The arithmetic expression 3**((i+1)%2) might look a bit complex, but is essentially the same as (i.odd? ? 1 : 3). Both variants are correct, which you use is up to you (and might affect speed...)
Also note, that if you use Ruby 1.9, you can use data[i] instead of data[i..i] which is required for for Ruby 1.8.