首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >素因式分解

素因式分解
EN

Code Review用户
提问于 2014-09-05 09:05:12
回答 4查看 3.3K关注 0票数 10

我正在做一个Objective项目,我需要做一些素因式分解,所以我想出了以下算法,即99% C,除了数组部分-C中最容易实现,因为它动态地为元素分配新的内存,因为它们只是简单的添加。目标-C部分是相当清楚的。

我主要是寻找反馈的算法,不一定是编码风格和诸如此类,但我会很高兴的反馈,我可以改进的东西!在我使用O3优化的机器上,分解1234567890的时间不到2秒。我真的不知道这是好是坏。我想在它中添加一个用于先前计算的因式分解的表,当在大的数字上,您很可能会遇到不止一次的数字(例如,4、6等)。我可能会添加一个函数,如果是偶数,在这些因素中添加一个2,而不做可能提高性能的除法/素数检查。

代码语言:javascript
复制
//Declare an array
NSMutableArray *factors;

bool isPrime(long n) {

    //Check if even or ends in a five
    if ((n % 2 == 0 && n > 2) || (n > 5 && n % 10 == 5)) {
        return 0;
    }

    if (n == 2) return 1;

    //set upper bound to square root
    long u = sqrt(n);
    long c = 0;

    for (long i = 3; i <= u; i+=2)
        if (n % i == 0) {
            c++;
            break;
        }

    return !c;
}


void factorize(long n) {

    //If prime return
    if (isPrime(n)) {
        [factors addObject:@(n)];
        return;
    }

    for (long i = n/2; i > 1; i--) {
        if (n % i) continue; //Check if indivisible

        long d = n/i;

        bool iP = isPrime(i), dP = isPrime(d);

        if (iP)
            [factors addObject:@(i)];
        else
            factorize(i);

        if (dP)
            [factors addObject:@(d)];
        else
            factorize(d);

        return;
    }
}

int main(int argc, const char * argv[]) {

    factors = [NSMutableArray array];

    long n = 1234567890;
    factorize(n);

    NSLog(@"%@", factors);

    return 0;
}
EN

回答 4

Code Review用户

发布于 2014-09-05 11:48:49

您的算法首先尝试找出最大的因素。这是无效的,因为您必须测试每个可能的因素,无论它是否是素数。

从最小的因素开始要有效得多。如果以这种方式将数字除以每个因素,那么您将只得到素因子,而素测试函数isPrime()就过时了。从除以2开始,然后按递增顺序测试可能的奇数因素。(如果您有一个预先计算的素数列表,那么这一点可以进一步改进。)

在我的计算机上,这将分解1234567890的运行时从1.6秒减少到0.00005秒。

此外,对于此更改,不再需要全局factors变量,您的方法可以如下所示:

代码语言:javascript
复制
NSMutableArray * primeFactorization(long n) {

    NSMutableArray *factors = [NSMutableArray array];

    // Divide by 2:
    while (n > 1 && n % 2 == 0) {
        [factors addObject:@(2)];
        n /= 2;
    }

    // Divide by 3, 5, 7, ...
    //
    // i is a possible *smallest* factor of the (remaining) number n.
    // If i * i > n then n is either 1 or a prime number.
    for (long i = 3; i * i <= n; i += 2) {
        while (n > 1 && n % i == 0) {
            [factors addObject:@(i)];
            n /= i;
        }
    }

    if (n > 1) {
        // Append last prime factor:
        [factors addObject:@(n)];
    }

    return factors;
}

int main(int argc, const char * argv[]) {

    long n = 1234567890;

    NSDate *startTime = [NSDate date];
    NSArray *factors = primeFactorization(n);
    NSLog(@"time: %f", -[startTime timeIntervalSinceNow]);

    NSLog(@"%@", factors);

    return 0;
}
票数 17
EN

Code Review用户

发布于 2014-09-05 09:35:20

对算法的反馈

代码语言:javascript
复制
//Check if even or ends in a five
if ((n % 2 == 0 && n > 2) || (n > 5 && n % 10 == 5)) {
    return 0;
}

检查您的数字是否除以5的部分进行了很少的优化,因为它是在下面的循环的第二次迭代中检查的。

代码语言:javascript
复制
for (long i = 3; i <= u; i+=2)
    if (n % i == 0) {
        c++;
        break;
    }

我建议为所有素数创建一个缓存,例如100000,然后像这样重写循环:

代码语言:javascript
复制
for (long i = 0; i < primeCacheSize; i++)
    if (n % primeCache[i] == 0) {
        return 1;
    }
for (long i = 100001; i <= u; i++)
    if (n % i == 0) {
        return 1;
    }
return 0;

这有点奇怪:

代码语言:javascript
复制
//If prime return
if (isPrime(n)) {
    [factors addObject:@(n)];
    return;
}

for (long i = n/2; i > 1; i--) {
    if (n % i) continue; //Check if indivisible

该代码将如何在208907 * 208907 * 208907 (9117147423118643)上工作?函数isPrime将确定n除以208907,返回false,然后开始从9117147423118643 / 2向下迭代。看起来像是一个无限循环(也是无用的)。

我可能建议的解决方案是:如果n是素数,则让isPrime函数返回数字本身,如果不是,则返回数字除数。

代码语言:javascript
复制
for (long i = 0; i < primeCacheSize; i++)
    if (n % primeCache[i] == 0) {
        return primeCache[i];
    }
for (long i = 100001; i <= u; i++)
    if (n % i == 0) {
        return i;
    }
return n;

并像这样重写分解:

代码语言:javascript
复制
void factorize(long n) {

//If prime return
while (n > 1) {
    divider = isPrime(n);
    [factors addObject:@(divider)];
    n /= divider;
}
票数 8
EN

Code Review用户

发布于 2014-09-05 23:33:33

考虑到这个答案是用C标记标记的,并且用户非常恰当地指出代码是99%的C,我认为可能值得演示这个问题的C解决方案是多么简单。

目标C的两个方面是用户使用的。

  1. NSMutableArray。这很方便,它为我们处理数组的大小调整。但是,需要注意的是,NSMutableArray的大小并没有什么神奇之处。目标-C可变数组的大小与C数组的大小相同.
  2. NSNumber。我们实际上并没有直接看到NSNumber,但这正是@()语法所做的--创建NSNumber对象。为什么?因为NSArray不能存储原始数据类型。它必须存储指针。

除非实际意图是在其他目标C代码中使用这个数组,因为在这些代码中,NSNumber对象实际上更容易处理,否则在这里使用objects并没有特别好的理由,除非在使用C样式数组时可能会感到不舒服。

所以,这是我在C中解决这个问题的方法。

作为起点,我使用了马丁R的回答,这实际上也非常类似于我用于这里一个稍微不同的问题的算法。

请注意,下面的代码片段包括马丁答案中的代码,这样两个解决方案可以并排比较,以防他编辑/删除他的答案。

马丁的回答很好:

代码语言:javascript
复制
NSMutableArray * primeFactorization(long n) {

    NSMutableArray *factors = [NSMutableArray array];

    // Divide by 2:
    while (n > 1 && n % 2 == 0) {
        [factors addObject:@(2)];
        n /= 2;
    }

    // Divide by 3, 5, 7, ...
    //
    // i is a possible *smallest* factor of the (remaining) number n.
    // If i * i > n then n is either 1 or a prime number.
    for (long i = 3; i * i <= n; i += 2) {
        while (n > 1 && n % i == 0) {
            [factors addObject:@(i)];
            n /= i;
        }
    }

    if (n > 1) {
        // Append last prime factor:
        [factors addObject:@(n)];
    }

    return factors;
}

我用直C的方法:

代码语言:javascript
复制
long * cPrimeFactorization(long n, long *factorCount) {
    long currentSize = 2;
    long currentIndex = 0;
    long *factors = malloc(sizeof(long) * currentSize);

    while (n > 1 && n % 2 == 0) {
        factors[currentIndex++] = 2;
        if (currentIndex >= currentSize) {
            currentSize *= 2;
            long *reallocFactors = realloc(factors, currentSize * sizeof(long));
            if (reallocFactors) {
                factors = reallocFactors;
            } else {
                printf("realloc failed");
                free(factors);
                return NULL;
            }
        }
        n /= 2;
    }

    for (long i = 3; i * i <= n; i += 2) {
        while (n > 1 && n % i == 0) {
            factors[currentIndex++] = i;
            if (currentIndex >= currentSize) {
                currentSize *= 2;
                long *reallocFactors = realloc(factors, currentSize * sizeof(long));
                if (reallocFactors) {
                    factors = reallocFactors;
                } else {
                    printf("realloc failed");
                    free(factors);
                    return NULL;
                }
            }
            n /= i;
        }
    }

    if (n > 1) {
        factors[currentIndex++] = n;
    }
    *factorCount = currentIndex; 
    return factors;
}

并排运行:

代码语言:javascript
复制
int main(int argc, const char * argv[]) {
    long n = 1234567890;

    NSDate *startTime = [NSDate date];
    NSArray *oFactors = primeFactorization(n);
    NSLog(@"time (Objective-C): %f", -[startTime timeIntervalSinceNow]);

    startTime = [NSDate date];
    long factorCount;
    long * cFactors = cPrimeFactorization(n, &factorCount);
    NSLog(@"time (C): %f", -[startTime timeIntervalSinceNow]);

    NSLog(@"%@", oFactors);
    for (long i = 0; i < factorCount; ++i) {
        printf("%li\n", cFactors[i]);
    }

    return 0;
}

为了保持基准测试的一致性,我只使用了Objective方法(因为Martin已经有了它,而且我不想使用C方法)。我尝试了几个不同的数字,并运行了他们所有的多次。C方法在每种情况下都运行得更快。

与速度相比,C方法实际上比目标C方法占用更少的内存.目标-C数组不仅比C数组具有更多的开销(每个索引大小相同,8字节),而且目标-C数组是指向对象的指针的索引。因此,objects数组已经比C数组稍大一些,另外还有一些更大的内存空间,可以将所有NSNumber对象都保存在堆中。

我们必须花费大量的时间在堆栈上创建这些NSNumber对象,这是目标C解决方案速度慢的原因之一。它也比较慢,因为将消息传递到对象(我们多次调用的addObject: )要比在内存位置直接插入一个值(这就是我们对C数组所做的)要慢得多。

请注意,我绝对是一个目标-C程序员,我不是一个最轻微的C程序员。我的C代码几乎肯定还有改进的余地。但是作为目标C程序员,我们必须始终记住纯C应该永远是一种选择。

值得注意的是,这种速度(和内存)的差异在非常大的二次幂下变得特别明显。例如,使用2^49,即562,949,953,421,312,目标-C解决方案在我的计算机上花费了几乎3倍的时间。

票数 8
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/62013

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档