Finding median for even length array in ruby - ruby

I cannot figure out why I cannot get the even length portion correct.
def median(array)
array.sort!
if array.length % 2 == 0 #if amount of array members is even
(array[(array.length/2) + 1] + array[array.length/2]) / 2.to_f #return average of the 2 middle array members
else #if amount of array members is odd
array[array.length/2.ceil] #return middle number
end
end
My attempt is for example, an array whose length is 6, and whose 3rd and 4th index value are 7 and 9.
array[6/3+1] + array [6/3]
(array[4] + array[3]) /2
9 + 7 / 2
I am receiving this error
Error!
median returns the correct median of an even-length array
expected: 5.5 got: 6.0 (compared using ==)
I have seen a shorter solution, but am most curious if I can make sense of the logic path I am trying to follow, thanks for playing along!
Solution I have seen:
def median(array)
sorted = array.sort
len = sorted.length
return (sorted[(len - 1) / 2] + sorted[len / 2]) / 2.0
end

Arrays are zero-indexed. So if the length was 4, you need to be taking average of indices 1 and 2. Your current attempt would take average of indices 3 and 2 for a length of 4. So you just need to change one small thing (plus into minus):
(array[(array.length/2) - 1] + array[array.length/2]) / 2.to_f
For an even numbered Fixnum n, this is always true: ( n - 1 ) / 2 == ( n / 2 ) - 1, which means you have figured out a similar approach to the one you found. This is not too surprising, there are a limited number of ways to calculate medians efficiently.

Here is my solution to your whole problem. you need use to -1 that's the reason "arr[(arr.length/2)-1]". Also you can use 2.0 instead of 2.to_f.
#Write a method that finds the median of a given array of integers. If the array has an odd number of integers,
# return the middle item from the sorted array. If the array has an even number of integers,
# return the average of the middle two items from the sorted array.
def find_median(arr)
arr.sort!
if arr.length.even?
return (arr[arr.length/2] + arr[(arr.length/2)-1])/2.0
else #else odd
return arr[arr.length/2.0]
end
end
puts find_median([2,3,4,9,7,8])

Related

Execution Timed Out (12000 ms): How can I optimize this simple kata to run faster?

I'm practicing my coding chops after a long break and ran into this kata on CodeWars
With an input of numbers in an array, return the sums of its parts. So for example:
def parts_sums(ls)
sums = []
until ls.size == 0
sums << ls.inject(:+)
ls.shift
end
sums << 0
end
######### INPUT #######
parts_sums([0, 1, 3, 6, 10])
######### EXPECTED OUTPUT ######
[20, 20, 19, 16, 10, 0]
0 + 1 + 3 + 6 + 10 = 20
1 + 6 + 3 + 10 = 20
3 + 6 + 10 = 19
6 + 10 = 16
10 = 10
0 = 0
My solution solves the kata, however once I reach arrays of around 30,000+ my solution takes too long to solve.
So my question is to the community, how would I even attempt to make this go faster. I know that recursion is usually slow, and that for loops and its variants are usually sufficient to get the job done. What happens when that fails? What are some things to try to make my code above faster?
I'm looking for some advice and some examples if anyone has any. Appreciate the input. Thanks.
def parts_sums(ls)
ls.each_with_object([ls.sum]) { |n,arr| arr << arr.last - n }
end
parts_sums([0, 1, 3, 6, 10])
#=> [20, 20, 19, 16, 10, 0]
The issue with the code is that you are performing an inject on every iteration of your loop, which is unnecessarily slow.
You only need to sum the elements of the array once, outside of any loop. Once you have that sum, you can iterate through the elements of the array and perform a constant time subtraction from the current sum and push it into the sums array.
def part_sums(ls)
sum = ls.inject(:+)
sums = [sum]
ls.each do |val|
sum -= val
sums << sum
end
sums
end
There is also no need to shift, if you iterate through the array with the each iterator or keep a counter and use a while loop.
This version of the function runs much faster:
def parts_sums_2(ls)
sums = []
last_sum = 0
(ls.length - 1).downto(0).each do |i|
last_sum += ls[i]
sums.prepend last_sum
end
sums + [0]
end
The key here is going backwards through the array - starting with the smallest sum (only the last element). Each subsequent step moves one index towards the beginning, and adds that value to the previous sum.
Since the problem statement requires you to shift each step, your result must have the largest sums at the beginning, even though these are the last ones to be computed. This is why my code uses prepend rather than push.
This is O(N) time complexity instead of O(N^2), which is an order of magnitude difference.
With 100_000 inputs, your original function took 7.040443 seconds, while mine here took 0.000008 seconds
Also in general you should try to avoid mutating the input to your methods (as you were doing with shift).

The integers in the array are either entirely odd or entirely even except for a single integer

You are given an array (which will have a length of at least three, but could be very large) containing integers. The array is either entirely comprised of odd integers or entirely comprised of even integers except for a single integer N. Write a method that takes the array as an argument and returns this outlier N.
This is my code so far, which doesn't seem to work:
arr = [160, 3, 1719, 19, 11, 13, -21]
n = arr.length
def getOddOccurrence(arr, arr_size)
for i in range(0, arr_size)
count = 0
for j in range(0, arr_size)
if arr[i] == arr[j]
count += 1
end
if(count % 2 != 0)
return arr[i]
end
end
end
return -1
end
print getOddOccurrence(arr, n)
What change do I need with this code?
Here is a simple way to do it
arr = [160, 3, 1719, 19, 11, 13, -21]
arr.group_by(&:odd?).values.sort_by(&:count)[0][0]
# => 160
group_by(&:odd?) Will make 2 hashes for odd and even numbers
values Will grab the values of the hash. 2 arrays, for even and odd
sort_by(&:count) Sort arrays, one with less values will be first
[0][0] Grab the first number of the first array
Here's a solution that is arcane (ugly), but relatively straightforward. It takes O(arr.size) time and uses O(1) additional storage. It also "short-circuits" as soon as it finds the outlier.
Here's the basic idea. Even numbers have zero for the least significant bit, and odd numbers have one, so if you XOR an adjacent pair of numbers the least significant bit will be one only if they lack parity. The first time that happens after the first pair, you've found the outlier. If it happens with the first pair, you need to check the second pairing. If that yields a zero, the first value was the outlier, otherwise it was the second.
def getOddOccurrence(arr)
arr.each_index do |i|
return arr[i == 1 && (arr[i] ^ arr[i + 1]) & 1 == 0 ? 0 : i] if i > 0 && (arr[i] ^ arr[i - 1]) & 1 == 1
end
end
And here's the same concept in a slightly more Rubyish manner:
def getOddOccurrence(arr)
arr.each_cons(3) { |x,y,z| return ((y ^ z) & 1 == 1 ? y : x) if (x ^ y) & 1 == 1 }
arr[-1]
end
If you prefer looking at subsets of 2, make a one-off check of the first 3 values and then work with cons(2) subsets. You can also replace bit-testing with a check for consistency on evenness (or oddness) to improve readability:
def getOddOccurrence(arr)
return arr[0] if (arr[0].odd? ^ arr[1].odd?) && !(arr[1].odd? ^ arr[2].odd?)
arr.each_cons(2) { |x,y| return y if (x.odd? ^ y.odd?)}
end
I finally had a few spare minutes to throw together a benchmark:
require 'benchmark/ips'
def getOddOccurrence_cons3(arr)
arr.each_cons(3) { |x,y,z| return ((y ^ z) & 1 == 1 ? y : x) if (x ^ y) & 1 == 1 }
arr[-1]
end
def getOddOccurrence_cons2(arr)
return arr[0] if (arr[0].odd? ^ arr[1].odd?) && !(arr[1].odd? ^ arr[2].odd?)
arr.each_cons(2) { |x,y| return y if (x.odd? ^ y.odd?) }
end
def getOddOccurrence_cons2_bits(arr)
return arr[0] if ((arr[0] ^ arr[1]) & 1 == 1) && ((arr[1] ^ arr[2]) & 1 == 0)
arr.each_cons(2) { |x,y| return y if (x ^ y) & 1 == 1 }
end
def getOddOccurrence_find(arr)
arr.first(3).count(&:odd?) > 1 ? arr.find(&:even?) : arr.find(&:odd?)
end
def getOddOccurrence_find_bits(arr)
arr.first(3).sum {|x| x & 1} > 1 ? arr.find { |x| (x & 1) == 0 } : arr.find { |x| (x & 1) == 1 }
end
def find_outlier(ary)
# fetch first 3 numbers and determine what kind of array
# are we dealing with here, mostly odd or mostly even?
mostly_odd = ary.take(3).count(&:odd?) > 1
# then just go and find the outlier element
if mostly_odd
ary.find(&:even?)
else
ary.find(&:odd?)
end
end
arr = Array.new(10_000) { |i| i * 2 }.shuffle << 5
Benchmark.ips do |b|
b.report('cons3 bits:') { getOddOccurrence_cons3(arr) }
b.report('cons2 bits:') { getOddOccurrence_cons2_bits(arr) }
b.report('cons2 even/odd:') { getOddOccurrence_cons2(arr) }
b.report('find even/odd:') { getOddOccurrence_find(arr) }
b.report('find bits:') { getOddOccurrence_find_bits(arr) }
b.report('find sergio:') { find_outlier(arr) }
b.compare!
end
As you can see, I put an odd value at the end of an even array to maximize the searching required.
And the winner is...
Warming up --------------------------------------
cons3 bits: 128.000 i/100ms
cons2 bits: 127.000 i/100ms
cons2 even/odd: 103.000 i/100ms
find even/odd: 216.000 i/100ms
find bits: 217.000 i/100ms
find sergio: 231.000 i/100ms
Calculating -------------------------------------
cons3 bits: 1.251k (± 4.9%) i/s - 6.272k in 5.026355s
cons2 bits: 1.294k (± 3.4%) i/s - 6.477k in 5.010802s
cons2 even/odd: 1.038k (± 4.4%) i/s - 5.253k in 5.070617s
find even/odd: 2.284k (± 4.2%) i/s - 11.448k in 5.022831s
find bits: 2.165k (± 5.3%) i/s - 10.850k in 5.027801s
find sergio: 2.277k (± 3.3%) i/s - 11.550k in 5.078381s
Comparison:
find even/odd:: 2283.6 i/s
find sergio:: 2276.9 i/s - same-ish: difference falls within error
find bits:: 2164.6 i/s - same-ish: difference falls within error
cons2 bits:: 1294.2 i/s - 1.76x slower
cons3 bits:: 1251.1 i/s - 1.83x slower
cons2 even/odd:: 1038.1 i/s - 2.20x slower
...the one-liner from Sagar Pandyar's comment!
The find-based approach clearly beats each_cons. Using Ruby's odd/even methods vs binary operations appears to have only a minor impact. Interestingly, using .each_cons(3) rather than .each_cons(2) has very little relative impact as well, although both are clearly dominated by Sagar & Sergio's approach.
Welcome to Stack Overflow!
Since you're new, let me start by saying that asking for solutions here is generally not well received. This isn't a place to have other people do your work for you, so you should check out https://stackoverflow.com/help/how-to-ask to learn about what makes a good question for the future.
That said, rather than give you a solution, let me see if I can aid your understanding of what seems to be tripping you up. I'm going to ignore a lot of the "ruby-isms" that can shorten things a lot, since they are nice but ultimately it seems like you might still need an understanding of the underlying approach rather than shortcuts, since that's what helps you program better in the long run.
if arr[i] == arr[j]
count +=1
end
The above code is looking for two numbers in the array that are equal. That means count is never going to be incremented unless your array contains two of the same value, which is not what you want from your description of the task. Additionally, this problem really doesn't need you to compare two numbers in the array. You just need to determine whether each number is odd or even and find the outlier.
The easiest (and possibly most common) programming way of determining whether a number is odd is by using the modulo operator (%). You used this in checking your count variable, which again really isn't what you need. Instead, you should be using it against each entry in the array. So for some integer value n, n % 2 will be 0 if it's an even number, or 1 if it's an odd number. It seems like you somewhat understood this, but use this against each number in the array to determine whether it is even or odd instead of on the count variable, and then you can act on that information for each number.
Once you've got it so that you're determining whether each number in the array is even or odd, you need a way to track whether you're searching for an odd or even number. The simplest way to do this would be to keep track of even/odd count in a variable, but have one variable for even count and a separate one for odd count. So when you encounter an even number, you can add 1 to the even count, and similarly for odd numbers but to the odd count. That way you know the type you are looking for (even or odd) is whichever count equals 1 after you finish going through the array. This means that these variables should be outside of the loop that looks through the array, since you don't want them to reset for each number in the array, and you're probably going to want to look at them after the loop also.
Once you've determined whether you're looking for odd or even, you can go through the array a second time (not a nested loop, but a second one after the first one) and return the odd or even number from the array as appropriate. There are ways to do it without a second loop, but I'm trying to keep it straight forward.
Hopefully this helps you come up with your own solution so you can learn from solving the problem. If you get it working with my basic layout, there are several ways that you can make it better in terms of performance or just amount of code (such as not using a second loop). Happy to clarify if you need.
Happy coding!
Here is a linear-time constant-memory algorithm
def find_outlier(ary)
# fetch first 3 numbers and determine what kind of array
# are we dealing with here, mostly odd or mostly even?
mostly_odd = ary.take(3).count(&:odd?) > 1
# then just go and find the outlier element
if mostly_odd
ary.find(&:even?)
else
ary.find(&:odd?)
end
end
ary = [161, 3, 1719, 19, 11, 160, 13, -21]
find_outlier(ary) # => 160

Ruby prime number sum

I am trying to take the sum of the n first prime numbers. I found a way of showing the first 100, but I don't know how to get rid of 1 and how to make a sum with the numbers. I was thinking about storing them into an array, but I can not figure it out.
num = 1
last = 100
while (num <= last)
condition = true
x = 2
while (x <= num / 2)
if (num % x == 0)
condition = false
break
end
x = x + 1
end
primes = [] # Here
if condition
puts num.to_s
primes << num.to_s # Here
end
num = num + 1
end
puts primes.inject(:+) # Here
Based on what I understood from what you guys are saying I added these lines (the ones commented # Here). It still does not print the sum of them. What I meant with getting rid of 1 is that I know that 1 is not considered a prime number, and I do not get how to make it without 1. Thank you very much guys for your time and answers, and please understand that I am just starting to study this.
If you want to add a list of numbers together you can use the following:
list_of_prime_numbers.inject(0) {|total,prime| total + prime}
This will take the list of numbers, and add them one by one to an accumulator (total) that was injected into the loop (.inject(0)), add it to the current number (prime) and then return the total which then becomes the value of total in the next iteration.
I'm not quite sure what you mean by:
I don't know how to get rid of 1
but if you mean to not use the first number (which is 1 in a list of primes starting from 0)
then you could do:
list_of_prime_numbers[1...list_of_prime_numbers.length].
inject(0) {|total,prime| total + prime}
Which would only get all the numbers except the first up to but not including the length of the array
and as for getting the number into the array you could push it into the array like so:
list_of_prime_numbers << prime_number
You can make use of Prime Enumerable in ruby
require 'prime'
((1..100).select { |number| Prime.prime?(number) }).inject(:+)
OR
Prime.each(100).inject(:+)
Hope this helps.

best way to rewind a variable at 1?

I have an array with 12 entries.
When doing 12+1, I want to get the entry 1 of the array
When doing 12+4, I want to get the entry 4 of the array
etc...
I'm done with
cases_to_increment.each do |k|
if k > 12
k = k-12
end
self.inc(:"case#{k}", 1)
end
I found a solution with modulo
k = 13%12 = 1
k = 16%12 = 4
I like the modulo way but 12%12 return 0 and I need only numbers between 1..12
There is a way to do that without condition ?
You almost had the solution there yourself. Instead of a simple modulo, try:
index = (number % 12) + 1
Edit: njzk2 is correct, modulo is a very expensive function if you are using it with a value that is not a power of two. If, however, your total number of elements (the number you are modulo-ing with) is a power of 2, the calculation is essentially free.

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