How to remove "duplicate" edges in 2-D array in Numpy? - arrays

I'm working with a (15000, 2) array in Numpy from which I plan to build an adjacency matrix. Each row represents a vertex connection from node to i to j, or in other words, the first element of each row has an edge with the second element. For example, [24, 79] represents an edge between node 24 and 79.
If there exists a row that was [79, 24], I would like to remove it altogether because [24, 79] already exists.
Is there a way to remove these "duplicate" connections so that the overall array only consists of uni-directional vertices? I'm doing this step before I make symmetrize the matrix, where I add the matrix to its transpose.

You can do that by sorting the items in each row so to easily track duplicate edges (ie. duplicate rows). The later can be done using np.unique. Here is an example:
v = np.random.randint(0, 1_000, size=(15000, 2)) # len: 15000
result = np.unique(np.sort(v, axis=1), axis=0) # len: 14790
result contains the set of unique non-directed edges where, for each row, the smallest ID is the first item. The computation is done efficiently in O(n log n) time.
Note the parameter return_index and return_inverse of np.unique can be used to track the unsorted row source index. Also note that using a (2, 15_000) array is likely to be faster due to the operation being more SIMD-friendly and cache-friendly.

Related

Array Moves Chunks Of Neighboring Elements To A New Index

The Question
The question is simple, Imagine I have a list or an array (not linkedList)
list = [1, 2, ... 999999]
Now I wanna move elements from index 3000 to 600000 to index 100. The result should be easy to imagine
[1, 2,... 99, 100, 3000, 3001, ... 6000000, 101, 102, ...2999, 600001, 600002, ... 999999]
How to do those operations efficiently?
Judge My Thinking
Disclaimer: You can say this operation is exactly same as moving 101 to 2999 to index 600000. Which seems a more efficient operation. But that's not the algorithm my question is about, my question is about how to move more efficiently, so let's do the original question.
I can think of several ways:
Just do delete and insert for element 3000 to 600000. (What's the time complexity?)
Save elements from [3000, 600000] to a temporary space, then use delete insert to move everything from [101 to 2999] down to [597192, 600000] to make space to transfer [3000, 600000] back into index 100. Temporary holding data from [3000, 600000] will cost some memory, but does the copying make the whole operation slower? (Time complexity?)
is an attempt to improve 2. Same idea, but the move operation is not done by delete insert, but by manually copy [101,2999] to [597192, 600000]. (Time complexity? does it improve speed compared to delete insert)?
is an attempt to improve 2 or 3. Same idea, but no delete insert, but using many copying. But not copying everything from [3000, 600000], but only hold 1 element at a time in temporary memory, and move / copy everything in a complicated way. (Is this faster than others? Is it possible to implement? Can you show me the code / pseudo-code)
Is there better ideas?
Thank you for reading and thinking.
The algorithm you are after is known as rotate. There are two common ways to implement it. Both are running in O(length) time and O(1) space.
One, which is attributed to Dijkstra, is ultimately efficient in a sense that every element is moved just once. It is kind of tricky to implement, and requires a non-obvious setup. Besides, it may behave in a very cache-unfriendly manner. For details, see METHOD 3 (A Juggling Algorithm).
Another is very simple, cache-friendly, but moves each element twice. To rotate a range [l, r) around a midpoint m do
reverse(l, m);
reverse(m, r);
reverse(l, r);
I split the list at the "breakpoints", and reassembled the pieces. Creating a slice should be O(n), with n being the length of the slice. The longest slice is up to len(a) elements, so the storing of the list pieces should be O(n), with n being len(a). Reassembling the list pieces is O(n) as well, so this should be O(n) in total. The memory requirement is 2*len(a), since we store the slices as well, which sum up to the same length as a.
def truck(a, start, end, to):
a = list(a)
diff = end - start
to_left = to < start
split_save = a[to:to+diff]
split_take = a[start:end]
if to_left:
split_first = a[:to]
split_second = a[to+diff:start]
split_third = a[end:]
res = split_first + split_take + split_second + split_save + split_third
else:
split_first = a[:start]
split_second = a[end:to]
split_third = a[to+diff:]
res = split_first + split_save + split_second + split_take + split_third
return res
print(truck(range(10), 5, 8, 2))
# [0, 1, 5, 6, 7, 2, 3, 4, 8, 9]
print(truck(range(10), 2, 5, 8))
# [0, 1, 8, 9, 5, 6, 7, 2, 3, 4]
Let [l, r] be the segment you want to move, and L = r-l+1, the length of segment. N is total count of elements.
Delete and Insert at arbitrary position in array takes O(N), and delete and insert occurs O(L). So total time complexity is O(NL).
Same as #1. It takes O(NL) because of delete and insert.
3, 4. Copy and move takes O(L). Simply we can say it takes O(N)
Now, some fancy data structure for better complexity. You can use tree data structure to store linear array.
Especially, self-balancing binary search tree(BBST). It takes O(logN) to insert and delete one element.
Time complexity of moving segment to arbitrary position could be O(L logN), delete and insert each element.
You can find this data strcuture std::map in C++.
O(L logN) does not seem to be better. But, it can be better with BBST, to amortized O(log N)
You can gather elements in [l, r] to one subtree. Then cut this subtree from BBST. It is delete.
Insert this subtree at position you want.
For example, gather [3000, 600000] to one subtree and cut it from its parent. (Delete segment at once)
Make this subtree right child of 100th element in inorder, or left child of 101th element in inorder. (Insert segment at once)
Then, tree contains elements in order what you want.
Splay Tree would be good choice.

Keeping track of indices after sorting

So I have several arrays of positions, velocities, etc in 3D-space (vec3(x,y,z)) and another array which holds indices that are used to look-up in the vec3 arrays. This is all for a particle-system representing cloth (in case anyone was wondering).
The second array is far larger than the position array because for each particle, the second array represents a type of "spring" relationship with another particle. So for example, any given particle might have a "spring" attached to 3 different particles. So the first bunch of indices in the second array might look like this: [0, 1, 0, 2, 0, 3, 0, 4, ...]
The problem here is that the position array is sorted based on a hash function used for finding neighbors in a uniform grid. This means that the indices held in the 2nd array will no longer be valid. I'm trying to figure out a way to sort the positions and then still use the 2nd array to index properly.
One thought that I've had would be to have a 3rd array which stores the new indices based on the sorting function, but I am not sure how I could actually go about doing this.
Also, the reason the data is separated rather than being put into an object is that this is being run in CUDA and it is an optimization for speed/memory.
Is there a simple way of going about this? Thanks for any help.
Would something like this work?
Transfer your array of vec3(x, y, z)s into an array of pair(index, vec3(x, y, z)). Sort this new array by taking the hash function of the second element. The result will be a sorted array of pair(index, vec3(x, y, z)), where index is the vector's initial position in the array. Then, use this to construct a third "lookup" array of integers whose indices are the initial indices and whose values are the new values. Now to get a vector from your second array you do something like vector_pairs[lookup[spring[4]]].second.
Python-ish pseudocode:
vecs = ...
spring = ...
pair_vecs = [(index, vec) for index, vec in enumerate(vecs)]
pair_vecs.sort(key=lambda index, vec: hash(vec))
lookup = [0] * len(pair_vecs)
for new_index, (initial_index, vec) in enumerate(pair_vecs):
lookup[initial_index] = new_index

Optimize queries performed on a subarray

I recently interviewed at Google. Because of this question my process didn't move forward after 2 rounds.
Suppose you are given an array of numbers. You can be given queries
to:
Find the sum of the values between indexes i and j.
Update value at index i to a new given value.
Find the maximum of the values between indexes i and j.
Check whether the subarray between indexes i and j, both inclusive, is in ascending or descending order.
I gave him a solution but it was to check the subarray between the indexes i and j. He asked me to optimize it. I thought of using a hashtable so that if the starting index is same and the ending index is more than the previous found, we store the maximum and whether its in ascending or descending and check only the remaining subarray. But that also didn't optimize it as much as required.
I'd love to know how I can optimize the solution so as to make it acceptable.
Constraints:
Everything from [1,10^5]
Thanks :)
All this queries can be answered in O(log N) time per query in the worst case(with O(N) time for preprocessing). You can just build a segment tree and maintain the sum, the maximum and two boolean flags(they indicate whether the range which corresponds to this node is sorted in ascending/descending order or not) for each node. All this values can be recomputed efficiently for an update query because only O(log N) nodes can change(they lie on the path from the root to a leaf which corresponds to the changing element). All other range queries(sum, max, sorted or not) are decomposed into O(log N) nodes(due to the properties of a segment tree), and it is easy to combine the value of two nodes in O(1)(for example, for sum the result of combining 2 nodes is just the sum of values for these nodes).
Here is some pseudo code. It shows what data should be stored in a node and how to combine values of 2 nodes:
class Node {
bool is_ascending
bool is_descending
int sum
int max
int leftPos
int rightPos
}
Node merge(Node left, Node right) {
res = Node()
res.leftPos = left.leftPos
res.rightPos = right.rightPos
res.sum = left.sum + right.sum
res.max = max(left.max, right.max)
res.is_ascending = left.is_ascending and right.is_ascending
and array[left.rightPos] < array[right.leftPos]
res.is_descending = left.is_descending and right.is_descending
and array[left.rightPos] > array[right.leftPos]
return res
}
As andy pointed out in the comments: The queries are quite different in nature, so the "best" solution will probably depend on which query type is executed most frequently.
However, the task
Find the sum of the values between indexes i and j.
can efficiently be solved by performing a scan/prefix sum computation of the array. Imagine an array of int values
index: 0 1 2 3 4 5 6
array: [ 3, 8, 10, -5, 2, 12, 7 ]
Then you compute the Prefix Sum:
index: 0 1 2 3 4 5 6, 7
prefix: [ 0, 3, 11, 21, 16, 18, 30, 37 ]
Note that this can be computed particularly efficient in parallel. In fact, this is one important building block of many parallel algorithms, as described in the thesis "Vector Models for Data-Parallel Computing" by Guy E. Blelloch (thesis as PDF File).
Additionally, it can be computed in two ways: Either starting with the value from array[0], or starting with 0. This will, of course, affect how the resulting prefix array has to be accessed. Here, I started with 0, and made the resulting array one element longer than the input array. This may also be implemented differently, but in this case, it makes it easier to obey the array limits (although one would still have to clarify in which cases indices should be considered as inclusive or exclusive).
However, given this prefix sum array, one can compute the sum of elements between indices i and j in constant time, by simply subtracting the corresponding values of the prefix sum array:
sum(n=i..j)(array[n]) = (prefix[j+1] - prefix[i])
For example:
sum(n=2..5)(array[n]) = 10 + (-5) + 2 + 12 = 19
prefix[5+1] - prefix[2] = 30 - 11 = 19
For task 2,
Update value at index i to a new given value.
this would mean that the prefix sums would have to be updated. This could be done brute-force, in linear time, by just adding the difference of the old value and the new value to all prefix sums that appear after the modified element (but for this, also see the notes in the last section of this answer)
The tasks 3 and 4
Find the maximum of the values between indexes i and j.
Check whether the subarray between indexes i and j, both inclusive, is in ascending or descending order.
I could imagine that the maximum value could simply be tracked while building the prefix sums, as well as checking whether the values are only ascending or descending. However, when values are updated, this information would have to be re-computed.
In any case, there are some data structures that deal with prefix sums in particular. I think that a Fenwick tree might allow to implement some of the O(n) operations mentioned above in O(logn), but I have not yet looked at this in detail.

Extracting 2 numbers n times and placing back the addition in O(n) instead of O(n*log(n))

I'm presenting a problem my professor showed in class, with my O(n*log(n)) solution:
Given a list of n numbers we'd like to perform the following n-1 times:
Extract the two minimal elements x,y from the list and present them
Create a new number z , where z = x+y
Put z back into the list
Suggest a data structure and algorithm for O(n*log(n)) , and O(n)
Solution:
We'll use a minimal heap:
Creating the heap one time only would take O(n). After that, extracting the two minimal elements would take O(log(n)). Placing z into the heap would take O(log(n)).
Performing the above n-1 times would take O(n*log(n)), since:
O(n)+O(n∙(logn+logn ))=O(n)+O(n∙logn )=O(n∙logn )
But how can I do it in O(n)?
EDIT:
By saying: "extract the two minimal elements x,y from the list and present them ", I mean printf("%d,%d" , x,y), where x and y are the smallest elements in the current list.
This is not a full answer. But if the list was sorted, then your problem is easiy doable in O(n). To do it, arrange all of the numbers in a linked list. Maintain a pointer to a head, and somewhere in the middle. At each step, take the top two elements off of the head, print them, advance the middle pointer until it is where the sum should go, and insert the sum.
The starting pointer will move close to 2n times and the middle pointer will move about n times, with n inserts. All of those operations are O(1) so the sum total is O(n).
In general you cannot sort in time O(n), but there are a number of special cases in which you can. So in some cases it is doable.
The general case is, of course, not solvable in time O(n). Why not? Because given your output, in time O(n) you can run through the output of the program, build up the list of pairwise sums in order as you go, and filter them out of the output. What is left is the elements of the original list in sorted order. This would give a O(n) general sorting algorithm.
Update: I was asked to show how could you go from the output (10, 11), (12, 13), (14, 15), (21, 25), (29, 46) to the input list? The trick is that you always keep everything in order then you know how to look. With positive integers, the next upcoming sum to use will always be at the start of that list.
Step 0: Start
input_list: (empty)
upcoming sums: (empty)
Step 1: Grab output (10, 11)
input_list: 10, 11
upcoming_sums: 21
Step 2: Grab output (12, 13)
input_list: 10, 11, 12, 13
upcoming_sums: 21, 25
Step 3: Grab output (14, 15)
input_list: 10, 11, 12, 13, 14, 15
upcoming_sums: 21, 25, 29
Step 4: Grab output (21, 25)
input_list: 10, 11, 12, 13, 14, 15
upcoming_sum: 29, 46
Step 5: Grab output (29, 46)
input_list: 10, 11, 12, 13, 14, 15
upcoming_sum: 75
This isn't possible in the general case.
Your problem statement reads that you must reduce your array to a single element, performing a total of n-1 reduction operations. Therefore, the number of reduction operations performed is on the order of O(n). To achieve a overall running time of O(n), each reduction operation must run in O(1).
You have clearly defined your reduction operation:
remove the 2 minimal elements in the array and print them, then
insert the sum of those elements into the array.
If your data structure were a sorted list, it is trivial to remove two minimal elements in O(1) time (pop them off the end of the list). However, reinserting an element in O(1) is not possible (in the general case). As SteveJessop pointed out, if you could insert into a sorted list in O(1) time, the resultant operations would constitute an O(n) sorting algorithm. But there is no such known algorithm.
There are some exceptions here. If your numbers are integers, you may be able to use "radix insert" to achieve O(1) inserts. If your array of numbers are sufficiently sparse in the number line, you may be able to deduce insert points in O(1). There are numerous other exceptions, but they are all exceptions.
This answer doesn't answer your question, per se, but I believe it's relevant enough to warrant an answer.
If the range of values is less than n, then this can be solved in O(n).
1> Create an array mk of size equal to range of values and initialize it to all zero
2> traverse through the array and increment value of mk at the position of the array element.
i.e if the array element is a[i] then increment mk[a[i]]
3) For presenting the answers after each of the n-1 operations follow the following steps:
There are two cases:
Case 1 : all of a[i] are positive
traverse through mk array from 0 to its size
cnt = 0
do this till cnt doesn't equal 2
grab a nonzero element decrease its value by 1 and increment cnt by 1
you can get two minimum values in this way
present them
now do mk[sum of two minimum]++
Case 2 : some of a[i] is negative
<still to update>
O(nlogn) is easy - just use a heap, treap or skiplist.
O(n) sounds tough.
https://en.wikipedia.org/wiki/Heap_%28data_structure%29
https://en.wikipedia.org/wiki/Treap
https://en.wikipedia.org/wiki/Skip_list

efficient sorted Cartesian product of 2 sorted array of integers

Need Hints to design an efficient algorithm that takes the following input and spits out the following output.
Input: two sorted arrays of integers A and B, each of length n
Output: One sorted array that consists of Cartesian product of arrays A and B.
For Example:
Input:
A is 1, 3, 5
B is 4, 8, 10
here n is 3.
Output:
4, 8, 10, 12, 20, 24, 30, 40, 50
Here are my attempts at solving this problem.
1) Given that output is n^2, Efficient algorithm can't do any better than O(n^2) time complexity.
2) First I tried a simple but inefficient approach. Generate Cartesian product of A and B. It can be done in O(n^2) time complexity. we need to store, so we can do sorting on it. Therefore O(n^2) space complexity too. Now we sort n^2 elements which can't be done better than O(n^2logn) without making any assumptions on the input.
Finally I have O(n^2logn) time and O(n^2) space complexity algorithm.
There must be a better algorithm because I've not made use of sorted nature of input arrays.
If there's a solution that's better than O(n² log n) it needs to do more than just exploit the fact that A and B are already sorted. See my answer to this question.
Srikanth wonders how this can be done in O(n) space (not counting the space for the output). This can be done by generating the lists lazily.
Suppose we have A = 6,7,8 and B = 3,4,5. First, multiply every element in A by the first element in B, and store these in a list:
6×3 = 18, 7×3 = 21, 8×3 = 24
Find the smallest element of this list (6×3), output it, replace it with that element in A times the next element in B:
7×3 = 21, 6×4 = 24, 8×3 = 24
Find the new smallest element of this list (7×3), output it, and replace:
6×4 = 24, 8×3 = 24, 7×4 = 28
And so on. We only need O(n) space for this intermediate list, and finding the smallest element at each stage takes O(log n) time if we keep the list in a heap.
If you multiply a value of A with all values of B, the result list is still sorted. In your example:
A is 1, 3, 5
B is 4, 8, 10
1*(4,8,10) = 4,8,10
3*(4,8,10) = 12,24,30
Now you can merge the two lists (exactly like in merge sort). You just look at both list heads and put the smaller one in the result list. so here you would select 4, then 8 then 10 etc.
result = 4,8,10,12,24,30
Now you do the same for result list and the next remaining list merging 4,8,10,12,24,30 with 5*(4,8,10) = 20,40,50.
As merging is most efficient if both lists have the same length, you can modify that schema by dividing A in two parts, do the merging recursively for both parts, and merge both results.
Note that you can save some time using a merge approach as is isn't required that A is sorted, just B needs to be sorted.

Resources