How to find max negative number and min positive number in array - ruby

Are there some methods that can find max negative number and min positive number in array?
Array contains no zeros
[-1,5,-4,6,1,8,-3].max_negative # => -1
[-1,5,-4,6,1,8,-3].min_positive # => 1

I don't think there's a built in solution. But you can achieve that very simply.
[-1,5,-4,6,1,8,-3].select{|n| n < 0}.max # => -1
[-1,5,-4,6,1,8,-3].select{|n| n >= 0}.min # => 1
You can even wrap them in a method if you want, maybe in array class.

[-1,5,-4,6,1,8,-3].select(&:negative?).max
[-1,5,-4,6,1,8,-3].select(&:positive?).min

neg_max, pos_min = [-1,5,-4,6,1,8,-3].minmax_by{|el| 1.0/el}

Here is one more way to do this - partition the array into positive and negative sub-arrays and then find max/min from each of those two arrays.
positives, negatives = arr.partition(&:positive?)
p positives.min
#=> 1
p negatives.max
#=> -1
Alternatively, you could do as below, where array is sorted and a pair is found where first element of pair is negative and second element is positive, thus giving us the values of max negative and min positive values.
max_neg, min_pos = arr.sort.each_slice(2)
.select {|i, j| i.negative? and j.positive?}.flatten

Related

Sort Integer Array Ruby

Have the function PermutationStep (num) take the num parameter being passed and return the next number greater than num using the same digits. For example: if num is 123 return 132, if it's 12453 return 12534. If a number has no greater permutations, return -1 (ie. 999)
Here's my code. I'd like to sort an array of large integers in numerical order. Using the regular sort method doesn't give the right order for some numbers. Is there a sort_by structure that I can replace 'sort' with in my code below?
def PermutationStep(num)
num = num.to_s.split('').map {|i| i.to_i}
permutations = num.permutation.to_a.sort #<= I want to sort by numerical value here
permutations.each_with_index do |n, idx|
if n == num
if n == permutations[-1]
return -1
else
return permutations[idx+1].join.to_i
end
end
end
end
For example, 11121. When I run the code it gives me 11121.I want the next highest permutation, which should be 12111.
Also, when I try { |a,b| b <=> a }, I also get errors.
You can pass a block to sort.
num.permutation.to_a.sort { |x, y| x.to_i <=> y.to_i }
This SO thread may be of some assistance: How does Array#sort work when a block is passed?
num.permutation.to_a is an array of arrays, not an array of integers, which causes the result not what you expected.
Actually you don't need to sort since you only need the minimum integer that is bigger than the input.
def PermutationStep(num)
nums = num.to_s.split('')
permutations = nums.permutation.map{|a| a.join.to_i}
permutations.keep_if{|n| n > num}.min || -1
end
puts PermutationStep(11121) # 11211
puts PermutationStep(999) # -1
Call to_i before your sort the permutations. Once that is done, sort the array an pick the first element greater than your number:
def PermutationStep(num)
numbers = num.to_s.split('')
permutations = numbers.permutation.map { |p| p.join.to_i }.sort
permutations.detect { |p| p > num } || -1
end
You don't need to consider permutations of digits to obtain the next higher number.
Consider the number 126531.
Going from right to left, we look for the first decrease in the digits. That would be 2 < 6. Clearly we cannot obtain a higher number by permuting only the digits after the 2, but we can obtain a higher number merely by swapping 2 and 6. This will not be the next higher number, however.
We therefore look for the smallest digit to the right of 2 that is greater than 2, which would be 3. Clearly, the next higher number will begin 13 and will have the remaining digits ordered smallest to largest. Therefore, the next higher number will be 131256.
You can easily see that the next higher number for 123 is 132, and for 12453 is 12534.
The proof that procedure is correct is easily established by induction, first showing that it is correct for numbers with two digits, then assuming it is correct for numbers with n>=2 digits, showing it is correct for numbers with n+1 digits.
It can be easily implemented in code:
def next_highest(n)
a = n.to_s.reverse.split('').map(&:to_i)
last = -Float::INFINITY
x,ndx = a.each_with_index.find { |d,i| res = d<last; last=d; res }
return nil unless x
swap_val = a[ndx]
swap_ndx = (0...ndx).select { |i| a[i] > swap_val }.min_by{ |i| a[i] }
a[ndx], a[swap_ndx] = a[swap_ndx], swap_val
a[0...ndx] = a[0...ndx].sort.reverse
a.join.reverse
end
next_highest(126531) #=> "131256"
next_highest(109876543210) #=> "110023456789"

Ruby - See if a multiple of a number has the exact same digits as the original number (re-arranged)

I have a number, and would like to see if I multiply the number by a real number, if the new number has the exact same digits as the previous number, only re-arranged. For example, if I wanted to multiply a number by 2 and see if the digits remained the same, I would do
125874
=> 251748
251748 is 125874 multiplied by 2 and both numbers have the exact same digits, only re-arranged. For simplicity, I'm only testing it with multiples of 2 for now. This is what I tried to do and failed.
x = 125874
array = x.to_s.chars.map{|x|x.to_i}
=> [1,2,5,8,7,4]
array.permutation.include?((x * 2).to_s.chars.map{|x|x.to_i}
=> true
Now, I tried to run this in a loop to find all numbers under 100,000 that met this criteria.
range = (1..100000).to_a
range.select do |x|
array = x.to_s.chars.map{|x|x.to_i}
array.permutation.include?((x * 2).to_s.chars.map{|x|x.to_i}
end
=> []
Now, it should have recorded at least 125874 in that array, since 125874 * 2 equals 251748, which is a permutation of 125874.
I think I managed to out-confuse myself on this one.
First assume that if the given number contains repeated digits, we require that the number and product of the number and a multiplier contain the same number of each digit that appears in either number:
def same_digits?(nbr, mult)
nbr.to_s.chars.sort == (nbr * mult).to_s.chars.sort
end
same_digits?(125874,2) #=> true (125874*2 => 251748)
same_digits?(125874,3) #=> false (125874*3 => 377622)
If nbr and nbr*prod must contain the same digits, but not necessarily the same number of each of those digits, the method differs only slightly:
def same_digits?(nbr, mult)
nbr.to_s.chars.uniq.sort == (nbr * mult).to_s.chars.uniq.sort
end
same_digits?(10255,2) #=> true (10255*2 => 20510)
same_digits?(10255,3) #=> false (10255*3 => 30765)
In this second case there are many other ways to determine if two arrays contain the same elements after duplicates have been removed. Let:
a = nbr.to_s.chars.uniq
b = (nbr*mult).to_s.chars.uniq
Above I've used a.sort == b.sort to check for a match. Here are a few of other ways:
(a&b)==a && (a&b)==b # Array intersection
(a-b).empty? && (b-a).empty? # Array difference
require 'set'
a.to_set == b.to_set

Find element(s) closest to average of array

What would be a 'ruby' way to do the following; I'm still thinking in more imperative style programming and not really adapting to thinking in ruby. What I want to do is find the closest element in size to the average of an array, for example, consider the following array
[1,2,3]
The average is 2.0. The method I want to write returns the element closest to the average from above and below it, in this case 1 and 3.
Another example will illustrate this better:
[10,20,50,33,22] avg is 27.0 method would return 22 and 33.
This is not the most efficient, but it is (in my humble opinion) rather Ruby-esque.
class Array
# Return the single element in the array closest to the average value
def closest_to_average
avg = inject(0.0,:+) / length
min_by{ |v| (v-avg).abs }
end
end
[1,2,3].closest_to_average
#=> 2
[10,20,50,33,22].closest_to_average
#=> 22
If you really want the n closest items, then:
class Array
# Return a number of elements in the array closest to the average value
def closest_to_average(results=1)
avg = inject(0.0,:+) / length
sort_by{ |v| (v-avg).abs }[0,results]
end
end
[10,20,50,33,22].closest_to_average #=> [22]
[10,20,50,33,22].closest_to_average(2) #=> [22, 33]
[10,20,50,33,22].closest_to_average(3) #=> [22, 33, 20]
How this Works
avg = inject(0.0,:+) / length
is shorthand for:
avg = self.inject(0.0){ |sum,n| sum+n } / self.length
I start off with a value of 0.0 instead of 0 to ensure that the sum will be a floating point number, so that dividing by the length does not give me an integer-rounded value.
sort_by{ |v| (v-avg).abs }
sorts the array based on the difference between the number and average (lowest to highest), and then:
[0,results]
selects the first results number of entries from that array.
I assume that what is desired is the largest element of the array that is smaller than the average and the smallest value of the array that is larger than the average. Such values exist if and only if the array has at least two elements and they are not all the same. Assuming that condition applies, we need only convert it from words to symbols:
avg = a.reduce(:+)/a.size.to_f
[ a.select { |e| e < avg }.max, a.select { |e| e > avg }.min ]
Another way, somewhat less efficient:
avg = a.reduce(:+)/a.size.to_f
b = (a + [avg]).uniq.sort
i = b.index(avg)
[ b[i-1], b[i+1] ]

Median of an array in Ruby not floating

OK folks...I could use some help on getting the median of an array in ruby
Here is my code:
def median(array)
array.sort! # sort the array
elements = array.count # count the elements in the array
center = elements/2 # find the center of the array
elements.even? ? (array[center] + array[center+1])/2 : array[center] # if elements are even take both the center numbers of array and divide in half, if odd...get the center number
end
Just not certain where to apply the .to_f since it wont return anything needing a float.
Thanks
I realized you've solved your own problem already, but this version is a little cleaner and safer:
def median(array)
raise ArgumentError, 'Cannot find the median on an empty array' if array.size == 0
sorted = array.sort
midpoint, remainder = sorted.length.divmod(2)
if remainder == 0 # even count, average middle two
sorted[midpoint-1,2].inject(:+) / 2.0
else
sorted[midpoint]
end
end
Use enumerable-statistics.
https://github.com/mrkn/enumerable-statistics
This gem was created by the Ruby committer as a C extension.
I think it is reliable and fast.
It Returns int if the number of array elements is odd. If the number of array elements is even, it returns a float.
require "enumerable/statistics"
arr = [1, 2, 3, 4, 5]
arr.median # => 3
arr.median.class # => Integer
arr = [1,2,3,4,5,6]
arr.median # => 3.5
Other useful methods are also available.
mean, variance, stdev, mean_variance, mean_stdev, percentile, value_counts histogram

Calculate missing number

Here's the exercise:
You have been given a list of sequential numbers from 1 to 10,000, but
they are all out of order; furthermore, a single number is missing
from the list. The object of the task is to find out which number is
missing.
The strategy to this problem is to sum the elements in the array, then sum the range 1 to 10,000, and subtract the difference. This is equal to the missing number. The formula for calculating the sum of the range from 1..n being n(n+1)/2.
This is my current approach:
def missing_number(array)
sum = 0
array.each do |element|
sum += element
end
((10000*10001)/2) - sum
end
Where I am getting tripped up is the output when I input an array such as this:
puts missing_number(*1..10000) #=> 0
Why does this happen?
Thanks!
No need to sort the array. An array of length N is supposed to have all but one of the numbers 1..(N+1) so the array length + 1 is the basis for figuring out what the grand_sum would be if all values were there.
def missing_number(array)
grand_sum = (array.length + 1) * (array.length + 2) / 2
grand_sum - array.inject(:+)
end
ADDENDUM
This method takes an array as an argument, not a range. You can't use a range directly because there wouldn't be a missing value. Before calling the method you need some mechanism for generating an array which meets the problem description. Here's one possible solution:
PROBLEM_SIZE = 10_000
# Create an array corresponding to the range
test_array = (1..PROBLEM_SIZE).to_a
# Target a random value for deletion -- rand(N) generates values in
# the range 0..N-1, inclusive, so add 1 to shift the range to 1..N
target_value = rand(PROBLEM_SIZE) + 1
# Delete the value and print so we can check the algorithm
printf "Deleting %d from the array\n", test_array.delete(target_value)
# Randomize the order of remaining values, as per original problem description
test_array.shuffle!
# See what the missing_number() method identifies as the missing number
printf "Algorithm identified %d as the deleted value\n", \
missing_number(test_array)
An alternative approach to solving the problem if it's not performance critical, because of its readability:
def missing_number(array)
(1..10_000).to_a - array
end
Instead of *1..10000, the argument should be (1..10000).to_a.
You shouldn't be using *1..10000, this will just expand to 10,000 arguments. (1..10000).to_a will return zero because there are no elements missing between 1..10000 you need to remove one. Below is some code with a detailed explanation.
def missing_number array
# put elements in order
array.sort!
# get value of last array element
last = array[-1]
# compute the expected total of the numbers
# 1 - last
# (n + 1)(n)/2
expected = (last + 1) * (last / 2)
# actual sum
actual = array.inject{|sum,x| sum + x}
# find missing number by subtracting
(expected - actual)
end
test = (1..10000).to_a
test.delete 45
puts "Missing number is: #{missing_number(test)}"

Resources