簡體   English   中英

通過單獨的索引/索引數組對數組進行排序的最快方法

[英]Fastest way to sort an array by a separate array of indices/indexes

假設我有以下設置:

int[] vectorUsedForSorting = new int[] { 1,0,2,6,3,4,5 }
int[] vectorToBeSorted = new int[] {1,2,3,4,5,6,7}

使用vectorUsedForSortingvectorToBeSorted進行排序的最有效/快速的方法是什么? 例如,我希望vectorToBeSorted[0]成為vectorToBeSorted[1] ,因為vectorUsedForSorting的第一個元素是1 (即, vectorToBeSorted[0]應該變成`vectorToBeSorted[vectorUsedForSorting[0]]等)。

我的目標是在排序算法完成后將vectorToBeSorted設置為[2,1,3,5,6,7,4]

我希望能夠非常快速地實現目標。 請注意,計算復雜性應該是主要關注點,因為我將對大小為1,000,000或更多的數組進行排序。

如果可能的話,我的目標是亞線性時間復雜度。

有兩種方法可以攻擊它。 第一個是復制快速排序算法,並使用可以處理你所擁有的間接的東西來更改訪問和交換值部分:

int valueAt(int index) { return vectorUsedForSorting[index]; }
int swap(int i1, int i2) {
    int tmp = vectorUsedForSorting[i1];
    vectorUsedForSorting[i1] = vectorUsedForSorting[i2];
    vectorUsedForSorting[i2] = tmp;

    tmp = vectorToBeSorted[i1];
    vectorToBeSorted[i1] = vectorToBeSorted[i2];
    vectorToBeSorted[i2] = tmp;
}

第二種方法是將值復制到新對象中:

public class Item {
    int index;
    int value;
}

創建一個數組,並使用兩個數組中的值創建的Item填充它。 然后,您可以創建一個Comparator<Item> ,它按index它們進行比較。

如果有了這個,可以使用Arrays.sort(items, comparator)對數組進行Arrays.sort(items, comparator)

如果這還不夠快,那么你可以創建N個線程並讓每個線程排序原始數組的1 / N. 完成后,使用合並排序中的合並步驟來加入結果。

您可以創建一個新數組,對該數組執行排序並將vectorToBeSorted設置為新數組。

int size = vectorToBeSorted.length;
int[] array = new int[size];
for (int i = 0; i < size; ++i)
    array[vectorUsedForSorting[i]] = vectorToBeSorted[i];
vectorToBeSorted = array;

編輯

如果您希望能夠進行排序,則需要循環,交換適當的值。

int size = vectorToBeSorted.length;
for (int i = 0; i < size; ++i) {
    int index = vectorUsedForSorting[i];
    int value = vectorToBeSorted[index];

    vectorUsedForSorting[i] = vectorUsedForSorting[index];
    vectorToBeSorted[index] = vectorToBeSorted[i];

    vectorUsedForSorting[index] = index;
    vectorToBeSorted[i] = value;
}

如果您能夠創建一個比較索引的結構。 你可以用一種; 但是,排序肯定比線性解決方案慢。

在這種情況下,這兩個陳述是等價的。

array[vectorUsedForSorting[i]] = vectorToBeSorted[i];
array[i] = vectorToBeSorted[vectorUsedForSorting[i]];

當性能成為一個問題,並且陣列很大時,你至少需要考慮一個並行實現(特別是因為這個問題非常平行:它不是很費力,並且隨着內核數量的增加應該會產生一個不錯的近線性加速):

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ArrayReordering
{
    public static void main(String[] args)
    {
        basicTest();
        performanceTest();
    }

    private static void basicTest()
    {
        int[] vectorUsedForSorting = new int[] { 1,0,2,6,3,4,5 };
        int[] vectorToBeSorted = new int[] {1,2,3,4,5,6,7};      
        int[] sortedVectorLinear = new int[vectorToBeSorted.length];
        int[] sortedVectorParallel = new int[vectorToBeSorted.length];

        sortLinear(vectorUsedForSorting, vectorToBeSorted, sortedVectorLinear);
        sortParallel(vectorUsedForSorting, vectorToBeSorted, sortedVectorParallel);

        System.out.println("Result Linear   "+Arrays.toString(sortedVectorLinear));
        System.out.println("Result Parallel "+Arrays.toString(sortedVectorParallel));
    }

    private static void performanceTest()
    {
        for (int n=1000000; n<=50000000; n*=2)
        {
            System.out.println("Run with "+n+" elements");

            System.out.println("Creating input data");
            int vectorUsedForSorting[] = createVectorUsedForSorting(n);
            int vectorToBeSorted[] = new int[n];
            for (int i=0; i<n; i++)
            {
                vectorToBeSorted[i] = i;
            }
            int[] sortedVectorLinear = new int[vectorToBeSorted.length];
            int[] sortedVectorParallel = new int[vectorToBeSorted.length];

            long before = 0;
            long after = 0;

            System.out.println("Running linear");
            before = System.nanoTime();
            sortLinear(vectorUsedForSorting, vectorToBeSorted, sortedVectorLinear);
            after = System.nanoTime();
            System.out.println("Duration linear   "+(after-before)/1e6+" ms");

            System.out.println("Running parallel");
            before = System.nanoTime();
            sortParallel(vectorUsedForSorting, vectorToBeSorted, sortedVectorParallel);
            after = System.nanoTime();
            System.out.println("Duration parallel "+(after-before)/1e6+" ms");

            //System.out.println("Result Linear   "+Arrays.toString(sortedVectorLinear));
            //System.out.println("Result Parallel "+Arrays.toString(sortedVectorParallel));
            System.out.println("Passed linear?   "+
                Arrays.equals(vectorUsedForSorting, sortedVectorLinear));
            System.out.println("Passed parallel? "+
                Arrays.equals(vectorUsedForSorting, sortedVectorParallel));
        }
    }

    private static int[] createVectorUsedForSorting(int n)
    {
        // Not very elegant, just for a quick test...
        List<Integer> indices = new ArrayList<Integer>();
        for (int i=0; i<n; i++)
        {
            indices.add(i);
        }
        Collections.shuffle(indices);
        int vectorUsedForSorting[] = new int[n];
        for (int i=0; i<n; i++)
        {
            vectorUsedForSorting[i] = indices.get(i);
        }
        return vectorUsedForSorting;
    }

    private static void sortLinear(
        int vectorUsedForSorting[], int vectorToBeSorted[], 
        int sortedVector[])
    {
        sortLinear(vectorUsedForSorting, vectorToBeSorted, 
            sortedVector, 0, vectorToBeSorted.length);
    }

    static void sortParallel(
        final int vectorUsedForSorting[], final int vectorToBeSorted[], 
        final int sortedVector[])
    {
        int numProcessors = Runtime.getRuntime().availableProcessors();
        int chunkSize = (int)Math.ceil((double)vectorToBeSorted.length / numProcessors);
        List<Callable<Object>> tasks = new ArrayList<Callable<Object>>();
        ExecutorService executor = Executors.newFixedThreadPool(numProcessors);
        for (int i=0; i<numProcessors; i++)
        {
            final int min = i * chunkSize;
            final int max = Math.min(vectorToBeSorted.length, min + chunkSize);
            Runnable task = new Runnable()
            {
                @Override
                public void run()
                {
                    sortLinear(vectorUsedForSorting, vectorToBeSorted, 
                        sortedVector, min, max);
                }
            };
            tasks.add(Executors.callable(task));
        }
        try
        {
            executor.invokeAll(tasks);
        }
        catch (InterruptedException e)
        {
            Thread.currentThread().interrupt();
        }
        executor.shutdown();
    }

    private static void sortLinear(
        int vectorUsedForSorting[], int vectorToBeSorted[], 
        int sortedVector[], int min, int max)
    {
        for (int i = min; i < max; i++)
        {
            sortedVector[i] = vectorToBeSorted[vectorUsedForSorting[i]];
        }          
    }

}

怎么樣:

int size = size(vectorUsedForSorting); 
int [] sortedVector = new int[size];
for (int i = 0; i < size; ++i)
{
    sortedVector[i] = vectorToBeSorted[vectorUsedForSorting[i]];
}  

還是必須在排序?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM