簡體   English   中英

來自兩個數組的互質對的計數小於 O(n^2) 復雜度

[英]Count of co-prime pairs from two arrays in less than O(n^2) complexity

我是在挑戰中遇到這個問題的。 有兩個大小為 N 的數組 A 和 B,我們需要返回對 (A[i],B[j]) 的計數,其中gcd(A[i],B[j])==1A[i] != B[j] 對於少數測試用例,我只能想到超過時間限制的蠻力方法。

for(int i=0; i<n; i++) {
    for(int j=0; j<n; j++) {
        if(__gcd(a[i],b[j])==1) {
             printf("%d %d\n", a[i], b[j]);
        }
    }
}

你能建議時間有效的算法來解決這個問題嗎?

編輯:無法分享問題鏈接,因為這是來自招聘挑戰。 我記得添加約束和輸入/輸出格式。

輸入 -

  • 第一行將包含 N,即兩個數組中存在的元素數。
  • 第二行將包含 N 個空格分隔的整數,數組 A 的元素。
  • 第三行將包含 N 個空格分隔的整數,數組 B 的元素。

輸出 -

  • 根據條件對 A[i],A[j] 的計數。

約束 -

  • 1 <= N <= 10^5
  • 1 < A[i],B[j] <= 10^9 其中 i,j < N

第一步是使用Eratosthenes篩子計算素數,最高可達sqrt(10^9) 然后,可以使用此篩子快速查找所有小於10^9的任意數量的質數因子(請參見下面的代碼示例中的getPrimeFactors(...)函數)。

接下來,對於具有素數因子p0, p1, ..., pk每個A[i] ,我們計算所有可能的子產品X - p0, p1, p0p1, p2, p0p2, p1p2, p0p1p2, p3, p0p3, ..., p0p1p2...pk並將其計入地圖cntp[X] 有效地,映射cntp[X]告訴我們可以被X整除的元素A[i]的數量,其中X是質數與0或1的冪的乘積。因此,例如,對於A[i] = 12 ,主要因素是2, 3 我們將計算cntp[2]++cntp[3]++cntp[6]++

最后,對於具有素數因子p0, p1, ..., pk每個B[j] ,我們再次計算所有可能的子產品X並使用包含-排除原理對所有非互質對C_j (即A[i]B[j]共享至少一個素數。 然后從總對數N*N減去數字C_j ,以獲得最終答案。

注意:包含-排除原理如下所示:

C_j = (cntp[p0] + cntp[p1] + ... + cntp[pk]) -
      (cntp[p0p1] + cntp[p0p2] + ... + cntp[pk-1pk]) +
      (cntp[p0p1p2] + cntp[p0p1p3] + ... + cntp[pk-2pk-1pk]) -
      ...

而占的事實, cntp[X]cntp[Y]我們可能已經算相同數量的A[i]的兩倍,因為它是由兩個分割的XY

這是該算法的一種可能的C ++實現,與OP的原始O(n ^ 2)算法產生的結果相同:

// get prime factors of a using pre-generated sieve
std::vector<int> getPrimeFactors(int a, const std::vector<int> & primes) {
    std::vector<int> f;
    for (auto p : primes) {
        if (p > a) break;
        if (a % p == 0) {
            f.push_back(p);
            do {
                a /= p;
            } while (a % p == 0);
        }
    }
    if (a > 1) f.push_back(a);

    return f;
}

// find coprime pairs A_i and B_j
// A_i and B_i <= 1e9
void solution(const std::vector<int> & A, const std::vector<int> & B) {
    // generate prime sieve
    std::vector<int> primes;
    primes.push_back(2);

    for (int i = 3; i*i <= 1e9; ++i) {
        bool isPrime = true;
        for (auto p : primes) {
            if (i % p == 0) {
                isPrime = false;
                break;
            }
        }
        if (isPrime) {
            primes.push_back(i);
        }
    }

    int N = A.size();

    struct Entry {
        int n = 0;
        int64_t p = 0;
    };

    // cntp[X] - number of times the product X can be expressed
    // with prime factors of A_i
    std::map<int64_t, int64_t> cntp;

    for (int i = 0; i < N; i++) {
        auto f = getPrimeFactors(A[i], primes);

        // count possible products using non-repeating prime factors of A_i
        std::vector<Entry> x;
        x.push_back({ 0, 1 });

        for (auto p : f) {
            int k = x.size();
            for (int i = 0; i < k; ++i) {
                int nn = x[i].n + 1;
                int64_t pp = x[i].p*p;

                ++cntp[pp];
                x.push_back({ nn, pp });
            }
        }
    }

    // use Inclusion–exclusion principle to count non-coprime pairs
    // and subtract them from the total number of prairs N*N

    int64_t cnt = N; cnt *= N;

    for (int i = 0; i < N; i++) {
        auto f = getPrimeFactors(B[i], primes);

        std::vector<Entry> x;
        x.push_back({ 0, 1 });

        for (auto p : f) {
            int k = x.size();
            for (int i = 0; i < k; ++i) {
                int nn = x[i].n + 1;
                int64_t pp = x[i].p*p;

                x.push_back({ nn, pp });

                if (nn % 2 == 1) {
                    cnt -= cntp[pp];
                } else {
                    cnt += cntp[pp];
                }
            }
        }
    }

    printf("cnt = %d\n", (int) cnt);
}

現場例子

我無法通過分析來估計復雜度,但是這是我的筆記本電腦上針對不同N以及均勻隨機A[i]B[j]一些分析結果:

For N = 1e2, takes ~0.02 sec
For N = 1e3, takes ~0.05 sec
For N = 1e4, takes ~0.38 sec
For N = 1e5, takes ~3.80 sec

為了進行比較,O(n ^ 2)方法采用:

For N = 1e2, takes ~0.00 sec
For N = 1e3, takes ~0.15 sec
For N = 1e4, takes ~15.1 sec
For N = 1e5, takes too long, didn't wait to finish

Python實現:

    import math
    from collections import defaultdict

    def sieve(MAXN):
        spf = [0 for i in range(MAXN)]
        spf[1] = 1
        for i in range(2, MAXN):
            spf[i] = i
        for i in range(4, MAXN, 2):
            spf[i] = 2
        for i in range(3, math.ceil(math.sqrt(MAXN))):
            if (spf[i] == i):
                for j in range(i * i, MAXN, i):
                    if (spf[j] == j):
                        spf[j] = i
        return(spf)

    def getFactorization(x,spf):
        ret = list()
        while (x != 1):
            ret.append(spf[x])
            x = x // spf[x]
        return(list(set(ret)))

    def coprime_pairs(N,A,B):
        MAXN=max(max(A),max(B))+1
        spf=sieve(MAXN)
        cntp=defaultdict(int)
        for i in range(N):
            f=getFactorization(A[i],spf)
            x=[[0,1]]
            for p in f:
                k=len(x)
                for i in range(k):
                    nn=x[i][0]+1
                    pp=x[i][1]*p
                    cntp[pp]+=1
                    x.append([nn,pp])
        cnt=0
        for i in range(N):
            f=getFactorization(B[i],spf)
            x=[[0,1]]
            for p in f:
                k=len(x)
                for i in range(k):
                    nn=x[i][0]+1
                    pp=x[i][1]*p
                    x.append([nn,pp])
                    if(nn%2==1):
                        cnt+=cntp[pp]
                    else:
                        cnt-=cntp[pp]
        return(N*N-cnt)

    import random
    N=10001
    A=[random.randint(1,N) for _ in range(N)]
    B=[random.randint(1,N) for _ in range(N)]
    print(coprime_pairs(N,A,B))

暫無
暫無

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

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