Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting an array of integers in nlog(n) time without using comparison operators

Imagine there's have an array of integers but you aren't allowed to access any of the values (so no Arr[i] > Arr[i+1] or whatever). The only way to discern the integers from one another is by using a query() function: this function takes a subset of elements as inputs and returns the number of unique integers in this subset. The goal is to partition the integers into groups based on their values — integers in the same group should have the same value, while integers in different groups have different values. The catch - the code has to be O(nlog(n)), or in other words the query() function can only be called O(nlog(n)) times.

I've spent hours optimizing different algorithms in Python, but all of them have been O(n^2). For reference, here's the code I start out with:

n = 100
querycalls = 0
secretarray = [random.randint(0, n-1) for i in range(n)]
def query(items):
    global querycalls
    querycalls += 1
    return len(set(items))
groups = []

secretarray generates a giant random list of numbers of length n. querycalls keeps track of how much the function is called. groups are where the results go.

The first thing I did was try to create an algorithm based off of merge sort (split the arrays down and then merge based on the query() value) but I could never get it below O(n^2).

like image 899
Muffinlicious Avatar asked Nov 04 '21 11:11

Muffinlicious


People also ask

How do you sort without comparison?

The sorting algorithms that sort the data without comparing the elements are called non-comparison sorting algorithms. The examples for non-comparison sorting algorithms are counting sort, bucket sort, radix sort, and others.

How do you sort an array in log N time?

It essentially follows two steps: Divide the unsorted list into sub-lists until there are N sub-lists with one element in each ( N is the number of elements in the unsorted list). Merge the sub-lists two at a time to produce a sorted sub-list; repeat this until all the elements are included in a single list.


1 Answers

Let's say you have an element x and an array of distinct elements, A = [x0, x1, ..., x_{k-1}] and want to know if the x is equivalent to some element in the array and if yes, to which element.

What you can do is a simple recursion (let's call it check-eq):

  • Check if query([x, A]) == k + 1. If yes, then you know that x is distinct from every element in A.
  • Otherwise, you know that x is equivalent to some element of A. Let A1 = A[:k/2], A2 = A[k/2+1:]. If query([x, A1]) == len(A1), then you know that x is equivalent to some element in A1, so recurse in A1. Otherwise recurse in A2.

This recursion takes at most O(logk) steps. Now, let our initial array be T = [x0, x1, ..., x_{n-1}]. A will be an array of "representative" of the groups of elements. What you do is first take A = [x0] and x = x1. Now use check-eq to see if x1 is in the same group as x0. If no, then let A = [x0, x1]. Otherwise do nothing. Proceed with x = x2. You can see how it goes.

Complexity is of course O(nlogn), because check-eq is called exactly n-1 times and each call take O(logn) time.

like image 113
Franciszek Malinka Avatar answered Oct 19 '22 12:10

Franciszek Malinka