是recursion迭代法比纯迭代法更好地发现一个数是否是素数?

我用C语言编写这个程序,testing一个数字是否为素数 。 我还不熟悉algorithm复杂性和所有这些大O的东西,所以我不确定我的方法是迭代和recursion结合 ,实际上比使用纯粹的迭代方法更有效率。

#include<stdio.h> #include<stdlib.h> #include<math.h> typedef struct primenode{ long int key; struct primenode * next; }primenode; typedef struct{ primenode * head; primenode * tail; primenode * curr; unsigned long int size; }primelist; int isPrime(long int number, primelist * list ,long int * calls, long int * searchcalls); primenode * primelist_insert(long int prime, primelist * list); int primelist_search(long int searchval, primenode * searchat, long int * calls); void primelist_destroy(primenode * destroyat); int main(){ long int n; long int callstoisprime = 0; long int callstosearch = 0; int result = 0; primelist primes; //Initialize primelist primes.head = NULL; primes.tail = NULL; primes.size = 0; //Insert 2 as a default prime (optional step) primelist_insert(2, &primes); printf("\n\nPlease enter a number: "); scanf("%d",&n); printf("Please wait while I crunch the numbers..."); result = isPrime(n, &primes, &callstoisprime, &callstosearch); switch(result){ case 1: printf("\n%ld is a prime.",n); break; case -1: printf("\n%ld is a special case. It's neither prime nor composite.",n); break; default: printf("\n%ld is composite.",n); break; } printf("\n\n%d calls made to function: isPrime()",callstoisprime); printf("\n%d calls made to function: primelist_search()",callstosearch); //Print all prime numbers in the linked list printf("\n\nHere are all the prime numbers in the linked list:\n\n"); primes.curr = primes.head; while(primes.curr != NULL){ printf("%ld ", primes.curr->key); primes.curr = primes.curr->next; } printf("\n\nNote: Only primes up to the square root of your number are listed.\n" "If your number is negative, only the smallest prime will be listed.\n" "If your number is a prime, it will itself be listed.\n\n"); //Free up linked list before exiting primelist_destroy(primes.head); return 0; } int isPrime(long int number, primelist * list ,long int * calls, long int *searchcalls){ //Returns 1 if prime // 0 if composite // -1 if special case *calls += 1; long int i = 2; if(number==0||number==1){ return -1; } if(number<0){ return 0; } //Search for it in the linked list of previously found primes if(primelist_search(number, list->head, searchcalls) == 1){ return 1; } //Go through all possible prime factors up to its square root for(i = 2; i <= sqrt(number); i++){ if(isPrime(i, list,calls,searchcalls)){ if(number%i==0) return 0; //It's not a prime } } primelist_insert(number, list); /*Insert into linked list so it doesn't have to keep checking if this number is prime every time*/ return 1; } primenode * primelist_insert(long int prime, primelist * list){ list->curr = malloc(sizeof(primenode)); list->curr->next = NULL; if(list->head == NULL){ list->head = list->curr; } else{ list->tail->next = list->curr; } list->tail = list->curr; list->curr->key = prime; list->size += 1; return list->curr; } int primelist_search(long int searchval, primenode * searchat, long int * calls){ *calls += 1; if(searchat == NULL) return 0; if(searchat->key == searchval) return 1; return primelist_search(searchval, searchat->next, calls); } void primelist_destroy(primenode * destroyat){ if(destroyat == NULL) return; primelist_destroy(destroyat->next); free(destroyat); return; } 

基本上,我见过的很多简单的原始testing是:0. 2是一个素数。 1.循环所有整数从2到半数或被测数字的平方根。 来自“简明英汉词典”如果这个数字可以被任何东西整除,则返回false。 它是复合的。 否则,在最后一次迭代之后返回true。 这是最重要的。

我认为你不必testing从2到平方根的每个数字,只是每个数,因为所有其他数字都是素数的倍数。 所以,函数调用它自己来找出一个数字在使用模数之前是否为素数。 这是有效的,但是我认为继续一遍又一遍地testing所有素数是有点乏味的。 所以,我使用了一个链表来存储在其中find的每一个素数,所以在testing之前,程序首先search列表。

它是真的更快,还是更有效率,还是我浪费了很多时间? 我在我的电脑上做了testing,对于更大的素数,它看起来更快,但我不确定。 我也不知道它是否使用了更多的内存,因为任务pipe理器只是保持不变的0.7 MB,不pipe我做什么。

感谢您的任何答案!

由于您的程序只testing一个数字,所以您正在浪费时间来避免使用复合材料进行testing。 您执行大量计算来保存一个微型模操作。

如果你testing的是素数连续的几个数字,那么预先计算达到该范围上限的一个sqrt的素数是有意义的,并且在testing候选时通过这些素数。

更好的办法是用Eratosthenes偏移 ( C代码 )来find给定范围内的素数。 Eratosthenes筛选从2到N的素数的时间复杂度为O(N log log N) ; O(N^1.5 / (log N)^2) (这是更糟糕的;例如,筛子运行时间与100k相比的运行时间与10k相比是10.7x,而22x审判分裂; 2毫升与1毫升筛为2.04倍,审判部2.7倍)。

Eratosthenes偏移筛的伪代码:

 Input: two Integers n >= m > 1 Let k = Floor(Sqrt(n)), Let A be an array of Boolean values, indexed by Integers 2 to k, and B an array of Booleans indexed by Integers from m to n, initially all set to True. for i = 2, 3, 4, ..., not exceeding k: if A[i] is True: for j = i^2, i^2+i, i^2+2i, ..., not greater than k: A[j] := False for j = i^2, i^2+i, i^2+2i, ..., between m and n, inclusive: B[j] := False Output: all `i`s such that B[i] is True, are all the primes between m and n, inclusive. 

一个常见的优化是只处理赔率, i = 3,5,7,... ,从一开始就避免任何偶数(无论如何2是已知的,任何偶数是复合)。 那么2i的步骤,而不仅仅是i ,可以用在两个内部循环中。 因此,偶数指数从加工中完全排除(通常采用浓缩寻址scheme, val = start + 2*i )。

以下片段可以大大加强:

 //Go through all possible prime factors up to its square root for(i = 2; i <= sqrt(number); i++){ if(isPrime(i, list,calls,searchcalls)){ if(number%i==0) return 0; //It's not a prime } } 

而不是循环遍历所有数字,只需循环链表中的数字:

 // Go through all possible prime factors up to its square root primenode *p; for (p = primes.head; p->key <= sqrt(number); p = p->next) { if (number%(p->key) == 0) return 0; //It's not a prime } }