Designing a wireless sensor algorithm - arrays

So I'm given a sorted array containing n >= 2 number of integers. These integers are used to represent wireless sensors and each has a broadcast radius of 2, meaning that if I had a number "4" it can reach at most "2" or "6". So I need to design an algorithm that returns an array containing all pairs of sensors (as subarrays) that can communicate with each other, possibly having their message forwarded by some intermediate sensor, eg. "8" is able to communicate with "12" given "10" exists in the array. The algorithm also needs to run in O(n^2) time.
So at first it was pretty simple, I would just get the length of the array, n, and iterate through it using a while loop (i < n) and if the current element + 2 was greater or equal to the following element, add its index and the following element's index to a subarray and add it to an empty array. But I was having problems with the intermediate sensor part. How would I find the intermediate sensor connections though?

Sort the list (takes O(NlogN) time)
Start traversing the array
See if the current element can communicate with our current set. If yes, then add it to the set and continue. Else, store the old set, create a new one and add current element to it.
For each set, generate all pairs of sensors that can communicate.
Something like:
def generate_pairs(array):
pairs = []
array_length = len(array)
for i in range(0, array_length):
for j in range(i+1, array_length):
pairs.append([array[i],array[j]])
return pairs
main_list = [1,2,4,7,9,10,13]
main_list.sort()
curr_set = [main_list[0]]
all_sets = []
for i in range (1,len(main_list)):
if main_list[i]-main_list[i-1] <=2:
curr_set.append(main_list[i])
else:
all_sets.append(curr_set)
curr_set = [main_list[i]]
all_pairs = []
for i in all_sets:
all_pairs += generate_pairs(i)
print(all_pairs)
# prints [[1, 2], [1, 4], [2, 4], [7, 9], [7, 10], [9, 10]]

Related

For each element, count how many given subarrays contain it

While I was solving a problem in a coding contest, I found that I needed to do this: Given the pairs (i, j) indicating the indexes of a subarray; for each element, count how many subarrays contain it. For example:
We have the array [7, -2, -7, 0, 6], and the pairs are (0, 2), (1, 4), (2, 3), (0, 3), then the result array will be [2, 3, 4, 3, 1], since the first element is in the subarrays (0, 2), (0, 3), the second one is in (0, 2), (1, 4), (0, 3), etc.
One way of doing it would of course be manually counting, using an array and count, but that will likely give me TLE because the array size and number of pairs is too big to do it. I've also read somewhere else that you can use an extra array storing "stuff" inside it (add/subtract something for every subarray), and then traverse through that array to find out, but I don't remember where I read it.
Here's the original problem if you also have any improvements on my algorithm:
Given an array of size n, for each i-th element in the array (0 <= i <= n-1), count how many balanced subarrays contain that element. A balanced subarray is a subarray whose sum is equal to 0.
I have known a way to find subarrays with its sum to be equal to 0: https://www.geeksforgeeks.org/print-all-subarrays-with-0-sum/ . But the second task, which I have stated above, I haven't figured it out yet. If you have some ideas about this, please let me know and thank you very much.
You could take each pair and mark a +1 where it starts and -1 where it stops (in a separate array). These represent changes in the number of ranges that overlap a certain index. Then iterate that list to collect and accumulate those changes into absolute numbers (number of overlapping ranges).
Here is an implementation in JavaScript for the ranges you have given as example -- the content of the given list really is not relevant for this, only its size (n=5 in the example):
let pairs = [[0, 2], [1, 4], [2, 3], [0, 3]];
let n = 5;
// Translate pairs to +1/-1 changes in result array
let result = Array(n+1).fill(0); // Add one more dummy entry
for (let pair of pairs) {
result[pair[0]]++;
result[pair[1] + 1]--; // after(!) the range
}
result.pop(); // ignore last dummy entry
// Accumulate changes from left to right
for (let i = 1; i < n; i++) {
result[i] += result[i-1];
}
console.log(result);

What's the most efficient way to manipulate Arrays in Ruby

Im trying to make the following code more efficient but ran out of ideas, so looking for some help:
I receive an interval x and an Array of Integers space, I need to separate the space array into segments of x interval so for example if
space = [1, 2, 3, 1, 2]
x = 2
# The intervals Would be
[1, 2][2, 3][3, 1][1, 2]
So Then I need to find the minimum on each interval and then find the Max value of the minimums, so in the example above the minimums would be:
[1, 2, 1, 1]
Then I need to return the max value of the minimums that would be 2
Below is my solution, most test cases pass but some of them are failing because the time execution is exceeded. Any idea about how to make the following code more efficient?
def segment(x, space)
minimums = {}
space.each_with_index do |_val, index|
# 1 Create the segment
mark = x - 1
break if mark >= space.length
segment = space[index..mark]
x = x + 1
# 2 Find the min of the segment
minimum = segment.min
minimums[index] = minimum
end
# Compare the maximum of the minimums
return minimums.values.max
end
I tried making the minimums a hash instead of an array but didn't work. Also I thought I would convert the space array to a hash and manipulate the a hash instead of an Array, but I think that would be even more complicated...
You don’t need to collect the segments and process them later. Just walk the array once, keeping the largest min of each segment as you go.
Also each_with_index is a waste. Just walk the array yourself taking successive slices.
Simple example:
# initial conditions
space = [1, 2, 3, 1, 2]
x = 2
# here we go
min = space.min
(0..space.length-x).each do |i|
min = [space[i,x].min, min].max
end
puts min
As usual in ruby, there's always a more elegant way. For example, no need to create the slices; let ruby do it (each_cons):
# initial conditions
space = [1, 2, 3, 1, 2]
x = 2
# here we go
min = space.min
space.each_cons(x) do |slice|
min = [slice.min, min].max
end
puts min
And of course once you understand how this works, it can be refined even further (e.g. as shown in the comment below).

How do I compare integer indexes in arrays when there are duplicate values?

First, some necessary background. I'm trying to make a number-based version of the game Mastermind as a way of learning to code in Ruby. My code basically works like this:
The computer generates an array (#computer_sequence) of 4 random numbers from 1-5
The user enters a 4 digit sequence, which winds up in an array called #user_array.
A method, called compare, iterates through #user_array, comparing the value and index of each number to those in #computer_sequence. The program then tells the user how many of their numbers have the correct value and the correct position, or how many numbers have the correct value only.
The problem: If there are multiple instances of a number in an array, they get the same index, right? Like if I have the array [1, 3, 3, 4], the number three has an index of 1, even though there are two 3s. For this program to work, though, each number has to have a unique position (is index even the word I want here?) in the array, even if the number occurs multiple times. Does that make sense?
Also, here's the code for the compare method:
def compare
value_only = 0
value_and_place = 0
puts "The computer's values are: #{#computer_sequence}"
puts "The user's values are: #{#user_array}"
#user_array.each do |candidate|
#computer_sequence.each do |computer_number|
if candidate == computer_number && #user_array.index(candidate) == #computer_sequence.index(computer_number)
value_and_place +=1
elsif candidate == computer_number && #user_array.index(candidate) != #computer_sequence.index(computer_number)
value_only +=1
end
end
end
Suppose
n = 4
computer = Array.new(n) { [1,2,3,4,5].sample }
#=> [3, 2, 3, 3]
user_digits = [2, 4, 2, 3]
First compute pairs of elements at the same index of computer and user_digits.
pairs = computer.zip(user_digits)
#=> [[3, 2], [2, 4], [3, 2], [3, 3]]
Compute number of values that match at the same position
pairs.count { |c,u| c==u }
#=> 1
Compute number of values that match at different positions
First remove the matches at the same positions of computer and user_digits.
comp, users = pairs.reject { |c,u| c==u }.transpose
#=> [[3, 2, 3], [2, 4, 2]]
meaning
comp #=> [3, 2, 3]
users #=> [2, 4, 2]
Now step through users removing the first matching element in comp (if there is one).
users.each do |n|
i = comp.index(n)
comp.delete_at(i) if i
end
So now:
comp #=> [3,3]
meaning that the number of elements that match at different positions is:
users.size-comp.size
#=> 1
Notice that we could alternatively compute the number of values that match at the same position as
n - users.size
For n equal to 4 this doesn’t offer any significant time saving, but it would if we had a problem with the same structure and n were large.
Alternative calculation
After computing
comp, users = pairs.reject { |c,u| c==u }.transpose
we could write
users.size - comp.difference(users).size
#=> 1
where Array#difference is as I defined it in my answer here.
Here
comp.difference(users)
#=> [3,3]
No, equal elements in an array don't have the same index. Maybe you're thinking that because Array#index only returns the index of the first element equal to its argument. But there are many ways to see that other equal elements have their own indexes. For example,
a = [1, 3, 3, 4]
a[1] == 3 # true
a[2] == 3 # also true
Aside from that issue, your algorithm doesn't quite match the rules of Mastermind. If there is one three in the computer's sequence and the player guesses two threes, both in different positions than the three in the computer's sequence, the player should be told that only one element of their sequence matches the computer's sequence in value but not position.
Given the above, plus that I think it would be clearer to calculate the two numbers separately, I'd do it like this:
value_and_place = 4.times { |i| #user_array[i] == #computer_sequence[i] }
value_only = (#user_array & #computer_sequence).length - value_and_place
That's less efficient than the approach you're taking, but CPU efficiency isn't important for 4-element arrays.
You can pass in the index value to your loop for each candidate using the each_with_index method. So when the first 3 is passed in, index will be 1 and when the second 3 is passed in, index will be 2.
The problem with using .index(candidate) is it returns the first index.
Try this:
#user_array.each_with_index do |candidate, index|
#computer_sequence.each do |computer_number|
if candidate == computer_number && candidate == #computer_sequence[index]
value_and_place +=1
elsif candidate == computer_number && candidate != #computer_sequence[index]
value_only +=1
end
end
end

Number of Distinct Subarrays

I want to find an algorithm to count the number of distinct subarrays of an array.
For example, in the case of A = [1,2,1,2],
the number of distinct subarrays is 7:
{ [1] , [2] , [1,2] , [2,1] , [1,2,1] , [2,1,2], [1,2,1,2]}
and in the case of B = [1,1,1], the number of distinct subarrays is 3:
{ [1] , [1,1] , [1,1,1] }
A sub-array is a contiguous subsequence, or slice, of an array. Distinct means different contents; for example:
[1] from A[0:1] and [1] from A[2:3] are not distinct.
and similarly:
B[0:1], B[1:2], B[2:3] are not distinct.
Construct suffix tree for this array. Then add together lengths of all edges in this tree.
Time needed to construct suffix tree is O(n) with proper algorithm (Ukkonen's or McCreight's algorithms). Time needed to traverse the tree and add together lengths is also O(n).
Edit: I think about how to reduce iteration/comparison number.
I foud a way to do it: if you retrieve a sub-array of size n, then each sub-arrays of size inferior to n will already be added.
Here is the code updated.
List<Integer> A = new ArrayList<Integer>();
A.add(1);
A.add(2);
A.add(1);
A.add(2);
System.out.println("global list to study: " + A);
//global list
List<List<Integer>> listOfUniqueList = new ArrayList<List<Integer>>();
// iterate on 1st position in list, start at 0
for (int initialPos=0; initialPos<A.size(); initialPos++) {
// iterate on liste size, start on full list and then decrease size
for (int currentListSize=A.size()-initialPos; currentListSize>0; currentListSize--) {
//initialize current list.
List<Integer> currentList = new ArrayList<Integer>();
// iterate on each (corresponding) int of global list
for ( int i = 0; i<currentListSize; i++) {
currentList.add(A.get(initialPos+i));
}
// insure unicity
if (!listOfUniqueList.contains(currentList)){
listOfUniqueList.add(currentList);
} else {
continue;
}
}
}
System.out.println("list retrieved: " + listOfUniqueList);
System.out.println("size of list retrieved: " + listOfUniqueList.size());
global list to study: [1, 2, 1, 2]
list retrieved: [[1, 2, 1, 2], [1, 2, 1], [1, 2], [1], [2, 1, 2], [2, 1], [2]]
size of list retrieved: 7
With a list containing the same patern many time the number of iteration and comparison will be quite low.
For your example [1, 2, 1, 2], the line if (!listOfUniqueList.contains(currentList)){ is executed 10 times. It only raise to 36 for the input [1, 2, 1, 2, 1, 2, 1, 2] that contains 15 different sub-arrays.
You could trivially make a set of the subsequences and count them, but i'm not certain it is the most efficient way, as it is O(n^2).
in python that would be something like :
subs = [tuple(A[i:j]) for i in range(0, len(A)) for j in range(i + 1, len(A) + 1)]
uniqSubs = set(subs)
which gives you :
set([(1, 2), (1, 2, 1), (1,), (1, 2, 1, 2), (2,), (2, 1), (2, 1, 2)])
The double loop in the comprehension clearly states the O(n²) complexity.
Edit
Apparently there are some discussion about the complexity. Creation of subs is O(n^2) as there are n^2 items.
Creating a set from a list is O(m) where m is the size of the list, m being n^2 in this case, as adding to a set is amortized O(1).
The overall is therefore O(n^2).
Right my first answer was a bit of a blonde moment.
I guess the answer would be to generate them all and then remove duplicates. Or if you are using a language like Java with a set object make all the arrays and add them to a set of int[]. Sets only contain one instance of each element and automatically remove duplicates so you can just get the size of the set at the end
I can think of 2 ways...
first is compute some sort of hash then add to a set.
if on adding your hashes are the same is an existing array... then do a verbose comparison... and log it so that you know your hash algorithm isn't good enough...
The second is to use some sort of probable match and then drill down from there...
if number of elements is same and the total of the elements added together is the same, then check verbosely.
Create an array of pair where each pair store the value of the element of subarray and its index.
pair[i] = (A[i],i);
Sort the pair in increasing order of A[i] and then decreasing order of i.
Consider example A = [1,3,6,3,6,3,1,3];
pair array after sorting will be pair = [(1,6),(1,0),(3,7),(3,5),(3,3),(3,1),(6,4),(6,2)]
pair[0] has element of index 6. From index 6 we can have two sub-arrays [1] and [1,3]. So ANS = 2;
Now take each consecutive pair one by one.
Taking pair[0] and pair[1],
pair[1] has index 0. We can have 8 sub-arrays beginning from index 0. But two subarrays [1] and [1,3] are already counted. So to remove them, we need to compare longest common prefix of sub-array for pair[0] and pair[1]. So longest common prefix length for indices beginning from 0 and 6 is 2 i.e [1,3].
So now new distinct sub-arrays will be [1,3,6] .. to [1,3,6,3,6,3,1,3] i.e. 6 sub-arrays.
So new value of ANS is 2+6 = 8;
So for pair[i] and pair[i+1]
ANS = ANS + Number of sub-arrays beginning from pair[i+1] - Length of longest common prefix.
The sorting part takes O(n logn).
Iterating each consecutive pair is O(n) and for each iteration find longest common prefix takes O(n) making whole iteration part O(n^2). Its the best I could get.
You can see that we dont need pair for this. The first value of pair, value of element was not required. I used this for better understanding. You can always skip that.

Cut a set of ranges of integers from another set of ranges

I have two arrays of ranges in this form:
wanted = {[10, 15], [20, 25]}
cut = {[5, 12], [22, 24]}
So wanted is an array of two elements (ranges) - [10, 15] and [20, 25].
Each of the two arrays fulfil these conditions:
It is sorted by the first value in each range of integers
The ranges will never overlap (e.g. [10, 15], [15, 25] is not possible)
This also means that each range is unique within the array (no [1, 5], [1, 5])
If a range is just one integer wide, it will be displayed as [5, 5] (beginning and end are equal)
I now want to obtain an array of ranges, where all ranges from cut have been removed from the ranges in wanted.
result = {[13, 15], [20, 21], [25, 25]}
Is there some brilliant algorithm better / easier / faster than the below?
For each element in wanted, compare that element to one element after another from cut until the element from cut ends above the element from wanted.
Say there are n elements in wanted and m elements in cut.
The following is an O(m + n) algorithm to perform the required task:
j = 1
result = {}
for i = 1:n
// go to next cut while current cut ends before current item
while j <= m && cut[j].end < wanted[i].start
j++
// cut after item, thus no overlap
if j > m || cut[j].start > wanted[i].end
result += (wanted[i].start, wanted[i].end)
else // overlap
// extract from start to cut start
if cut[j].start > wanted[i].start
result += (wanted[i].start, cut[j].start-1)
// extract from cut end to end
if cut[j].end < wanted[i].end
result += (cut[j].end+1, wanted[i].end)
j++
Note that, asymptotically, you can't do better than O(m + n), since it should be reasonably easy to prove that you need to look at every element (in the worst case).
What is the biggest size which wanted and cut may be? Comparing the "first element from wanted" with "all from cut" will take O(n^2) run time, i.e. very slow if the arrays are large.
It would be much faster to work over each array in parallel until you reach the end of both, something like a "merge".

Resources