Arithmetic series in Ruby - ruby

I am trying to write a function in ruby that returns "Arithmetic" when the input is an arithmetic series array(the delta between a number and the consecutive number in the array is the same across the array). Here's what I have for so far:
array = [1,3,5,7,9] #arithmetic series input example
array.each_cons(2).map {|x,y| y - x == array[2] - array[1]}
This returns the following
[true,true,true,true,true]
My goal is to write a function that identifies what kind of series the array is, if there is one at all. Here's what I am trying to build
def isarithemetic(array)
if array.each_cons(2).map {|x,y| y - x == array[2] - array[1]} == true
return "arithmetic"
I am open to new suggestions as well.

If an array arr contains integer values, those (ordered) values comprise at most one arithmetic expression. If the average interval is integer-valued (i.e.,
((arr.last-arr.first) % (arr.size-1).zero?
is true), a unique progression is given by the values of arr.first, arr.last and arr.size, namely:
avg_interval = (arr.last-arr.first)/(arr.size-1)
(arr.first..arr.last).step(avg_interval).to_a
If the average interval is not integer-valued, it cannot be an arithmetic progression, but we don't need to check that condition. That's because by using integer arithmetic to compute avg_interval, the resulting arithmetic progression cannot equal arr if the average interval is not integer-valued.
Hence, rather than check if all the intervals are equal, we could just check to see if the above arithmetic progression equals arr.
Code
def arithmetic_progression?(arr)
return true if arr.size < 2
(arr.first..arr.last).step((arr.last-arr.first)/(arr.size-1)).to_a == arr
end
Examples
arithmetic_progression?([1,3,5,7,9]) #=> true
arithmetic_progression?([1,3,5,7,8]) #=> false
In the first example:
arr = [1,3,5,7,9]
avg_interval = (arr.last-arr.first)/(arr.size-1) #=> 2
(arr.first..arr.last).step(avg_interval).to_a #=> [1, 3, 5, 7, 9]
In the second:
arr = [1,3,5,7,8]
avg_interval = (arr.last-arr.first)/(arr.size-1) #=> 1
(arr.first..arr.last).step(avg_interval).to_a #=> [1, 2, 3, 4, 5, 6, 7, 8]
You could then write, for example:
arithmetic_progression?([1,3,5,7,9]) ? "Arithmetic progression" :
"Not arithmetic progression" #=> "Arithmetic progression"
Variant
The method could probably be made slightly more efficient by inserting the following line after the first line of the method:
return false unless ((arr.last-arr.first) % (arr.size-1)).zero?

You should be able to do this using all?:
array.each_cons(2).map {|x,y| y - x == array[2] - array[1]}.all?
or perhaps
array.each_cons(2).all? {|x,y| y - x == array[2] - array[1]}

Related

Printing odd numbers in an array

I am looking to print odd numbers in an array. I have this:
numbers = []
puts "Please enter 10 numbers, one at a time."
10.times do
puts "Please enter a number"
numbers << gets.chomp.to_i
if numbers % 3 == 0
p numbers
end
numbers = numbers + 1
end
puts "Here are the numbers you selected"
p numbers
When I type in a number, I get the following:
undefined method `%' for [1]:Array
(repl):6:in `block in <main>'
Any idea as to what is happening?
Checking values modulo 3 won't work to correctly identify odd numbers. However, Ruby has a built-in method Integer#odd?. Combine this with the Array#select method, and you can quickly pick off array elements that are odd once you've read them in.
a = (1..10).to_a # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
p a.select(&:odd?) # [1, 3, 5, 7, 9]
If you insist on using the modulo operator, you would check for x % 2 == 1 to check integer x for oddness:
p a.select { |x| x % 2 == 1 } # [1, 3, 5, 7, 9] again, but less efficiently
The modulo method does not work with an array as the receiver, which is what you attempted to do. That's what the error message is telling you.
Odd number is a number that when divided by two, leaves a remainder.
So in Ruby it looks like number % 2 != 0. Why do you decide to use % 3?
In your code numbers is Array, but you can use % only to numbers.
And you don't need it because in Ruby Integer has built-in method odd?. It returns true if number is odd.
You can use method .find_all or .select to your Array. Read here.
Also you use numbers = numbers + 1. It's better to write numbers += 1, it's the same. But you don't need it when use method .times. Read here.
By the way, you don't need use .chomp if your variable will be Integer, just use .to_i
So it's better to use code like this:
numbers = []
10.times do
puts 'Enter a number'
number = gets.to_i
numbers << number
end
puts "Here your odd numbers: #{numbers.find_all(&:odd?)}"

How to 'reverse sum' in Ruby?

I have no clue how to call this in correct math-terms. Consider a method which takes two digits:
def num_of_sum(total, group_count)
end
where total is an integer and group_count is an integer.
How would I get a 'nicely' grouped Array of integers of group_count-length which sum up till total.
My spec would look like:
describe "number to sum of" do
it "grabs all numbers" do
expect(num_of_sum(10, 2)).to eq([5,5])
expect(num_of_sum(10, 3)).to eq([3,3,4])
expect(num_of_sum(20, 3)).to eq([6,7,7])
expect(num_of_sum(100, 3)).to eq([33,33,34])
expect(num_of_sum(100, 2)).to eq([50,50])
end
end
I tried this, which works:
def num_of_sum(total, in_groups_of)
result = []
section_count ||= (total.to_f / in_groups_of.to_f).round
while(total > 0)
total -= section_count
if (total - section_count) < 0 && (total + section_count).even?
section_count += total
total -= total
end
result << section_count
end
result
end
But, for instance, this spec doesn't work:
expect(num_of_sum(67,5)).to eq([13,13,13,14,14])
I need the array to contain numbers that are as close to each other as possible. But the array is limited to the length of the group_count.
Does someone know what the mathemetical name for this is, so I can search a bit more accurately?
The mathematical term for this is an integer partition
A more direct approach to this is to observe that if you do integer division (round down) of the total by the number of groups, then your sum would be short by total mod number_of_groups, so you just need to distribute that amount across the array:
def even_partition(total, number_of_groups)
quotient, remainder = total.divmod(number_of_groups)
(number_of_groups-remainder).times.collect {quotient} +
remainder.times.collect { quotient + 1}
end
def n_parts(num, groupcount)
div, mod = num.divmod(groupcount)
Array.new(groupcount-mod, div) + Array.new(mod, div+1)
end
n_parts(100,3) => [33, 33, 34]
Docs to Array.new and Fixnum.divmod
A naive implementation is like this:
Let's take example of (20, 3). You want three numbers as a result.
20 / 3 # => 6
This is your "base" value. Create an array of three sixes, [6, 6, 6]. That'll get you 18. Now you have to distribute remaining 2 as equally as possible. For example, enumerate array elements and increment each one by 1, until you have no value to distribute. Result is [7, 7, 6]. Good enough, I think.
Possible (working) implementation:
def breakdown(total, group_count)
avg_value, extra = total.divmod(group_count)
result = Array.new(group_count, avg_value)
extra.times do |i|
result[i] += 1
end
result
end
breakdown(10, 2) == [5, 5] # => true
breakdown(10, 3) == [4, 3, 3] # => true
breakdown(20, 3) # => [7, 7, 6]
I have no clue how it’s called, but here is a solution:
def num_of_sum sum, count
result = [i = sum / count] * count # prepare an array e.g. [3,3,3] for 10,3
result[sum - i * count..-1] + # these should be left intact
result[0...sum - i * count].map { |i| i + 1 } # these are ++’ed
end
Hope it helps.
Another way:
def floors_then_ceils(n, groups)
floor, ceils = n.divmod(groups)
groups.times.map { |i| (i < groups-ceils) ? floor : floor + 1 }
end
floors_then_ceils(10, 3)
#=> [3, 3, 4]
floors_then_ceils(9, 3)
#=> [3, 3, 3]
Alternatively, groups.times.map... could be replaced with:
Array.new(groups-ceils, floor).concat(Array.new(ceils, floor+1))

How do I filter elements in an array?

Sample Array:
x = [1,2,3,4,2,2,2]
Filter:
y = [2,4,7,9]
Desired output:
result = [2,4,2,2,2]
I tried:
result = (x & y)
but this gives me [4,2].
How do I get: result = [2,4,2,2,2]?
How about:
x - (x - y)
#=> [2, 4, 2, 2, 2]
1-2 lines longer than #Mark's answer, but more efficient (if both arrays are large):
require 'set'
keep = Set[2,4,7,9] # or Set.new(some_large_array)
result = x.select{ |n| keep.include?(n) } #=> [2, 4, 2, 2, 2]
The problem with writing...
x.select{ |i| y.include?(i) }
...is that this is O(x*y) the the number of elements in each array. With 100 elements in each you are doing 10,000 operations in the worst case; my answer does only 100 operations.
First, don't capitalize variables in Ruby. Capitalization is for constants, like class names.
result = x.select {|i| y.include? i}
Note that select is also called find_all, and is the positive filter in ruby; the negative filter is reject. Between the braces you can put any code you want; it will be run once for each item of x (the item is passed in as an argument and becomes i), and the result of the whole call will include all the elements for which the block returns a true value.

How do I iterate through the digits of an integer? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Turning long fixed number to array Ruby
Well, I have to iterate over the digits of a integer in Ruby. Right now I was just splitting it up into an array, and then iterating over that. However I was wondering if there was a faster way to do this?
The shortest solution probably is:
1234.to_s.chars.map(&:to_i)
#=> [1, 2, 3, 4]
A more orthodox mathematical approach:
class Integer
def digits(base: 10)
quotient, remainder = divmod(base)
quotient == 0 ? [remainder] : [*quotient.digits(base: base), remainder]
end
end
0.digits #=> [0]
1234.digits #=> [1, 2, 3, 4]
0x3f.digits(base: 16) #=> [3, 15]
You can use the old trick of modulus/divide by 10, but this won't be measurably faster unless you have huge numbers, and it will give the digits to you backwards:
i = 12345
while i > 0
digit = i % 10
i /= 10
puts digit
end
Output:
5
4
3
2
1
split=->(x, y=[]) {x < 10 ? y.unshift(x) : split.(x/10, y.unshift(x%10))}
split.(1000) #=> [1,0,0,0]
split.(1234) #=> [1,2,3,4]
Ruby has divmod, which will calculate both x%10and x/10 in one go:
class Integer
def split_digits
return [0] if zero?
res = []
quotient = self.abs #take care of negative integers
until quotient.zero? do
quotient, modulus = quotient.divmod(10) #one go!
res.unshift(modulus) #put the new value on the first place, shifting all other values
end
res # done
end
end
p 135.split_digits #=>[1, 3, 5]
For things like Project Euler, where speed is of some importance, this is nice to have. Defining it on Integer causes it to be available on Bignum too.
I like to use enumerators for this purpose:
class Integer
def digits
to_s.each_char.lazy.map(&:to_i)
end
end
This gives you access to all the good Enumerator stuff:
num = 1234567890
# use each to iterate over the digits
num.digits.each do |digit|
p digit
end
# make them into an array
p num.digits.to_a # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
# or take only some digits
p num.digits.take(5) # => [1, 2, 3, 4, 5]
# ...
Try mod by 10 (will give you the last digit), then divide by 10 (will give you the rest of digits), repeat this until you're down to the final digit. Of course, you'll have to reverse the order if you want to go through the digits from left to right.

Chunk a Ruby array according to streaks within it

Summary: The basic question here was, I've discovered, whether you can pass a code block to a Ruby array which will actually reduce the contents of that array down to another array, not to a single value (the way inject does). The short answer is "no".
I'm accepting the answer that says this. Thanks to Squeegy for a great looping strategy to get streaks out of an array.
The Challenge: To reduce an array's elements without looping through it explicitly.
The Input: All integers from -10 to 10 (except 0) ordered randomly.
The Desired Output: An array representing streaks of positive or negative numbers. For instance, a -3 represents three consecutive negative numbers. A 2 represents two consecutive positive numbers.
Sample script:
original_array = (-10..10).to_a.sort{rand(3)-1}
original_array.reject!{|i| i == 0} # remove zero
streaks = (-1..1).to_a # this is a placeholder.
# The streaks array will contain the output.
# Your code goes here, hopefully without looping through the array
puts "Original Array:"
puts original_array.join(",")
puts "Streaks:"
puts streaks.join(",")
puts "Streaks Sum:"
puts streaks.inject{|sum,n| sum + n}
Sample outputs:
Original Array:
3,-4,-6,1,-10,-5,7,-8,9,-3,-7,8,10,4,2,5,-2,6,-1,-9
Streaks:
1,-2,1,-2,1,-1,1,-2,5,-1,1,-2
Streaks Sum:
0
Original Array:
-10,-9,-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8,9,10
Streaks:
-10,10
Streaks Sum:
0
Note a few things:
The streaks array has alternating positive and negative values.
The sum of the elements streaks array is always 0 (as is the sum of the original).
The sum of the absolute values of the streak array is always 20.
Hope that's clear!
Edit: I do realize that such constructs as reject! are actually looping through the array in the background. I'm not excluding looping because I'm a mean person. Just looking to learn about the language. If explicit iteration is necessary, that's fine.
Well, here's a one-line version, if that pleases you more:
streaks = original_array.inject([]) {|a,x| (a.empty? || x * a[-1] < 0 ? a << 0 : a)[-1] += x <=> 0; a}
And if even inject is too loopy for you, here's a really silly way:
streaks = eval "[#{original_array.join(",").gsub(/((\-\d+,?)+|(\d+,?)+)/) {($1[0..0] == "-" ? "-" : "") + $1.split(/,/).size.to_s + ","}}]"
But I think it's pretty clear that you're better off with something much more straightforward:
streaks = []
original_array.each do |x|
xsign = (x <=> 0)
if streaks.empty? || x * streaks[-1] < 0
streaks << xsign
else
streaks[-1] += xsign
end
end
In addition to being much easier to understand and maintain, the "loop" version runs in about two-thirds the time of the inject version, and about a sixth of the time of the eval/regexp one.
PS: Here's one more potentially interesting version:
a = [[]]
original_array.each do |x|
a << [] if x * (a[-1][-1] || 0) < 0
a[-1] << x
end
streaks = a.map {|aa| (aa.first <=> 0) * aa.size}
This uses two passes, first building an array of streak arrays, then converting the array of arrays to an array of signed sizes. In Ruby 1.8.5, this is actually slightly faster than the inject version above (though in Ruby 1.9 it's a little slower), but the boring loop is still the fastest.
new_array = original_array.dup
<Squeegy's answer, using new_array>
Ta da! No looping through the original array. Although inside dup it's a MEMCPY, which I suppose might be considered a loop at the assembler level?
http://www.ruby-doc.org/doxygen/1.8.4/array_8c-source.html
EDIT: ;)
original_array.each do |num|
if streaks.size == 0
streaks << num
else
if !((streaks[-1] > 0) ^ (num > 0))
streaks[-1] += 1
else
streaks << (num > 0 ? 1 : -1)
end
end
end
The magic here is the ^ xor operator.
true ^ false #=> true
true ^ true #=> false
false ^ false #=> false
So if the last number in the array is on the same side of zero as the number being processed, then add it to the streak, otherwise add it to the streaks array to start a new streak. Note that sine true ^ true returns false we have to negate the whole expression.
Since Ruby 1.9 there's a much simpler way to solve this problem:
original_array.chunk{|x| x <=> 0 }.map{|a,b| a * b.size }
Enumerable.chunk will group all consecutive elements of an array together by the output of a block:
>> original_array.chunk{|x| x <=> 0 }
=> [[1, [3]], [-1, [-4, -6]], [1, [1]], [-1, [-10, -5]], [1, [7]], [-1, [-8]], [1, [9]], [-1, [-3, -7]], [1, [8, 10, 4, 2, 5]], [-1, [-2]], [1, [6]], [-1, [-1, -9]]]
This is almost exactly what OP asks for, except the resulting groups need to be counted up to get the final streaks array.
More string abuse, a la Glenn McDonald, only different:
runs = original_array.map do |e|
if e < 0
'-'
else
'+'
end
end.join.scan(/-+|\++/).map do |t|
"#{t[0..0]}#{t.length}".to_i
end
p original_array
p runs
# => [2, 6, -4, 9, -8, -3, 1, 10, 5, -7, -1, 8, 7, -2, 4, 3, -5, -9, -10, -6]
# => [2, -1, 1, -2, 3, -2, 2, -1, 2, -4]

Resources