Beginner Ruby- If/else question, test failing for the final parameter - ruby

I am doing some online exercises, and am stuck as the last test keeps failing.
The task involves creating a program to calculate the number of cars produced by a factory given the factory's speed. The rate per hour is equal to the speed of the factory (between 1-10) * 221. However, the speed effects the quality of the cars produced exponentially, so discounts are made depending on the speed, which were provided in the specification.
The code below is used to calculate how many whole and working cars can be produced per minute by a factory of a given speed between 1-10.
I have used the .floor method to ensure the answer is rounded down to the nearest whole number. All the tests are passing, except for when I enter 10. In this case, the code returns 27, not 28.
class AssemblyLine
def initialize(speed)
#speed = speed
end
def working_items_per_minute
rate = #speed * 221 / 60
if #speed <= 4
rate = rate
elsif #speed <= 8
rate = rate * 0.9
elsif #speed == 9
rate = rate * 0.8
else
rate = rate * 0.77
end
return rate.floor
end
end
Any ideas on why it is returning 27 would be appreciated! (221*10/60 *0.77 should be rounded down to 28!)

The problem you're having is because ruby will deal in whole numbers until you introduce a Float.
It's easy to see what's going on if you launch a repl like irb or pry:
$ pry
pry(main)> 2210 / 60
=> 36
pry(main)> 2210 / 60.0
=> 36.833333333333336
pry(main)> 60.class
=> Integer
pry(main)> 60.0.class
=> Float

Related

Generating a series of non-random numbers with a maximum sum of 100

I am working on a program that has a component that will generate simulated demographic numbers for various hypothetical jurisdictions.
The methods I have set up to generate each subset of demographics are dependent on some other variables, but generally, most jurisdictions are supposed to look something like:
White - 65%
Latino - 20%
African-American - 10%
Other - 5%
Of course, this isn't always the case. In some scenarios, white may be well under 50% with either Latino or AA being the most significant, but those are more edge cases. But in general that's usually about the balance.
So I am trying to figure out how to generate each demographic, which again is fed from different variables, mostly independently, but ensuring the number always adds up to 100.
I had thought about generating white first, since it's typically the largest, and then just creating a generator where Latino% = 100 - white%*.35 (.35 is just an example here), and so forth, but this creates a situation in which white would always be the plurality, which I don't necessarily want to happen.
So I am a bit stuck. I imagine this is as much a math problem as a Ruby problem. As a non-math person (who, as they have delved into programming, wishes they had paid better attention in class), I'd appreciate any guidance here.
Thank you!
First specify a cumulative distribution function (CDF).
DIST = { white: 0.65, latino: 0.85, aa: 0.95, other: 1.00 }
Note that
DIST[:white] - 0 #=> 0.65
DIST[:latino] - DIST[:white] #=> 0.20
DIST[:aa] - DIST[:latino] #=> 0.10
DIST[:other] - DIST[:aa] #=> 0.05
Now create a method to (pseudo-) randomly select one person from the population and return their ethnicity.
def select_one
rn = rand
DIST.find { |_k, v| rn <= v }.first
end
Try it.
10.times { p select_one }
:white
:aa
:latino
:white
:white
:white
:white
:white
:white
:latino
Now write a method to return a random sample of size n.
def draw_sample(n)
n.times.with_object(Hash.new(0)) { |_, h| h[select_one] += 1 }
end
Try it.
10.times { p draw_sample(100) }
{:white=>66, :latino=>21, :aa=>9, :other=>4}
{:white=>72, :latino=>14, :aa=>11, :other=>3}
{:white=>61, :latino=>19, :aa=>14, :other=>6}
{:white=>64, :latino=>25, :aa=>8, :other=>3}
{:white=>69, :latino=>19, :aa=>4, :other=>8}
{:white=>68, :latino=>17, :aa=>9, :other=>6}
{:white=>68, :latino=>16, :aa=>12 :other=>4}
{:white=>51, :latino=>27, :aa=>10, :other=>12}
{:white=>69, :latino=>23, :aa=>6, :other=>2}
{:white=>63, :latino=>19, :aa=>14, :other=>4}
(Note the order of the keys above varied; I reordered them to improve readability.)
On could alternatively write
def draw_sample(n)
n.times.map { select_one }.tally
end
though this has the disadvantage that it creates an intermediate array.
See Kernel#rand, the form of Hash::new that takes an argument (the default value, here zero) and Enumerable#tally.
From what I understand, each demographic depends on some external variables. What you could do then is
whites = getWhites(); // could be anything really
aa = getAA();
latinos = getLatinos();
sum = whites + aa + latinos;
whites = whites / sum * 100;
aa = aa / sum * 100;
latinos = latinos / sum * 100;
This guarantees that they always sum up to 100
Edit: The code is pseudocode (not ruby), assuming floating point data type

How to find lowest fixed balance in decimals using bisection in Ruby

How do I find the LOWEST FIXED monthly payment in 2 dp to pay off the balance with compounded interest for 12 months using bisection in ruby?
Disclaimer: I am applying upperBound and unpaidBalance formula correctly.
Test case:
find_fixed_payment(320000, 0.2) should return 29157.09
But my current method returns the middle value of 29591.87 which is way more than enough as the fixed payment (need to reduce to the lowest).
Where did my code went wrong as I am not sure how to do bisection (im using binary search)?
def find_fixed_payment(balance,annualInterestRate)
lowerBound = balance/12
upperBound = (balance * (1 + annualInterestRate/12)**12) / 12.0
unpaidBalance = balance
while true
middle = (lowerBound + upperBound )/2
#use middle as the monthly payment at first
(1..12).each do |i|
unpaidBalance = (unpaidBalance - middle) +
annualInterestRate / 12 * (unpaidBalance - middle)
end
temp = unpaidBalance.floor
if temp < 0
middle -= 0.01
upperBound = middle
# should go to for loop to reduce unpaid balance
# with new middle but not happening, why?
else
middle += 0.01
lowerBound = middle
end
if temp == 0
return middle
end
if upperBound == lower+1
return -1
end
return middle.round(2)
end
end
You made a lot of errors, I'm assuming you were transcribing this method from a reading somewhere, and simply failed to understand the process it was describing.
Basically you initialize all your starting variables, then you need the create the loop. Once in the loop, each time through you're calculating, you'll need to reset the balance back to 320,000 or it becomes a bit of a moving target and we'll never get the answer you're looking for.
I used the times method on the integer 12 to simulate the 12 month cycle, in your attempt you used a range and called the each method.
Now, I'm not sure why you were trying to adjust the value of middle like that, but it was a moot point because once it looped back to the beginning of your while true loop, it just went back to the original middle value. In my version I changed the upper or lower bound appropriately. Think of it like a number guessing of 1-10, you're smart enough to know you just pick the middle every time until you get it, and you would start with a lower bound of 1 and upper of 10, then guess 5. If I told you higher, you now know that 5 is your lower and 10 is still your upper.
Note: I hope this helps, and welcome other users feel free to critique and refactor my code, I only started ruby recently.
def find_fixed_payment(balance,annualInterestRate)
monthlyInterestRate = annualInterestRate / 12
lowerBound = balance/12
upperBound = (balance * (1 + monthlyInterestRate)**12) / 12.0
unpaidBalance = balance
accuracy = 0.01
while unpaidBalance.abs > accuracy
balance = unpaidBalance
middle = (lowerBound + upperBound) / 2
12.times do
newUnpaidBalance = unpaidBalance - middle
monthInterest = monthlyInterestRate * newUnpaidBalance
unpaidBalance = newUnpaidBalance + monthInterest
end
if unpaidBalance < 0
upperBound = middle
unpaidBalance = balance
elsif unpaidBalance > accuracy
lowerBound = middle
unpaidBalance = balance
end
end
middle.round(2)
end
find_fixed_payment(320000, 0.2)

A loop with many iterations makes execution incredibly slow

Execution time for this code is around 1 second:
start_time = Time.now
prev = 1
(1..1000).each do |i|
(1..10000).each do |j|
result = j * prev
result = result + prev
result = result - prev
result = result / prev
prev = j
end
end
end_time = Time.now
printf('%f sec', end_time - start_time)
But when I use one loop with 10000000 iterations (instead of 2 loops, 1000 and 10000 iterations as written above), it becames much slower (around 4.5 seconds):
start_time = Time.now
prev = 1
(1..10000000).each do |j|
result = j * prev
result = result + prev
result = result - prev
result = result / prev
prev = j
end
end_time = Time.now
printf('%f sec', end_time - start_time)
Why is it happening? The total iterations number is still the same.
The second example processes much larger numbers than the first one (as #Sergii K commented above). It is possible that the second sample code reaches the maximum Fixnum limit on your system. On a 32-bit system, the maximum signed integer is 2**(32-1) - 1 = 2147483647 which is much less than the maximum product j * prev in the second example (as opposed to max products in the first example). In a situation like this ruby has to convert the Fixnums to Bignums internally which is why the second sample code may be slower than the first one.
On a 64-bit system I would expect both samples to run approximately the same time because the biggest integers will never reach the Fixnum limit. That is why perhaps most other commenters did not see a big difference in the timings.
Update: if the max Fixnum number is only 1073741823, as commented by the OP above, then it must mean that while the OS itself is 64-bits and perhaps the installed ruby is also a 64-bit ruby, it still uses only 4 bytes to store Fixnum numbers (instead of 8 in truly 64-bit rubies). The max integer value is much less then needed in the second example so it indeed has to convert the higher numbers to Bignums and that is where the slowness of the second sample comes from.
You can check this yourself if you compare:
(2**(0.size * 8 -2) -1).class # => Fixnum vs:
(2**(0.size * 8 -2) -1 + 1).class # => should be Bignum

Difference in times is returning a negative number

I have this trial timer code to time euler solutions in Ruby.
$RUNS = 12
def run(solve)
times = []
$RUNS.times do
start_t = Time.now.usec
solve.call
end_t = Time.now.usec
times << (end_t - start_t)/1000.0
end
#times = times.delete_if {|i| i < 0}
puts times.inspect
times.sort
mean = times.inject{|a,c| a+c} / $RUNS
puts("Mean:\t#{mean}");
if (times.length % 2 == 0) then
median = (times[times.length / 2 - 1] + times[times.length / 2]) / 2.0
else
median = times[times.length / 2];
end
puts("Median: #{median}");
end
Unfortunately, I keep getting answers like this:
[409.805, 418.16, -582.23, 402.223, -581.94, 413.196, 426.816, -584.732, 519.457, -569.557, 558.918, -579.176]
What can I do to avoid these strange negative numbers?
usec returns the microseconds from the time in the same was as month returns the month. It is not the number of microseconds for the given time since the epoch.
So if start_t was 1049896564.259970 seconds and end_t was 1049896592.123130 seconds then you would get 123130 - 259970 if you subtracted the usecs. i.e. a negative number.
Instead you could use Time.now.to_f to convert to floating point number of seconds since epoch and subtract those from each other. You can also just subtract one Time object from another directly e.g.
start_t = Time.now
solve.call
end_t = Time.now
times << end_t - start_t
Current time in seconds since the Epoch:
Time.now.to_f
=> 1278631398.143
That should have microsecond resolution, despite only three decimal places being shown here.

Optimizing this "Boundarize" method for Numerics in Ruby

I'm extending Numerics with a method I call "Boundarize" for lack of better name; I'm sure there are actually real names for this. But its basic purpose is to reset a given point to be within a boundary.
That is, "wrapping" a point around the boundary; if the area is betweeon 0 and 100, if the point goes to -1:
-1.boundarize(0,100) # => 99
(going one too far to the negative "wraps" the point around to one from the max).
102.boundarize(0,100) # => 2
It's a very simple function to implement; when the number is below the minimum, simply add (max-min) until it's in the boundary. If the number is above the maximum, simply subtract (max-min) until it's in the boundary.
One thing I also need to account for is that, there are cases where I don't want to include the minimum in the range, and cases where I don't want to include the maximum in the range. This is specified as an argument.
However, I fear that my current implementation is horribly, terribly, grossly inefficient. And because every time something moves on the screen, it has to re-run this, this is one of the bottlenecks of my application. Anyone have any ideas?
module Boundarizer
def boundarize min=0,max=1,allow_min=true,allow_max=false
raise "Improper boundaries #{min}/#{max}" if min >= max
raise "Cannot have two closed endpoints" if not (allow_min or allow_max)
new_num = self
if allow_min
while new_num < min
new_num += (max-min)
end
else
while new_num <= min
new_num += (max-min)
end
end
if allow_max
while new_num > max
new_num -= (max-min)
end
else
while new_num >= max
new_num -= (max-min)
end
end
return new_num
end
end
class Numeric
include Boundarizer
end
0.boundarize(10,50) # => 40
10.boundarize(0,10) # => 0 (the maximum is by default not allowed)
0.boundarize(0,20,false) # => 20 (the minimum is not allowed)
It looks to me like all you need is modulo (% operator) with a couple of extra checks to handle allow_min and allow_max:
irb(main):002:0> -1 % 100
=> 99
irb(main):003:0> -101 % 100
=> 99
irb(main):004:0> 102 % 100
=> 2
your operation is pretty much the modulo operation see here.
Ruby provides the operator % for that.

Resources