按字母顺序排列其主要因素

时间:2017-11-21 02:36:29

标签: algorithm sorting math depth-first-search prime-factoring

我正在尝试解决算法问题,这需要我根据其素因子的词典顺序对大小为N(N <= 1e6)的数字列表进行排序。列表中的每个数字都在[2,1e6]中。

Link问题。

例如,

2 3 4 5 6

将排序为:

2 4 6 3 5

他们的主要因素如下所示:

2 = 2
3 = 3
4 = 2 * 2
5 = 5
6 = 2 * 3

我的尝试:

我可以通过对每个数字使用O(logn)素因子化方法并将其存储到1e6 * 21 2d数组中来为此设计正确的解决方案,因为所有数字&lt; = 1e6最多可以有20个自2 ^ 20以来的主要因素1E6。

此后,我使用这些素因子的词汇顺序对每个数字进行排序。

我的程序能够在2秒的时间限制内运行,但使用的内存太多(内存限制为32mb)。

有人可以告诉我更好的方法来解决这个问题吗?

P.S。这个问题被标记为“深度优先搜索”,但无论如何我都看不出它是如何工作的。

3 个答案:

答案 0 :(得分:4)

这对我来说听起来像是一个分区问题。第一步是对数组进行分区,使得除以2的数字首先出现。然后将那个组除以第二次除以2的那些。递归,直到你有一个空的子组。现在再次使用除数为3.继续列出素数列表,直到达到sqrt(1e6)或者您已找到每个数字的所有除数。

答案 1 :(得分:0)

由于您已经处于时间限制之内,因此您只需要一种更有效的方法来存储每个数字的所有素数因子,以便您可以轻松查找它们。您可以使用2级查找表执行此操作。

在一个表格中,存储数字时最多存储1e3(1000)。这将需要1e3 x 10 2d阵列(1000 x 10 x 4 = 10000字节)。

要存储最多1e6的数字的所有素因子,您需要为每个存储最多3个数字(即1200万字节)。要计算3个数字,请从素数因子列表开始,然后将它们相乘,直到不能超过1000而将其相乘。将其存储在第一个条目中,然后对余数进行相同处理并存储在第二个数字,如果剩下任何一个,只需将它放在第三个位置(你永远不需要超过3个 - 如果你有4个就意味着最后两个相乘将超过1000,这意味着第一个乘以一起的2将<&lt; 1000,在这种情况下它们不会被单独存储)。如果列表中存在超过1000的素因子,则只需要2,因为所有其他因子将乘以&lt; 1000。

要检索条目的素数因子的原始列表,请取三个数字中的每一个(将是素数或复数1000或更少或素数> 1000),如果它们低于1000,则查找其中的主要因子小表,如果不按原样,你可以重建列表。

例如存储515130(2 * 3 * 5 * 7 * 11 * 223)

1st number: 210 (2*3*5*7) can't multiply by 11 without going over 1000
2nd number: 11 (prime) can't multiply by 223 without going over
3rd number: 223

667023(3 * 7 * 23 * 1381)

1st: 438 (3*7*23)
2nd: 1381 (prime)

即使素数因子列表未排序,这也会有效。

答案 2 :(得分:0)

实际上,对我的算法进行了简单的修改就可以了。我没有存储每个整数的素数分解,而是利用了我已经拥有的东西,这是一种在O(logn)中工作的素数分解方法。我创建了一个自定义排序方法,使用分解方法对两个整数进行分解,同时比较它们的素因子。因此,时间复杂度保持不变,并且不需要存储任何整数的素数因子分解。

对于那些想知道这种快速因子分解方法是如何工作的人来说,这里是link(参见使用最低除数的方法)。

对于面临同样问题的未来读者,这是我接受的代码:

#include<cstdio>
#include<algorithm>

#define FACTOR_LIM (int) 1e6+2 // used by preFactor(n). Defined as <= n <= 1e8

using namespace std;

int lowestDiv[FACTOR_LIM+1], a[FACTOR_LIM], n;

void preFactor(int n) {
    int root = 2;
    for(int i = 2; i*i <= n; i++) {
        if(lowestDiv[i]) continue;
        root = lowestDiv[i] = i;
        for(int j = i*i; j <= n; j+=i) {
            lowestDiv[j] = (lowestDiv[j]) ? lowestDiv[j] : i;
        }
    }
    for(int i = root; i <= n; i++) {
        if(!lowestDiv[i]) {
            lowestDiv[i] = i;
        }
    }
}

bool cmp(const int i, const int j) {
    int x = i, y = j;
    while (x != y) {
        int p = lowestDiv[x];
        int q = lowestDiv[y];
        if (p != q) return p < q;
        x /= p;
        y /= q;
    }
    return false;
}

int main() {
    preFactor(FACTOR_LIM-1);
    scanf("%d",&n);

    for(int i = 0; i < n; i++) {
        scanf("%d",&a[i]);
    }
    sort(a,a+n,cmp);

    for(int i = 0; i < n; i++) {
        printf("%d\n",a[i]);
    }
    return 0;
}