簡體   English   中英

如何以最快的方式交叉兩個排序的數組?

[英]How to intersect two sorted arrays the fastest possible way?

我有兩個巨大的排序數組(每個~100K項)。 我需要非常快地交叉它們。 現在我以標准的方式做到這一點:

  • 如果a [i] <b [j]則i ++
  • 如果a [i]> b [j]那么j ++
  • else:在交集,i ++,j ++中添加[i]

但是完成時間太長(~350微秒),導致整體性能相當差。 有沒有辦法更快地做到這一點?

PS交叉口尺寸不大於1000件(平均而言),我只需要25到100件。

並行運行2個100k陣列需要大約200k比較。 您目前正在以350微秒= 350k納秒的速度完成它。 所以你的每個比較時間不到2納秒。 如果你的CPU大約是4 GHz,那么這是8個時鍾周期。

那很好。 你可以嘗試復雜,檢測跑步等等,但是你可能會因為管道檔位而傷害自己,而不是節省工作。

只有兩種方法可以加快速度。 減少工作量,或增加更多工人。

你已經表明減少工作是可行的,這就是為什么Tamas Hegedus建議的。 而不是創建交集,創建一個Iterator ,它將返回交集中的下一個東西。 這將要求您重寫使用所述迭代器的邏輯,但是您將在當前計算的10%以下進行。 這將快接近10倍。

至於添加工作者,你需要在工作線程之間划分工作並防止它們彼此踩踏。 對於小k (不大於你的CPU數量!),在數組大小的對數工作量,你可以快速選擇找到k-1值,將組合數組分成k偶數塊( oops Adapt) http://www.geeksforgeeks.org/median-of-two-sorted-arrays/而不是做一個quickselect ...),以及每個數組中這些值的索引。 這會產生甚至困難的k問題,每個問題都可以指定為4個數字。 旋轉k線程,讓每個線程獲得答案的一部分。 這將比您目前所做的快約k倍。

在更多的精力的成本,這些方法可以結合起來。 你做的是讓迭代器創建4個工人,然后分配給每個工人。 當你調用iter.next() ,迭代器會給你一個下一個值,如果它有一個值。 如果它沒有,它將等待正在生成其下一個塊的worker完成,抓取該塊,如果一個就准備好,將該另一個塊交給該worker,然后分發該塊中的第一個值。 您可以使用塊大小。 您希望它足夠大以至於CPU可以很好地確定它應該從RAM流式傳輸到CPU緩存,並且不認為線程之間存在同步爭用。

考慮到大小和同步約束,我認為混合方法對於迭代器方法來說並不是一個勝利,如果有的話。 但如果你真的很絕望,你可以嘗試一下。

我發布了一個問題/解決方案的天真實現:2個數組填充隨機int。 如果達到100個相交值的閾值,則循環中斷。

一個循環使用OP邏輯。 另一個啟動兩個線程,每個線程處理一半的數組。

似乎線程開銷可能是一個問題。 或者它可能需要微調。

這是一個20跑的樣本。 最糟糕的情況:沒有交叉點強制運行到數組的末尾。 時間以微秒為單位。

Workers: 2806
Workers: 4197
Workers: 4235
Workers: 818
Workers: 729
Workers: 3376
Workers: 740
Workers: 688
Workers: 2245
Workers: 732
Workers: 330
Workers: 945
Workers: 605
Workers: 630
Workers: 630
Workers: 334
Workers: 643
Workers: 309
Workers: 290
Workers: 761
done
Sorted: 1525
Sorted: 405
Sorted: 550
Sorted: 880
Sorted: 265
Sorted: 267
Sorted: 252
Sorted: 310
Sorted: 253
Sorted: 272
Sorted: 285
Sorted: 270
Sorted: 270
Sorted: 315
Sorted: 267
Sorted: 269
Sorted: 265
Sorted: 258
Sorted: 269
Sorted: 289
done

package so;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public final class CrazyClass {

    static class Feeder implements Runnable{
        final int b, e;
        int[] k1001;
        int[] k1002;

        final Set<Integer> setThis;

        Feeder(int[] ia, int[] ia1, int be, int en, Set<Integer> s){
            k1001 = ia;
            k1002= ia1;
            b = be;
            e = en;
            setThis = s;
        }

        public void run() {
            int i2 = b;
            for(int i1 = b; i1 < e; i1++){
                if (k1001[i1] == k1002[i2]){
                    synchronized(setThis){
                        setThis.add(k1001[i1]);
                        if (setThis.size() == 25){
                            System.out.println("bye!!!");
                            break;
                        }
                    }
                }
                else if (k1001[i1] < k1002[i2])
                    i1++;
                else if (k1001[i1] > k1002[i2])
                    i2++;
            }

        }
    }

    static void sorted(){
        int i1 = 0, i2 = 0;
        Set<Integer> result = new HashSet<Integer>();
        Random r = new Random();
        int[] k1001 = new int[100000];
        int[] k1002 = new int[100000];

        for(int i = 0; i< k1001.length; i++){
            k1001[i] = r.nextInt();
            k1002[i] = r.nextInt();
        }

        Arrays.sort(k1001);
        Arrays.sort(k1002);

        long l = System.nanoTime();

        for(; i1 < k1001.length; i1++){
            if (k1001[i1] == k1002[i2]){
                result.add(k1001[i1]);
                if (result.size() == 100){
                    System.out.println("bye!!!");
                    break;
                }
            }
            else if (k1001[i1] < k1002[i2])
                i1++;
            else if (k1001[i1] > k1002[i2])
                i2++;
        }
        l = System.nanoTime() - l;
        System.out.println("Sorted: " + TimeUnit.MICROSECONDS.convert(l, TimeUnit.NANOSECONDS));
    }

    static void workers(){
        Thread t1, t2;
        Set<Integer> setThis = new HashSet<Integer>();
        Random r = new Random();
        int[] k1001 = new int[100000];
        int[] k1002 = new int[100000];

        for(int i = 0; i< k1001.length; i++){
            k1001[i] = r.nextInt();
            k1002[i] = r.nextInt();
        }

        t1 = new Thread(new Feeder(k1001, k1002, 0, 49999, setThis));
        t2 = new Thread(new Feeder(k1001, k1002, 50000, 99999, setThis));
        try{
            long l = System.nanoTime();
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            System.out.println("Workers: " + TimeUnit.MICROSECONDS.convert(System.nanoTime() - l, TimeUnit.NANOSECONDS));

        }catch(Exception x){

        }
    }

    static public void main(String[] args){
        int run = 20;
        for(int i = 0; i < run; i++)
            workers();
        System.out.println("done");
        for(int i = 0; i < run; i++)
            sorted();
        System.out.println("done");

    }
}

下面的代碼對我來說在10毫安左右。 所以我猜你要么處理字符串要么是腳本語言。

package com.example.so.algorithms;

import java.util.Arrays;
import java.util.Random;

/**
 * <p> http://stackoverflow.com/questions/42538902/how-to-intersect-two-sorted-arrays-the-fastest-possible-way#comment72213844_42538902 </p>
 * <p> Given two sorted sub-lists of 100k each determine the first 10 intersecting (common) entries within 350 millis </p>
 * @author Ravindra
 * @since 03March2017
 *
 */
public class TestMergeIntersection {

    /**
     * <pre>
Time (millis):9
Result :[442958664, 932132404, 988442487, 1356502780, 1614742980, 1923995812, 1985016181, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    </pre>
     * @param args
     */
    public static void main(String[] args) {
        handleTest();
    }

    private static void handleTest() {
        int size = 1024*128;
        int intersectionCount = 100;
        int[] arrayOne = generateSortedSublist(size);
        int[] arrayTwo = generateSortedSublist(size);
        int[] result = new int[intersectionCount];
        int count = 0;
        int i=0;
        int j=0;
        long start = System.currentTimeMillis();
        while(count < 100 && i < size && j < size ) {
            if( arrayOne[i] < arrayTwo[j]) {
                i++;
            } else if(  arrayOne[i] > arrayTwo[j] ) {
                j++;
            } else {
                result[count] =arrayOne[i]; 
                i++;
                j++;
                count++;
            }
        }
        long end = System.currentTimeMillis();

        System.out.println("Time (millis):"+(end-start));
        System.out.println("Result :"+Arrays.toString(result));
    }

    private static int[] generateSortedSublist(int size) {

        Random random = new Random();
        int[] result = new int[size];

        for(int i=0;i<result.length;i++) {
            result[i] = random.nextInt(Integer.MAX_VALUE);
        }

        Arrays.sort(result);

        return result;
    }

}

暫無
暫無

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

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