Complexity with Array.min - ruby

I have an array:
[0, 0, 0, 0, 0, 0, 0, 1, 2, 3]
I need to figure out index of the minimal element which is not zero. How do I do that?

For ruby 1.8.7+:
>> [0,0,2,0,1,3].each_with_index.reject {|(e, i)| e == 0}
=> [[2, 2], [1, 4], [3, 5]]
>> [0,0,2,0,1,3].each_with_index.reject {|(e, i)| e == 0}.min
=> [1, 4]
>> [0,0,2,0,1,3].each_with_index.reject {|(e, i)| e == 0}.min[1]
=> 4
For ruby 1.8.6:
a.zip((0...a.size).to_a).reject {|(e, i)| e == 0}.min[1]
(solution by chuck)

a=[0, 0, 0, 0, 0, 0, 0, 1, 2, 3]
i=a.index a.reject{|x|x==0}.min
(i=7)

Simplest way:
check each element of the array, keep a variable that is the minimum, set it equal to the first number you come across (unless 0, then discard and use next number). Any time you come across a number smaller than your minimum, set it to your minimum. And, of course, discard any zero rather than setting your minimum.
More efficient:
It appears we have a sorted array, if we can use that to our advantage, we can use a better search mechanism, such as quick-search or binary-search. I will describe binary search as it is easy to understand.
Our array is in ascending order.
Check the middle most element, set it equal to your minimum (unless 0). Split the array in half on this middle element. Since the array is ascending, check the middle point of the left half (unless element was 0, then check right). Continue until there is only one element to the left when you split. That is your minimum.

I don't know Ruby, so I can't offer code, but the process that immediately springs to my mind is:
Create a copy of the array (if needed)
Remove all the 0 entries
Check the minimum value in the new array

Related

What does this line mean in Sorting Algo(Bubble example)

def bubbleSort(array):
swapped = False
for i in range(len(array)-1,0,-1):
print(i)
for j in range(i):
print(j)
if array[j]>array[j+1]:
array[j], array[j+1] = array[j+1], array[j]
swapped= True
if swapped:
swapped=False
else:
break
print(array)
bubbleSort([5, 2, 1, 3])
How should I interpret this line: for i in range(len(array)-1,0,-1)? I'm particularly confused about the need for the 0 and -1 parameters.
That line has a couple of things happening, which I will give simplified explanations of (I'm assuming this code is written in Python).
First, for i in iterable will loop through iterable, meaning the code in the for loop will repeat as many times as there are elements in iterable, which could be an array, a list, a string etc, and each time it loops, i will be the next element of iterable, starting with the first. For example, for i in [1, 2, 3] will loop 3 times; the first time, i will be equal to 1; the second, 2, etc.
Next, the range function produces an iterable that is a range of numbers, for example from 0-9. With a single argument, range will produce a range from 0 to that number, but stopping just before it, e.g. range(5) will give you [0, 1, 2, 3, 4]. Thus if you were to use for i in range(5), your code would repeat 5 times, with i incrementing from 0 to 4.
With two arguments, the range will start at the first and stop before the second, which must be greater than the first. For example, range(3, 8) would give you [3, 4, 5, 6, 7]. range(8, 3), however, will not work, as the start number is greater than the stop number. This means you cannot count down with only 2 arguments.
The third optional argument for range is the step size; how much you want the numbers to increase or decrease by each step. For example, range(0, 10, 2) will give you the output [0, 2, 4, 6, 8], stopping before 10. Here is where you can produce a descending range, by setting the step argument to a negative number. range(10, 0, -2) will give you [10, 8, 6, 4, 2], again stopping before the second argument, and range(10, 0, -1) will give you the full [10, 9, 8, 7, 6, 5, 4, 3, 2, 1].
Finally, the len(iterable) function will give you the length of whatever you give it, or the number of items contained in say a list. For example len("Hello!") will give you 6, and len([1, 2, 3, 4, 5]) will give you 5.
Putting this all together, the line for i in range(len(array)-1, 0, -1) will do the following:
the code will repeat as many times as there are items in a list, with i taking on each value in the list
that list is a range of numbers
that start number of the range is the length of array minus one
the end of the range is 0
the range is descending, with a step size of -1
Thus if array were ["fish", "banana", "pineapple", "onion"], len(array) will return 4, so you will have for i in range(3, 0, -1), which will loop 3 times, with i being 3, then 2, then 1.
This was a rather simplified answer, so I suggest you find some tutorials on any functions you don't understand.

How is this obscure sorting algorithm called

I came up with an obscure sorting algorithm and given that it's so simple, it must have been invented and named before, so I was wondering what it's called.
It has a very rare constraint: It only works for inputs that have keys from 0 to n-1 (or equivalent). That's a very strong constraint that makes it useless in practice, but maybe one can construct some artificial settings in which it's useful. The algorithm basically swaps the element at a particular position with its final position until the array is sorted. Pseudocode:
def obscure_sort(array):
sorted_until = 1
while true
if key(array[0]) != 0:
# Swap the element at position 0 to its final position.
swap(array, 0, key(array[0]))
else:
# Find the next element that isn't in its final position.
while key(array[sorted_until]) == sorted_until:
sorted_until++
# If we happen to reach the end, we're done.
if sorted_until == array.length:
return
# Swap the newfound first unsorted element to position 0
swap(array, 0, sorted_until)
The algorithm actually runs in O(n). It's not completely trivial to see that and I'll leave out the analysis unless someone is really interested.
Does anyone know if this has a name?
This is a slight variation of a restricted cycle sort, probably closest to the algorithm from section 3 of this paper.
Normally with cycle sort on the keys A = [0, 1,...(A.length-1)], you would loop through the array testing indices 0 to A.length-1 as a 'cycle start', looking for cycles to rotate. One 'rotation' is done by always holding a temporary variable 'temp' (initially our cycle start), and doing a swap(temp, A[temp]) until we are back at the start of the cycle (i.e., when temp == A[temp]).
Here, in contrast, we add 0 at the back of the cycle, and 'A[0]' takes the place of 'temp'. We use the operation swap(A[0], A[A[0]]), so that in general, an element x that's moved takes a journey of A[old] -> A[0] -> A[x] rather than A[temp] -> temp -> A[x].
In the linear time algorithm described in the paper above, upon starting loop iteration i, all of the elements 0, 1, ..., i-1 are in place and never moved again. This algorithm is similar, except that if it were written with the same loop style, 0, 1, ..., i-1 are also in place at the start of iteration i but element 0 is not fixed, being moved constantly during an iteration.
As a small example:
Traditional Cycle Sort
Initially, A = [1, 3, 0, 2]
Step 1: A = [1, 3, 0, 2], temp = 1, with cycle_start = 0
Step 2: A = [1, 1, 0, 2], temp = 3
Step 3: A = [1, 1, 0, 3], temp = 2
Step 4: A = [2, 1, 2, 3], temp = 0
Step 5: A = [0, 1, 2, 3], temp = 2; stop since temp == A[temp]
Custom Cycle-like Sort
A = [1, 3, 0, 2]
Step 1: A = [1, 3, 0, 2]
Step 2: A = [3, 1, 0, 2]
Step 3: A = [2, 1, 0, 3]
Step 4: A = [0, 1, 2, 3]
Note that this new sort can take more steps than the normal cycle sort, since 'adding 0 at the back of the cycle' can add an additional swap operation per cycle. The total number of array swaps, though, is linear (and at most twice the array length).

How to check if every element in a 2D Array are connected together

Question is in the title. I have a 2D array:
array = [
[0, 0, 1, 0, 1],
[0, 0, 1, 0, 1],
[1, 1, 1, 1, 1],
[0, 0, 1, 0, 0],
[0, 0, 1, 0, 0]
]
How do I check to see if every element "1" in this example are all connected together as neighbors either laterally or horizontally. In this example the function should return TRUE since all of the 1's are all connected together. In contrast:
array = [
[0, 0, 0, 1, 1],
[0, 0, 0, 1, 1],
[0, 0, 0, 0, 0],
[1, 1, 0, 0, 0],
[1, 1, 0, 0, 0]
]
This should return FALSE, since their is a divide between the 1's and not all of them are neighbors.
My initial thought was to iterate through the array and check to see if any of the adjacent items were 1's or not. However, this doesn't work since two elements can be next to each other yet away from the rest of the group. Any help is greatly appreciated.
You can use BFS or DFS for that.
These are exploration algorithms that helps you to discover all nodes connected to your starting one.
The "trick" is to think of your matrix as a graph where:
V = { (i,j) | a[i][j] == 1} (informally, all locations where there is 1 in the matrix
E = { ((i1, j1), (i2, j2)) | (i1, j1), (i2, j2) are adjacent }
Then, just find a place where a[i][j] == 1, and start a BFS or DFS from it to disccover all reachable nodes.
Once you are done, iterate the matrix again, and see if each a[i][j] == 1 element was discovered.
Good luck!
The correct answer for this question is counting all the elements that are 1's then finding any element that is a '1' then using a flood fill algorithm that counts the amount of 1's. If the two values are equal then the answer is True if not then false.
https://en.wikipedia.org/wiki/Flood_fill

Ruby : Can you please explain why reverse range not giving expected result in ruby

why the below given [] for a reverse order range in ruby
(0..5).to_a
# => [0, 1, 2, 3, 4, 5]
(5..0).to_a
# => []
I am new on ruby PL
what is the best way to use a reverse range in ruby
The best way to use a reverse range for iteration
I know .reverse of array
But can I inherit the range and make a custom method of it and use it as a range
I also try
class Range
def r
to_a.reverse
end
end
# => :r
(5..0).r
# => []
You asked why (5..0).to_a doesn't give the "expected result", by which I assume you mean [5,4,3,2,1].
If a range is a..b that means it includes all values x such that a <= x <= b.1 If a > b, no values are included, so nil is returned. There is no such thing as an "empty" range, as there is an empty array ([]), hash ({}), string ("") and so on. (1..1 is a range, but it is not empty.) That's why 5..3 cannot return a Range object, and therefore returns nil.
Ruby does not support the concept of a "reversed range". If you just want to step down from 3 to 1 there are many ways to do that without involving a range.
Note also that ranges, unlike arrays, for example, may contain an infinite number of values. 1.0..3.0 is one such example.
1 The range a...b (three dots) includes all values x such that a <= x < b.
what should I do if I need a reverse range
(0..5).to_a.reverse
#=> [5, 4, 3, 2, 1, 0]
or
(0..5).reverse_each.to_a
#=> [5, 4, 3, 2, 1, 0]
Here's a way to do it that might more closely express they way you're thinking about it:
(5.downto 0).to_a
=> [5, 4, 3, 2, 1, 0]
Try:
[*0..5].reverse
#=> [5, 4, 3, 2, 1, 0]
OR
> r = Proc.new {|x| x.reverse}
> r.call((0..5).to_a)
#=> [5, 4, 3, 2, 1, 0]

Find the middle element in merged arrays in O(logn)

We have two sorted arrays of the same size n. Let's call the array a and b.
How to find the middle element in an sorted array merged by a and b?
Example:
n = 4
a = [1, 2, 3, 4]
b = [3, 4, 5, 6]
merged = [1, 2, 3, 3, 4, 4, 5, 6]
mid_element = merged[(0 + merged.length - 1) / 2] = merged[3] = 3
More complicated cases:
Case 1:
a = [1, 2, 3, 4]
b = [3, 4, 5, 6]
Case 2:
a = [1, 2, 3, 4, 8]
b = [3, 4, 5, 6, 7]
Case 3:
a = [1, 2, 3, 4, 8]
b = [0, 4, 5, 6, 7]
Case 4:
a = [1, 3, 5, 7]
b = [2, 4, 6, 8]
Time required: O(log n). Any ideas?
Look at the middle of both the arrays. Let's say one value is smaller and the other is bigger.
Discard the lower half of the array with the smaller value. Discard the upper half of the array with the higher value. Now we are left with half of what we started with.
Rinse and repeat until only one element is left in each array. Return the smaller of those two.
If the two middle values are the same, then pick arbitrarily.
Credits: Bill Li's blog
Quite interesting task. I'm not sure about O(logn), but solution O((logn)^2) is obvious for me.
If you know position of some element in first array then you can find how many elements are smaller in both arrays then this value (you know already how many smaller elements are in first array and you can find count of smaller elements in second array using binary search - so just sum up this two numbers). So if you know that number of smaller elements in both arrays is less than N, you should look in to the upper half in first array, otherwise you should move to the lower half. So you will get general binary search with internal binary search. Overall complexity will be O((logn)^2)
Note: if you will not find median in first array then start initial search in the second array. This will not have impact on complexity
So, having
n = 4 and a = [1, 2, 3, 4] and b = [3, 4, 5, 6]
You know the k-th position in result array in advance based on n, which is equal to n.
The result n-th element could be in first array or second.
Let's first assume that element is in first array then
do binary search taking middle element from [l,r], at the beginning l = 0, r = 3;
So taking middle element you know how many elements in the same array smaller, which is middle - 1.
Knowing that middle-1 element is less and knowing you need n-th element you may have [n - (middle-1)]th element from second array to be smaller, greater. If that's greater and previos element is smaller that it's what you need, if it's greater and previous is also greater we need to L = middle, if it's smaller r = middle.
Than do the same for the second array in case you did not find solution for first.
In total log(n) + log(n)

Resources