How to get the rightmost digit from an Integer - Ruby - ruby

I am working on a program that requires me to read in and validate a number, but in doing so I need to take a two digit integer (for example we'll say 18) and add it 1 + 8. Now I have it working where I do a conditional check if it is greater than 10 and subtract, but that's kind of an ugly solution in my opinion and was wondering if there is a cleaner way of doing it?

You can use the modulo operator n % 10, to get the rightmost digit. For example:
18 % 10
# => 8
9 % 10
# => 9
0 % 10
# => 0
(-123).abs % 10
# => 3
To handle negative numbers, use Integer#abs.
EDIT
Ruby 2.4 has a Integer#digits method. It will give you the digits as an array, starting from unit's place. And if you want to add the digits, you can use Enumerable#sum.
123.digits
# =>[3, 2, 1]
123.digits.first
# => 3
123.digits.sum
# => 6

To add all digits from a number, you can use the following:
18.to_s.chars.map(&:to_i).reduce(:+)
It transforms the number into a string, splits it into digits, transforms each into an integer and adds them all together.
Works with numbers of any length.

This is how I would do it:
any_number = 1234
# Ensure your input is at most a two digit number (might not be needed)
two_digit_number = any_number % 100 #=> 34
# Calculate the final addition
total = two_digit_number / 10 + two_digit_number % 10 #=> 7

For two digit integers, there is at least another method we could use: Numeric#divmod, which returns the quotient and modulus in an array. And here's a look at the speed of the different approaches to sum up the digits:
b = 18
n = 1_000_000
Benchmark.bmbm do |x|
x.report('to_s:') { n.times { b.to_s.chars.map(&:to_i).reduce(:+) }}
x.report('divmod:') { n.times { b.divmod(10).reduce(:+) }}
x.report('direct division:') { n.times { b/10 + b%10 } }
x.report('digits:') { n.times { a.digits.reduce(:+) } }
end
#####
user system total real
to_s: 0.750000 0.000000 0.750000 ( 0.750700)
divmod: 0.150000 0.000000 0.150000 ( 0.153516)
direct division: 0.080000 0.000000 0.080000 ( 0.076697)
digits: 0.560000 0.020000 0.580000 ( 0.574687)

Here a hands on solution for the fun of it:
def get_digits(n)
res=[]; i=0;
while 10**i < n
res.push( n/10**i%10 )
i += 1
end
return res
end

def digits_sum(number,n)
number_array=number.to_s.chars
n=[n,number_array.length].min
number_array.slice(-n,n).map {|c| c.to_i }.reduce(:+) || 0
end
this method return the sum of N right-digits.
digits_sum 123456789,5 => 35
digits_sum 123456789,2 => 17
This will work if you give N greater then number length but wont work if negative count is provided

Related

Ruby: find multiples of 3 and 5 up to n. Can't figure out what's wrong with my code. Advice based on my code please

I have been attempting the test below on codewars. I am relatively new to coding and will look for more appropriate solutions as well as asking you for feedback on my code. I have written the solution at the bottom and for the life of me cannot understand what is missing as the resultant figure is always 0. I'd very much appreciate feedback on my code for the problem and not just giving your best solution to the problem. Although both would be much appreciated. Thank you in advance!
The test posed is:
If we list all the natural numbers below 10 that are multiples of 3 or
5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Finish the solution so that it returns the sum of all the multiples of
3 or 5 below the number passed in. Additionally, if the number is
negative, return 0 (for languages that do have them).
Note: If the number is a multiple of both 3 and 5, only count it once.
My code is as follows:
def solution(number)
array = [1..number]
multiples = []
if number < 0
return 0
else
array.each { |x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
return multiples.sum
end
In a situation like this, when something in your code produces an unexpected result you should debug it, meaning, run it line by line with the same argument and see what each variable holds. Using some kind of interactive console for running code (like irb) is very helpfull.
Moving to your example, let's start from the beginning:
number = 10
array = [1..number]
puts array.size # => 1 - wait what?
puts array[0].class # => Range
As you can see the array variable doesn't contain numbers but rather a Range object. After you finish filtering the array the result is an empty array that sums to 0.
Regardless of that, Ruby has a lot of built-in methods that can help you accomplish the same problem typing fewer words, for example:
multiples_of_3_and_5 = array.select { |number| number % 3 == 0 || number % 5 == 0 }
When writing a multiline block of code, prefer the do, end syntax, for example:
array.each do |x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
end
I'm not suggesting that this is the best approach per se, but using your specific code, you could fix the MAIN problem by editing the first line of your code in one of 2 ways:
By either converting your range to an array. Something like this would do the trick:
array = (1..number).to_a
or by just using a range INSTEAD of an array like so:
range = 1..number
The latter solution inserted into your code might look like this:
number = 17
range = 1..number
multiples = []
if number < 0
return 0
else range.each{|x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
multiples.sum
#=> 60
The statement return followed by end suggests that you were writing a method, but the def statement is missing. I believe that should be
def tot_sum(number, array)
multiples = []
if number < 0
return 0
else array.each{|x|
if x % 3 == 0 || x % 5 == 0
multiples << x
end
}
end
return multiples.sum
end
As you point out, however, this double-counts numbers that are multiples of 15.
Let me suggest a more efficient way of writing that. First consider the sum of numbers that are multiples of 3 that do not exceed a given number n.
Suppose
n = 3
m = 16
then the total of numbers that are multiples of three that do not exceed 16 can be computed as follows:
3 * 1 + 3 * 2 + 3 * 3 + 3 * 4 + 3 * 5
= 3 * (1 + 2 + 3 + 4 + 5)
= 3 * 5 * (1 + 5)/2
= 45
This makes use of the fact that 5 * (1 + 5)/2 equals the sum of an algebraic series: (1 + 2 + 3 + 4 + 5).
We may write a helper method to compute this sum for any number n, with m being the number that multiples of n cannot exceed:
def tot_sum(n, m)
p = m/n
n * p * (1 + p)/2
end
For example,
tot_sum(3, 16)
#=> 45
We may now write a method that gives the desired result (remembering that we need to account for the fact that multiples of 15 are multiples of both 3 and 5):
def tot(m)
tot_sum(3, m) + tot_sum(5, m) - tot_sum(15, m)
end
tot( 9) #=> 23
tot( 16) #=> 60
tot(9999) #=> 23331668

Find all natural numbers which are multiplies of 3 and 5 recursively

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.
def multiples_of(number)
number = number.to_f - 1.0
result = 0
if (number / 5.0) == 1 || (number / 3.0) == 1
return result = result + 5.0 + 3.0
elsif (number % 3).zero? || (number % 5).zero?
result += number
multiples_of(number-1)
else
multiples_of(number-1)
end
return result
end
p multiples_of(10.0)
My code is returning 9.0 rather than 23.0.
Using Core Methods to Select & Sum from a Range
It's not entirely clear what you really want to do here. This is clearly a homework assignment, so it's probably intended to get you to think in a certain way about whatever the lesson is. If that's the case, refer to your lesson plan or ask your instructor.
That said, if you restrict the set of possible input values to integers and use iteration rather than recursion, you can trivially solve for this using Array#select on an exclusive Range, and then calling Array#sum on the intermediate result. For example:
(1...10).select { |i| i.modulo(3).zero? || i.modulo(5).zero? }.sum
#=> 23
(1...1_000).select { |i| i.modulo(3).zero? || i.modulo(5).zero? }.sum
#=> 233168
Leave off the #sum if you want to see all the selected values. In addition, you can create your own custom validator by comparing your logic to an expected result. For example:
def valid_result? range_end, checksum
(1 ... range_end).select do |i|
i.modulo(3).zero? || i.modulo(5).zero?
end.sum.eql? checksum
end
valid_result? 10, 9
#=> false
valid_result? 10, 23
#=> true
valid_result? 1_000, 233_168
#=> true
There are a number of issues with your code. Most importantly, you're making recursive calls but you aren't combining their results in any way.
Let's step through what happens with an input of 10.
You assign number = number.to_f - 1.0 which will equal 9.
Then you reach the elsif (number % 3).zero? || (number % 5).zero? condition which is true, so you call result += number and multiples_of(number-1).
However, you're discarding the return value of the recursive call and call return result no matter what. So, your recursion doesn't have any impact on the return value. And for any input besides 3 or 5 you will always return input-1 as the return value. That's why you're getting 9.
Here's an implementation which works, for comparison:
def multiples_of(number)
number -= 1
return 0 if number.zero?
if number % 5 == 0 || number % 3 == 0
number + multiples_of(number)
else
multiples_of(number)
end
end
puts multiples_of(10)
# => 23
Note that I'm calling multiples_of(number) instead of multiples_of(number - 1) because you're already decrementing the input on the function's first line. You don't need to decrement twice - that would cause you to only process every other number e.g. 9,7,5,3
explanation
to step throgh the recursion a bit to help you understand it. Let's say we have an input of 4.
We first decrement the input so number=3. Then we hits the if number % 5 == 0 || number % 3 == 0 condition so we return number + multiples_of(number).
What does multiples_of(number) return? Now we have to evaluate the next recursive call. We decrement the number so now we have number=2. We hit the else block so now we'll return multiples_of(number).
We do the same thing with the next recursive call, with number=1. This multiples_of(1). We decrement the input so now we have number=0. This matches our base case so finally we're done with recursive calls and can work up the stack to figure out what our actual return value is.
For an input of 6 it would look like so:
multiples_of(6)
\
5 + multiples_of(5)
\
multiples_of(4)
\
3 + multiples_of(3)
\
multiples_of(2)
\
multiples_of(1)
\
multiples_of(0)
\
0
The desired result can be obtained from a closed-form expression. That is, no iteration is required.
Suppose we are given a positive integer n and wish to compute the sum of all positive numbers that are multiples of 3 that do not exceed n.
1*3 + 2*3 +...+ m*3 = 3*(1 + 2 +...+ m)
where
m = n/3
1 + 2 +...+ m is the sum of an algorithmic expression, given by:
m*(1+m)/2
We therefore can write:
def tot(x,n)
m = n/x
x*m*(1+m)/2
end
For example,
tot(3,9) #=> 18 (1*3 + 2*3 + 3*3)
tot(3,11) #=> 18
tot(3,12) #=> 30 (18 + 4*3)
tot(3,17) #=> 45 (30 + 5*3)
tot(5,9) #=> 5 (1*5)
tot(5,10) #=> 15 (5 + 2*5)
tot(5,14) #=> 15
tot(5,15) #=> 30 (15 + 3*5)
The sum of numbers no larger than n that are multiple of 3's and 5's is therefore given by the following:
def sum_of_multiples(n)
tot(3,n) + tot(5,n) - tot(15,n)
end
- tot(15,n) is needed because the first two terms double-count numbers that are multiples of 15.
sum_of_multiples(9) #=> 23 (3 + 6 + 9 + 5)
sum_of_multiples(10) #=> 33 (23 + 2*5)
sum_of_multiples(11) #=> 33
sum_of_multiples(12) #=> 45 (33 + 4*3)
sum_of_multiples(14) #=> 45
sum_of_multiples(15) #=> 60 (45 + 3*5)
sum_of_multiples(29) #=> 195
sum_of_multiples(30) #=> 225
sum_of_multiples(1_000) #=> 234168
sum_of_multiples(10_000) #=> 23341668
sum_of_multiples(100_000) #=> 2333416668
sum_of_multiples(1_000_000) #=> 233334166668

Ruby: What is the speed difference between While and Until loops?

I'm learning Ruby right now. Coming from using Javascript the past couple of years, I'm familiar with the While loop. But the until loop? I've looked around but couldn't find a solid reason why one would be better than the other.
Ruby has "until" which is described as another way to phrase a problem. The way I see it, "while" iterates until false, and "until" iterates until true.
I'm sure that most of the programs I write won't really need refactoring for speed. However, I like to get into the little details sometimes.
Is there a speed difference between the two loops? Why is there an "until" syntax in Ruby? Why not just stick with "while?"
There would not be a speed difference between while and until as they mirror each other.
We'll compare a while loop with an until loop:
n = 0
puts n += 1 while n != 3
n = 0
puts n += 1 until n == 3
These will both print 1 through 3.
Here's a diff between the two disassembled human-readable instruction sequences from the Ruby VM:
## -13,7 +13,7 ##
0021 pop
0022 getlocal_OP__WC__0 2
0024 putobject 3
-0026 opt_neq <callinfo!mid:!=, argc:1, ARGS_SIMPLE>, <callcache>, <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
-0031 branchif 8
-0033 putnil
-0034 leave
+0026 opt_eq <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
+0029 branchunless 8
+0031 putnil
+0032 leave
A while loop uses a branchif for its jump, whereas the until loop used a branchunless. So, these loops simply differ in the comparison being made, which you can see by looking at how branchif and branchunless are defined:
DEFINE_INSN
branchif
(OFFSET dst)
(VALUE val)
()
{
if (RTEST(val)) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
DEFINE_INSN
branchunless
(OFFSET dst)
(VALUE val)
()
{
if (!RTEST(val)) {
RUBY_VM_CHECK_INTS(th);
JUMP(dst);
}
}
Performance between while and until should be nearly identical. Usage should be determined by readability.
Speed differences aside, it's really all about readability, which is something that Ruby prides itself on.
Let's pretend we're making a drink - which do you think reads better?
A) pour_drink until glass.full?
B) pour_drink while !glass.full?
Speed will be more influenced by your choice of comparison operator than your choice of while or until
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n != 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n == 10_000_000
end
end
user system total real
while 0.250000 0.000000 0.250000 ( 0.247949)
until 0.220000 0.000000 0.220000 ( 0.222049)
With while n != 10_000_000 vs. until n == 10_000_000, until appears to be faster.
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n < 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n == 10_000_000
end
end
user system total real
while 0.210000 0.000000 0.210000 ( 0.207265)
until 0.220000 0.000000 0.220000 ( 0.223195)
Change it to while n < 10_000_000 and now while seems to have the edge. To be fair we should give them the more equivalent while n < 10_000_000 vs. until n > 9_999_999
Benchmark.bmbm do |bm|
bm.report('while') do
n = 0
n += 1 while n < 10_000_000
end
bm.report('until') do
n = 0
n += 1 until n > 9_999_999
end
end
user system total real
while 0.200000 0.000000 0.200000 ( 0.208428)
until 0.200000 0.000000 0.200000 ( 0.206218)
Now they're almost identical. So follow Ruby's lead and gain your satisfaction from code that reads like an English sentence. But make sure you use < or > to gain that extra boost of .0000000001 seconds.

Random selection between 2 or more ranges

I'm trying to make a random number generator which will pick more "evenly" between 3 and 4 digit ranges. If I just do:
result = rand(100..9999)
I'm well aware that in the majority of cases, a 4 digit number will be selected. I want to give 3-digit numbers more chance of being selected, so I did this:
rand_3_digit_num = (100..999)
rand_4_digit_num = (1000..9999)
rand([rand_3_digit_num, rand_4_digit_num].sample)
Is there any other approach to do this? My goal is to just give 3-digit numbers bigger chance of being selected than with an ordinary rand. This problem gets even worse if I introduce 5-digit or 6-digit numbers, the chance of 3-digit or 4-digit numbers to get selected quickly diminishes.
Brute solution:
list = (100..999).to_a*10 + (1000..9999).to_a
=> [100, ..., 9999]
list.size
=> 18000
list.count { |e| e < 1000 }
=> 9000
list.count { |e| 999 < e && e < 10000 }
=> 9000
Now list.sample should give equal probabilities of 3- and 4-digits numbers.
First specify a probability distribution over ranges, say:
range_prob = { (100..999) => 0.2,
(1000..9999) => 0.5,
(10000..43562) => 0.3 }
Given these probabilities, a range can be selected at random thusly:
def select_random_range(range_prob)
rnd_cum_prob = rand
cum_prob = 0.0
range_prob.each_with_object({}) do |(rng, prob),h|
cum_prob += prob
h[rng] = cum_prob
end.find { |rng, cum_prob| rnd_cum_prob <= cum_prob }.first
end
What I have done here is construct a cumulative distribution function ("cdf") from the discrete probability density function ("pdf") range_prob. (See the graph below.) To obtain a random variate we generate a pseudo-random number between zero and one, plot that on the vertical axis, determine where a horizontal line intersects the cdf and select the associated value on the horizontal axis.
For range_prob above,
select_random_range(range_prob) #=> 10000..43562
select_random_range(range_prob) #=> 100..999
select_random_range(range_prob) #=> 1000..9999
select_random_range(range_prob) #=> 100..999
select_random_range(range_prob) #=> 10000..43562
Selecting a random value in a random range is small additional step.
rand select_random_range(range_prob) #=> 6467
rand select_random_range(range_prob) #=> 16689
rand select_random_range(range_prob) #=> 2282
rand select_random_range(range_prob) #=> 1317
rand select_random_range(range_prob) #=> 9015
See Kernel#rand.
I think your idea is good. What you want to achieve is find the uniformly random N, where N represents number of digits in the number, then find the random number of length N.
You could split that in two functions:
randomSelection(lengths):
K = A random number from the array lengths
return randomNumberForLength(K)
randomNumberForLength(K):
lower_bound = 10^K
upper_bound = 10^(K+1) - 1
return rand(lower_bound, upper_bound)
If you wanted to find a random number between 100 - 9999 giving equal probability to both 2-length and 3-length numbers, you can just call randomSelection([2,3])
It depends entirely on how you want to bias the results. For example, if you want an even chance that you'll get a three or four digit number, you can use something as simple as (pseudo-code):
def getRand():
if rand(0, 1) == 0: // assume inclusive both ends.
return rand(100, 999)
return rand(1000, 9999)
Although the fact that you're calling rand twice may stuff up distributions for truly random requirements, it's probably good enough for most purposes.
To do it in a single call which is therefore likely to preserve distribution, you can just map values:
def getRand():
num = rand(1000, 18999)
if num > 9999:
num = (num - 10000) % 900 + 100
This would generate two equal-sized groups, 1000-9999 and 10000-18999 and would map the values in the upper group to become 100-999 (hence equally likely to get a three- or four-digit number):
10000 - 10899 -> 100 - 999
10900 - 11799 -> 100 - 999
11800 - 12699 -> 100 - 999
12700 - 13599 -> 100 - 999
13600 - 14499 -> 100 - 999
14500 - 15399 -> 100 - 999
15400 - 16299 -> 100 - 999
16300 - 17199 -> 100 - 999
17200 - 18099 -> 100 - 999
18100 - 18999 -> 100 - 999
There are no doubt other ways to do it but it all depends on the desired distribution.
For the problem you described, your solution is good enough.
999 will appear 10 times more often than 1000, though. If you want a smoother transition between the ranges, you could use :
# Defines a distribution for random numbers between min and max.
# Smaller numbers have a higher probably to appear.
class BiasedGenerator
def initialize(min, max)
#range = (Math.log(min)..Math.log(max))
end
def self.digit_range(min_digit, max_digit)
new(10**(min_digit - 1), 10**max_digit - 1)
end
def rand
Math.exp(Kernel.rand(#range)).round
end
end
You just need to initialize it once :
generator = BiasedGenerator.digit_range(3, 4)
and use generator.rand as many times as you want :
random_numbers = (1..1_000_000).map do
generator.rand
end
puts 'Min :'
puts random_numbers.min
puts 'Max :'
puts random_numbers.max
puts
random_numbers.group_by { |n| n.to_s.size }.sort_by(&:first).each do |digits, numbers|
puts "#{digits} digits : #{numbers.size}"
end
it outputs :
Min :
100
Max :
9999
3 digits : 500061
4 digits : 499939
The distribution looks like this :
The green area between 100 and 999 should be almost the same as the one between 1000 and 9999.
Your generator also has this property :
For comparison, here is Kernel.rand :
With BiasedGenerator.digit_range(3, 6) :
Min :
100
Max :
999998
3 digits : 250342
4 digits : 250714
5 digits : 249814
6 digits : 249130

Is it better way to do that?

I wrote a simple script to sum all digits of positive integer input until 1 digit is left ( for example for input 12345 result is 6 because 1+2+3+4+5 = 15 and 1+5 = 6). It works but is it better way to do that? ( more correct?)
here is a code:
def sum(n)
string=n.to_s
while string.length > 1 do
result=string.chars.inject { |sum,n| sum = sum.to_i + n.to_i}
string=result.to_s
end
puts "Sum of digits is " + string
end
begin
p "please enter a positive integer number:"
number = Integer(gets.chomp)
while number<0
p "Number must be positive!Enter again:"
number = Integer(gets.chomp)
end
rescue
p "You didnt enter integer!:"
retry
end
sum(number)
According to Wikipedia, the formula is:
dr(n) = 1 + ((n − 1) mod 9)
So it boils down to:
def sum(n)
1 + (n - 1) % 9
end
To account for 0, you can add return 0 if n.zero?
You could use divmod (quotient and modulus) to calculate the digit sum without converting to / from string. Something like this should work:
def sum(number)
result = 0
while number > 0 do
number, digit = number.divmod(10)
result += digit
if number == 0 && result >= 10
number = result
result = 0
end
end
result
end
sum(12345) #=> 6
The line
number, digit = number.divmod(10)
basically strips off the last digit:
12345.divmod(10) #=> [1234, 5]
1234 becomes the new number and 5 is being added to result. If number eventually becomes zero and result is equal or greater than 10 (i.e. more than one digit), result becomes the new number (e.g. 15) and the loops starts over. If result is below 10 (i.e. one digit), the loop exits and result is returned.
Short recursive version:
def sum_of_digits(digits)
sum = digits.chars.map(&:to_i).reduce(&:+).to_s
sum.size > 1 ? sum_of_digits(sum) : sum
end
p sum_of_digits('12345') #=> "6"
Single call version:
def sum_of_digits(digits)
digits = digits.chars.map(&:to_i).reduce(&:+).to_s until digits.size == 1
return digits
end
It's looking good to me. You might do things a little more conscise like use map to turn every char into an integer.
def sum(n)
string=n.to_s
while string.length > 1 do
result = string.chars.map(&:to_i).inject(&:+)
string = result.to_s
end
puts "Sum of digits is " + string
end
You could also use .digits, so you don't have to convert the input into a string.
def digital_root(n)
while n.digits.count > 1
array = n.digits
n = array.sum
end
return n
end

Resources