我在寻找成对友好数字的算法中遇到了时间复杂度高的问题。尽管输入的上限为10000,但要花3分钟才能找到所有对。
如何优化以下代码?
#include <stdio.h>
int sumofdiv(int);
void amicable_nums(int);
int main()
{
int limit;
printf("Enter the limit to check amicable numbers: ");
scanf("%d", &limit);
amicable_nums(limit);
return 0;
}
void amicable_nums(int limit)
{
int num1, num2, sum1, sum2;
for(num1=220; num1<limit; num1++)
{
if ((num1%2 != 0) && (num1%3 != 0) && (num1%5 != 0))//prime number detection (odd num > 220, so number 2, for example, that is prime doesn't count), prime number can't be amicable
{
continue;
}
for (num2 = num1+1; num2<limit; num2++)
{
if ((num1%2==0 && num2%2 != 0) || (num1%2 != 0 && num2%2 == 0))//only both odd or even numbers can be amicable
{
continue;
}
if ((num2%2 != 0) && (num2%3 != 0) && (num2%5 != 0))//the same prime number detection as before
{
continue;
}
sum1 = sumofdiv(num1);
if (sum1 != num2)//if the sum of proper divisors of the first number is NOT equal to the second number, there is no reason to check the sum of proper divisors of the second number
{
continue;
}
sum2 = sumofdiv(num2);
if (sum1 == num2 && sum2 == num1 && num1 != num2)
{
printf("(%d, %d)\n", num1, num2);
}
}
}
}
int sumofdiv(int num)
{
int div, sum = 0, even_limit = num/2, odd_limit = num/3;
if (num%2 == 0)
{
for (div=1; div<even_limit; div++)
{
if ((num%div) == 0)
{
sum += div;
}
}
sum += even_limit;
}
else
{
for (div=1; div<odd_limit; div+=2)//odd number can't be divided by even number
{
if ((num%div) == 0)
{
sum += div;
}
}
sum += odd_limit;
}
return sum;
}
P.S。:代码中的一些语句中有一些注释。 我试图避免质数,并避免计算很多默认情况下不友好的数字。计算仍然很慢。我还在嵌套的for循环中对num1或/和num2进行了更多的跳转,但输出的对少于必须的。
答案 0 :(得分:1)
您的算法的时间复杂度大于 O(N 3 ),因为对于每个@Scheduled(cron = "0 14 * * *")
public void scheduleTaskUsingCronExpression() {
jobMehod();
}
public synchronized void jobMehod(){
// code wihout creation new threads
}
@EventListener(ContextRefreshedEvent.class)
public void onStartup(){
jobMehod();
}
,您都会尝试num1
的大多数值,并且num2
的计算也具有线性复杂度。
您可以通过每次迭代仅测试一个值来大大降低这种复杂性:如果sumofdiv()
,您有一对友好的数字。
这是一个简单但有效的实施方式:
sumofdiv(sumofdiv(num1)) == num1)
在我的系统上,高达10000的扫描时间从71秒减少到不到100毫秒。还要注意代码的简单性和通用性:不测试特殊情况。
您的函数void amicable_nums(int limit) {
int num1, num2;
for (num1 = 1; num1 < limit; num1++) {
num2 = sumofdiv(num1);
if (num2 > num1 && sumofdiv(num2) == num1) {
printf("(%d, %d)\n", num1, num2);
}
}
}
也可以得到简化和加速,将速度提高10倍(对于10000),最终复杂度略高于 O(N 1.5 ) 。
以下是改进的代码:
sumofdiv()
输出:
#include <stdio.h>
#include <stdlib.h>
int sumofdiv(int num) {
int div, sum = 1;
for (div = 2; div * div <= num; div++) {
if (num % div == 0) {
int quo = num / div;
sum += div;
if (quo != div)
sum += quo;
}
}
return sum;
}
void amicable_nums(int limit) {
int num1, num2;
for (num1 = 1; num1 < limit; num1++) {
num2 = sumofdiv(num1);
if (num2 > num1 && sumofdiv(num2) == num1) {
printf("(%d, %d)\n", num1, num2);
}
}
}
int main(int argc, char *argv[]) {
int limit;
if (argc > 1) {
/* this is optional, for testing purposes */
limit = strtol(argv[1], NULL, 0);
} else {
printf("Enter the limit to check amicable numbers: ");
scanf("%d", &limit);
}
amicable_nums(limit);
return 0;
}
进一步的测试在122毫秒内产生13对高达100000的对,在3.6秒内产生42对高达1000000的对,以及在2分钟内产生10000000的108对。
答案 1 :(得分:0)
要优化您的解决方案,请执行以下操作:
1)构建Sieve of Eratosthenes,以实现更快的数字分解
2)将每个数字分解一次,并将其素数之和保存在数组中
3)在分解了一个数字之后,请执行以下操作:
if (sum_of_prime_factors < n) {
if (arr[sum_of_prime_factors] == n){
// ... pair found ...
}
}
答案 2 :(得分:0)
您的big O函数上将进行较大的优化(如sumofdiv)。您一直循环到n
。对于a <= b
中的每对因素n
,较小的因素a
小于或等于sqrt(n)
。另一个因素b
等于n/a
。使用它可以帮助您在循环到sqrt(n)
的情况下编写函数。
为进一步帮助您,请查看sumofdiv(num1)
的位置。您是否真的需要在for (num2 = num1+1; num2<limit; num2++)
循环内进行计算?尝试将它放在其他地方。
答案 3 :(得分:0)
一个小的优化。
如何更改此代码
for (num2 = num1+1; num2<limit; num2++)
{
if ((num1%2==0 && num2%2 != 0) || (num1%2 != 0 && num2%2 == 0))
{
continue;
}
...
到
for (num2 = num1+2; num2<limit; num2+=2)
{
从num1 + 2开始,然后num2就像num1是偶数一样开始。通过增加2,我们确保即使num1是偶数,num2仍然存在。同样,如果num1为奇数,则确保num2始终为奇数。
答案 4 :(得分:0)
最优化。 您不需要double for循环。 如果数字是友好的,则不必检查所有可能的数字,只需检查其因素的总和即可。
使用伪代码:
for i = 220 to max:
j = sum_of_div(i)
i_prime = sum_of_div(j)
if (i == i_prime) and (i < j):
print i and j are amicable