Counting inversions in a changing array - arrays

You have an array A[] of size (1 ≤ N ≤ 10^5). For each of i = 0, 1, 2, ..., N - 1, we want to determine the number of inversions in the array if all entries greater than i are decreased to i.
An inversion is defined as two entries A[i] and A[j] where A[i] > A[j] and i < j.
Example:
A[] = {3, 2, 1, 5, 2, 0, 5}
i = 0: {0, 0, 0, 0, 0, 0, 0} Inversions: 0
i = 1: {1, 1, 1, 1, 1, 0, 1} Inversions: 5
i = 2: {2, 2, 1, 2, 2, 0, 2} Inversions: 7
i = 3: {3, 2, 1, 3, 2, 0, 3} Inversions: 10
i = 4: {3, 2, 1, 4, 2, 0, 4} Inversions: 10
i = 5: {3, 2, 1, 5, 2, 0, 5} Inversions: 10
i = 6: {3, 2, 1, 5, 2, 0, 5} Inversions: 10
So your output would be:
0
5
7
10
10
10
10
I know how to find the number of inversions in an array through MergeSort in O(NlogN). However, if I was to explicitly generate every array for each value of i, it would be an O(N^2logN) algorithm which wouldn't pass in time.
One observation I made was that the inversions increase as i increases. This makes sense because when all entries are 0, there will be no inversions (as it is sorted), but as you keep increasing the maximum entry value, the entry can become larger than entries that previously were of the same value.
So you could start with an A[] with only 0s, and keep increasing i. You can use your answer for previous values of i to determine the answer for larger values of i. Still, if you scanned through each array you would still get an O(N^2) algorithm.
How can I solve this problem?

I'll take a stab at this. We're going to consider queries in descending order, so from i = N-1, ..., down to 0. First of all, notice that when we're shrinking all A[j] > i to i, any A[j] = i will no longer cause an inversion with elements larger than it of smaller index.
For example, say we have A = [1, 2, 5, 4] and we shrink A[2] to 4. Then we have A = [1, 2, 4, 4] and our single inversion disappears. Thus, for each j, we can count the number of elements in A with smaller index and larger value, and denote this V[j], the "number of inversions it contributes". We find the total number of inversions in the original array, and then for each i = N-1,...,0 we remove V[j] from the total number of inversions for all j such that V[j] = i.
Let's apply this to the example given.
A = [3, 2, 1, 5, 2, 0, 5]
V = [0, 1, 2, 0, 2, 5, 0]
Then, going through i = 6, 5, 4, 3, 2, 1:
i = 6: A = [3, 2, 1, 5, 2, 0, 5], res = 10 (original calculation using merge sort)
i = 5: A = [3, 2, 1, 5, 2, 0, 5], res = 10 (subtract nothing because V[3] = V[6] = 0)
i = 4: A = [3, 2, 1, 4, 2, 0, 4], res = 10 (subtract nothing because no occurrences of 4)
i = 3: A = [3, 2, 1, 3, 2, 0, 3], res = 10 (10 - V[0] = 10)
i = 2: A = [2, 2, 1, 2, 2, 0, 2], res = 7 (10 - V[1] - V[4] = 10 - 1 - 2 = 7)
i = 1: A = [1, 1, 1, 1, 1, 0, 1], res = 5 (7 - V[2] = 7 - 2 = 5)
i = 0: A = [0, 0, 0, 0, 0, 0, 0], res = 0 (5 - V[5] = 5 - 5 = 0)
And we get our desired outputs. Implementation details can vary; you can find the number of elements greater than A[j] with lower index using a Fenwick Tree or something similar. This algorithm runs in O(NlogN) time.

Related

A new variant of Rotated array, how to get the index of the first minimum element after rotating using binary-search?

Suppose an array of length n sorted in ascending order is rotated between 1 and n times. For example, the array nums = [0,1,4,4,5,6,7] might become:
[4,5,6,7,0,1,4] if it was rotated 4 times.
[0,1,4,4,5,6,7] if it was rotated 7 times.
Notice that rotating an array [a[0], a[1], a[2], ..., a[n-1]] 1 time results in the array [a[n-1], a[0], a[1], a[2], ..., a[n-2]].
Given the sorted rotated array nums that may contain duplicates, return the index of first minimum element of this array after rotating(same as the times of rotating).
For example:
Input [1, 2, 3, 3, 4, 4] Output: 0
Input [5, 6, 1, 2, 3, 4] Output: 2
Input [2, 3, 4, 1, 1, 1, 1] Output: 3
Input [1, 1, 1, 1, 2, 1, 1] Output: 5
Can we solve it using binary-search? In the worst case, the time complexity is O(n), however, it will be much better in general cases.
Do a binary search and look at every position if the left neighbour is greater than the current position value. Then return the position index. Otherwise return 0.
In Python you can write
def search_recursive(array, start, end):
if start > end:
return 0
mid = (start + end) // 2
if mid > 0 and array[mid - 1] > array[mid]:
return mid
left = search_recursive(array, start, mid-1)
right = search_recursive(array, mid+1, end)
if left == 0:
return right
if right == 0:
return left
return min(left, right)
inputs = [[1, 2, 3, 3, 4, 4], [5, 6, 1, 2, 3, 4], [2, 3, 4, 1, 1, 1, 1], [1, 1, 1, 1, 2, 1, 1]]
for input in inputs:
print(search_recursive(input, 0, len(input)-1))
// Outputs:
// 0
// 2
// 3
// 5
The complexity is not good because you visit every element two times.
Another approach is use the binary search points and cut of some branches. For example in [5, 6, 1, 2, 3, 4] and the values start=5, mid=1, succ_mid=2 and end=4 you can concentrate the search to the array [5, 6, 1]. My implementation would look like the following:
def search_recursive(array, start_value, end_value, start, end):
if start >= end:
return 0
mid = (start + end) // 2
mid_value = array[mid]
succ_mid = mid + 1
succ_mid_value = array[succ_mid]
if start_value > mid_value:
return search_recursive(array, start_value, mid_value, start, mid)
if mid_value > succ_mid_value:
return succ_mid
left = search_recursive(array, start_value, mid_value, start, mid)
right = search_recursive(array, succ_mid_value, end_value, succ_mid, end)
if left == 0:
return right
return min(left, right)
inputs = [[1, 2, 3, 3, 4, 4], [5, 6, 1, 2, 3, 4], [2, 3, 4, 1, 1, 1, 1], [1, 1, 1, 1, 2, 1, 1]]
for input in inputs:
print(search_recursive(input, input[0], input[len(input)-1], 0, len(input)-1))
// Outputs:
// 0
// 2
// 3
// 5

Ruby: How to find to similarity in two arrays

I am trying to find the common elements in two arrays.
pairs = Array.new
a = exchange_one.get_symbols
b = exchange_two.get_symbols
c = a+b
c.uniq{|pair| pairs << pair}
I am combining the two arrays using +
Then I am calling uniq to remove the duplicate, but passing it to a block so the found duplicates can be added to an array before they are deleted.
For some reason the array pairs is just the entire c array.
What is the correct way to find array similarities.
If your goal is simply to determine which elements are the same between two arrays, you can use the intersection operator Array#&.
a = exchange_one.get_symbols
b = exchange_two.get_symbols
intersection = a & b
First understand what are you doing and what you want.
For eg.
a = 15.times.map { rand 6 }
#=> [1, 0, 5, 3, 1, 3, 4, 1, 3, 2, 1, 2, 4, 2, 3]
b = 15.times.map { rand 6 }
#=> [3, 3, 3, 1, 3, 1, 3, 1, 5, 1, 4, 2, 0, 0, 4]
Now what are you doing
c = a + b
#=> [1, 0, 5, 3, 1, 3, 4, 1, 3, 2, 1, 2, 4, 2, 3, 3, 3, 3, 1, 3, 1, 3, 1, 5, 1, 4, 2, 0, 0, 4]
c - only combine arrays irrespective of content hence get all values.
Now
pairs = Array.new
c.uniq{|pair| pairs << pair}
Here uniq is just act as a iterator means if you check 'pair' then it iterate all the values of 'c' and insert those values in 'pairs' array.
check this
c.uniq{|pair| puts pair}
Thats why you are getting all values within 'pairs' array.
The best way to find similarity in arrays is (a&b), but you can make changes in your code as follow to achieve it.
pairs = (arr1+arr2).uniq
OR
pairs = arr1 & arr2 #best and efficient way.
Suppose:
arr1 = 15.times.map { rand 6 }
#=> [1, 0, 4, 0, 2, 3, 1, 0, 2, 4, 4, 1, 3, 1, 1]
arr2 = 15.times.map { rand 6 }
#=> [5, 5, 4, 1, 5, 1, 5, 0, 4, 0, 2, 0, 4, 5, 0]
arr1 contains 5 1s and arr2 contains 2 1s. If, by "common elements" you wish to report that both arrays contain [5, 2].min #=> 2 1s, and similar counts for the other elements that appear in either array, you can do the following:
h1 = count(arr1)
#=> {1=>5, 0=>3, 4=>3, 2=>2, 3=>2}
h2 = count(arr2)
#=> {5=>5, 4=>3, 1=>2, 0=>4, 2=>1}
(h1.keys | h2.keys).each_with_object({}) { |k,h| h[k] = [h1[k], h2[k]].min }
#=> {1=>2, 0=>3, 4=>3, 2=>1, 3=>0, 5=>0}
def count(arr)
arr.each_with_object(Hash.new(0)) { |n,h| h[n] += 1 }
end

Add multiple elements in single dimension array at odd intervals

I have to write a function that inserts multiple elements into a single dimension array of unknown length.
For example:
input_array = [1, 2, 3, 4, 5]
Inserting two zeroes between each element, to give:
output_array = [1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, 5]
.....
Any ideas?
Here is two versions of the code.
Simple for loop:
input_array = [1, 2, 3, 4, 5]
output_array = []
for k in input_array:
output_array.append(k)
output_array.append(0)
output_array.append(0)
print(output_array)
Using list comprehensions:
input_array = [1, 2, 3, 4, 5]
output_array = [item for sublist in [[x, 0, 0] for x in input_array] for item in sublist])
print(output_array)
I can't say whether the asker, as #Willem notes, was looking for a faster solution than he/she himself/herself was able to come up with. In reality, this seems like a simple task:
def fill(iterable, padding: tuple):
result = list()
for i in iterable:
# The * symbol is a sequence unpacking and it serves to flatten the values inside result
# For example, [*(0, 1, 2)] equals [0, 1, 2] and not [(0, 1, 2)]
result.extend([i, *padding])
return result
if __name__ == "__main__":
data = range(1, 6)
padding = (0, 0)
print(fill(data, padding))
I could obviously choose any other container type in place of list assigned to result.
Below is what the above script outputs when running on my machine:
None#vacuum:~$ python3.6 ./test.py
[1, 0, 0, 2, 0, 0, 3, 0, 0, 4, 0, 0, 5, 0, 0]

Create an array containing values oscillating between two boundaries

I am wondering if there is a way to generate an array which, for example, would start from 0, increase by 1 until it reaches 3, and then decreases by 1 until it reaches 0 again, eg
[0,1,2,3,2,1,0]
and if I could specify the number of values in the array ahead of time, that would be great. For example, if I could set the lower bound(0), upper bound (3), increment(1), and length of array (9):
[].oscillate(0,3,1,9) would give me this:
[0,1,2,3,2,1,0,1,2]
As of now, the best thing I can come up with is this:
values = []
until values.count >= 9
values.pop
x=0
values << x && x+=1 while x < 3
values << x && x-=1 while x >= 0
end
Fun exercise!
You're looking for a triangle wave.
The formulas on Wikipedia are for the standard shape (between -1 and 1), but here's an adapted version for any wave position, period and amplitude :
def triangle_wave(min, max, increment, length, offset = 0)
amplitude = max - min
period = 2 * amplitude
Array.new(length) do |i|
min + ((increment * (i + offset) - amplitude) % period - amplitude).abs
end
end
puts triangle_wave(0, 3, 1, 9) == [0, 1, 2, 3, 2, 1, 0, 1, 2]
# true
p triangle_wave(-3, 3, 1, 20)
# => [-3, -2, -1, 0, 1, 2, 3, 2, 1, 0, -1, -2, -3, -2, -1, 0, 1, 2, 3, 2]
p triangle_wave(5, 9, 2, 9)
# => [5, 7, 9, 7, 5, 7, 9, 7, 5]
p triangle_wave(0, 1, 0.25, 9)
# => [0.0, 0.25, 0.5, 0.75, 1.0, 0.75, 0.5, 0.25, 0.0]
p triangle_wave(-3, 0, 1, 9, 3)
# => [0, -1, -2, -3, -2, -1, 0, -1, -2]
p triangle_wave(0, 1, 1, 9)
# => [0, 1, 0, 1, 0, 1, 0, 1, 0]
min should be lower than max, increment should be positive and max-min should be divisible by increment. Those are restrictions on the input but not on the output : any wave can be generated.
This problem could be a textbook example of the use of Ruby's flip-flop operator.
As the question only makes sense when there is a non-negative integer steps such that high = low + steps * increment, I've replaced the method's argument high with steps.
def oscillate(low, steps, increment, length)
high = low + steps * increment
n = low
length.times.each_with_object([]) do |_,a|
a << n
n += (n==low)..(n==high-increment) ? increment : -increment
end
end
oscillate(0,3,1,9)
#=> [0, 1, 2, 3, 2, 1, 0, 1, 2]
oscillate(-1, 4, 2, 16)
#=> [-1, 1, 3, 5, 7, 5, 3, 1, -1, 1, 3, 5, 7, 5, 3, 1]
To show what's happening here I will modify the code a little and add some puts statements, then run it with the first example.
def oscillate(low, steps, increment, length)
high = low + steps * increment
puts "high = #{high}"
n = low
length.times.each_with_object([]) do |_,a|
a << n
diff = (n==low)..(n==high-increment) ? increment : -increment
print "n=#{n}, a<<n=#{a}, diff=#{diff}, "
n += diff
puts "n+=diff=#{n}"
end
end
oscillate(0,3,1,9)
high = 3
n=0, a<<n=[0], diff= 1, n+=diff=1
n=1, a<<n=[0, 1], diff= 1, n+=diff=2
n=2, a<<n=[0, 1, 2], diff= 1, n+=diff=3
n=3, a<<n=[0, 1, 2, 3], diff=-1, n+=diff=2
n=2, a<<n=[0, 1, 2, 3, 2], diff=-1, n+=diff=1
n=1, a<<n=[0, 1, 2, 3, 2, 1], diff=-1, n+=diff=0
n=0, a<<n=[0, 1, 2, 3, 2, 1, 0], diff= 1, n+=diff=1
n=1, a<<n=[0, 1, 2, 3, 2, 1, 0, 1], diff= 1, n+=diff=2
n=2, a<<n=[0, 1, 2, 3, 2, 1, 0, 1, 2], diff= 1, n+=diff=3
#=> [0, 1, 2, 3, 2, 1, 0, 1, 2]
Try this
def oscillate(a, b, step, num)
ramp_up = a.step(b, step).entries
ramp_down = ramp_up.drop(1).reverse.drop(1)
ramp_up.concat(ramp_down).cycle.take(num)
end
How does this work?
creates the ramp_up and ramp_down arrays
concatenates the two arrays
cycle returns an ever-repeating enumerator
take materializes num elements from that enumerator. Other than suggested in a comment, this does not recalculate anything. It just materializes entries from the enumerator.

How do I sort data from a list and append the the number of occurrences in the list

I am having trouble dissecting this data. I would like to find out how many #1 are in each list. After finding that number, I would like to append it to another list for later.
I seem to be getting the input:
--> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
When I really want:
--> [2, 2, 2, 2, 0, 2, 2, 1, 11, 0]
This is the code:
d = []
count = 0
b = [[1,3,6,2,7,3,9,2,7,1,7],
[1,5,8,3,0,3,6,2,7,2,1],
[1,5,2,6,8,6,2,5,1,8,9],
[5,2,5,2,1,8,1,5,2,4,6],
[5,7,2,7,3,7,3,7,3,9,2],
[1,5,8,3,0,3,6,2,7,2,1],
[5,2,5,2,1,8,1,5,2,4,6],
[3,6,1,5,7,8,4,3,6,3,3],
[1,1,1,1,1,1,1,1,1,1,1],
[3,4,5,6,8,5,7,5,7,3,7]]
for i in b:
for x in b:
if x == 1:
count =+ 1
d.append(count)
count = 0
print(d)
You are iterating over the wrong object in your second for loop, I believe you meant:
for x in i:
This is why you are getting 0s
There is a Counter class in the standard collections module, so you can simplify this:
>>> from collections import Counter
>>> [Counter(i)[1] for i in b]
[2, 2, 2, 2, 0, 2, 2, 1, 11, 0]
You can also do this without the Counter class a bit more verbosely:
>>> [sum(1 for x in i if x == 1) for i in b]
[2, 2, 2, 2, 0, 2, 2, 1, 11, 0]

Resources