注意:下面的版本2使用了Eratosthenes的Sieve。有几个答案有助于我最初的问题。我选择了Eratosthenes筛选方法,实现了它,并适当地更改了问题标题和标签。感谢所有帮助过的人!
我编写了这个花哨的小方法,它生成一个int数组,其中包含小于指定上限的素数。它工作得很好,但我有一个问题。
private static int [] generatePrimes(int max) {
int [] temp = new int [max];
temp [0] = 2;
int index = 1;
int prime = 1;
boolean isPrime = false;
while((prime += 2) <= max) {
isPrime = true;
for(int i = 0; i < index; i++) {
if(prime % temp [i] == 0) {
isPrime = false;
break;
}
}
if(isPrime) {
temp [index++] = prime;
}
}
int [] primes = new int [index];
while(--index >= 0) {
primes [index] = temp [index];
}
return primes;
}
我担心的是我创建的数组对于方法返回的最终元素数量来说太大了。麻烦的是我不知道一个正确猜测低于指定数字的素数的好方法。
这是程序使用数组的方式。这就是我想要改进的地方。
temp[]
要素primes[]
无需迭代
两个数组并复制元素
一个接一个?第2版(感谢Jon Skeet):
private static int [] generatePrimes(int max) {
int [] temp = new int [max];
temp [0] = 2;
int index = 1;
int prime = 1;
boolean isPrime = false;
while((prime += 2) <= max) {
isPrime = true;
for(int i = 0; i < index; i++) {
if(prime % temp [i] == 0) {
isPrime = false;
break;
}
}
if(isPrime) {
temp [index++] = prime;
}
}
return Arrays.copyOfRange(temp, 0, index);
}
版本3(感谢Paul Tomblin)使用Sieve of Erastosthenes:
private static int [] generatePrimes(int max) {
boolean[] isComposite = new boolean[max + 1];
for (int i = 2; i * i <= max; i++) {
if (!isComposite [i]) {
for (int j = i; i * j <= max; j++) {
isComposite [i*j] = true;
}
}
}
int numPrimes = 0;
for (int i = 2; i <= max; i++) {
if (!isComposite [i]) numPrimes++;
}
int [] primes = new int [numPrimes];
int index = 0;
for (int i = 2; i <= max; i++) {
if (!isComposite [i]) primes [index++] = i;
}
return primes;
}
答案 0 :(得分:13)
通过将数组的每个元素与每个可能的因子进行比较,找到素数的方法非常低效。您可以通过立即对整个阵列执行Sieve of Eratosthenes来极大地改进它。除了做更少的比较,它还使用加法而不是除法。分工比较慢。
答案 1 :(得分:9)
ArrayList<>
Eratosthenes筛选// Return primes less than limit
static ArrayList<Integer> generatePrimes(int limit) {
final int numPrimes = countPrimesUpperBound(limit);
ArrayList<Integer> primes = new ArrayList<Integer>(numPrimes);
boolean [] isComposite = new boolean [limit]; // all false
final int sqrtLimit = (int)Math.sqrt(limit); // floor
for (int i = 2; i <= sqrtLimit; i++) {
if (!isComposite [i]) {
primes.add(i);
for (int j = i*i; j < limit; j += i) // `j+=i` can overflow
isComposite [j] = true;
}
}
for (int i = sqrtLimit + 1; i < limit; i++)
if (!isComposite [i])
primes.add(i);
return primes;
}
小于或等于max
的素数的上限公式(见wolfram.com):
static int countPrimesUpperBound(int max) {
return max > 1 ? (int)(1.25506 * max / Math.log((double)max)) : 0;
}
答案 2 :(得分:8)
创建ArrayList<Integer>
,然后在结尾处转换为int[]
。
周围有各种第三方IntList
(等)类,但除非你真的担心拳击几个整数,我不担心。
您可以使用Arrays.copyOf
创建新数组。您可能还希望每次需要时将尺寸加倍,然后在最后修剪。这基本上就是模仿ArrayList
行为。
答案 3 :(得分:7)
Algo使用Eratosthenes筛选
public static List<Integer> findPrimes(int limit) {
List<Integer> list = new ArrayList<>();
boolean [] isComposite = new boolean [limit + 1]; // limit + 1 because we won't use '0'th index of the array
isComposite[1] = true;
// Mark all composite numbers
for (int i = 2; i <= limit; i++) {
if (!isComposite[i]) {
// 'i' is a prime number
list.add(i);
int multiple = 2;
while (i * multiple <= limit) {
isComposite [i * multiple] = true;
multiple++;
}
}
}
return list;
}
描绘上述算法的图像(灰色单元代表素数。由于我们将所有数字视为素数,因此最初的网格是灰色的。)
图片来源:WikiMedia
答案 4 :(得分:2)
最简单的解决方案是返回Collections Framework的某些成员而不是数组。
答案 5 :(得分:2)
您使用的是Java 1.5吗?为什么不返回List<Integer>
并使用ArrayList<Integer>
?如果您确实需要返回int[]
,则可以在处理结束时将列表转换为int[]
。
答案 6 :(得分:1)
Paul Tomblin指出,有更好的算法。
但是保持你拥有的东西,并假设每个结果的对象太大了:
您只是追加到数组。因此,使用相对较小的int []数组。当它完全使用时,将其附加到List并创建替换。最后将其复制到正确大小的数组中。
或者,猜猜int []数组的大小。如果它太小,请用大小比当前数组大小大一小部分的int []替换。其性能开销与大小成正比。 (在最近的stackoverflow播客中对此进行了简要讨论。)
答案 7 :(得分:1)
现在你已经有了一个基本的筛子,请注意内循环只需要持续到temp[i]*temp[i] > prime
。
答案 8 :(得分:1)
我的实施非常有效:
BitSet
,每个号码只需要一位。initialCapacity
。以下是代码:
public ArrayList<Integer> sieve(int n) {
int upperBound = (int) (1.25506 * n / Math.log(n));
ArrayList<Integer> result = new ArrayList<Integer>(upperBound);
if (n >= 2)
result.add(2);
int size = (n - 1) / 2;
BitSet bs = new BitSet(size);
int i = 0;
while (i < size) {
int p = 3 + 2 * i;
result.add(p);
for (int j = i + p; j < size; j += p)
bs.set(j);
i = bs.nextClearBit(i + 1);
}
return result;
}
答案 9 :(得分:0)
重构您的代码。抛出临时数组,而是编写只是初始化测试整数的函数。它会相当快,因为你只使用本机类型。然后,您可以循环并构建一个整数列表,然后再将其转换为要返回的数组。
答案 10 :(得分:0)
我终于完成了这个程序 这是一个优化的筛子
public static int[] generatePrimes(int max) {
boolean[] isComposite = new boolean[max + 1];
LinkedList<Integer> Primes = new LinkedList<>(); //linkedlist so not have to iterate 2 times
int sqrt = (int) Math.sqrt(max); //end at the square root
for (int i = 2; i <= sqrt; i++) {
if (!isComposite[i]) {
int s = i*i; //start from the prime's square
while (s <= max) {
isComposite[s] = true; //oh no its a not prime
s+=i;
}
}
}
for(int i = 2; i < max; i++){
if(!isComposite[i]){
Primes.add(i);
}
}
int[] result = new int[Primes.size()];
for (int i = 0; i < result.length; i++) {
result[i] = Primes.get(i);
}
return result;
}
答案 11 :(得分:0)
不确定这是否适合您的情况,但您可以查看我的方法。我使用Sieve of Eratosthenes来使用我的。
public static List<Integer> sieves(int n) {
Map<Integer,Boolean> numbers = new LinkedHashMap<>();
List<Integer> primes = new ArrayList<>();
//First generate a list of integers from 2 to 30
for(int i=2; i<n;i++){
numbers.put(i,true);
}
for(int i : numbers.keySet()){
/**
* The first number in the list is 2; cross out every 2nd number in the list after 2 by
* counting up from 2 in increments of 2 (these will be all the multiples of 2 in the list):
*
* The next number in the list after 2 is 3; cross out every 3rd number in the list after 3 by
* counting up from 3 in increments of 3 (these will be all the multiples of 3 in the list):
* The next number not yet crossed out in the list after 5 is 7; the next step would be to cross out every
* 7th number in the list after 7, but they are all already crossed out at this point,
* as these numbers (14, 21, 28) are also multiples of smaller primes because 7 × 7 is greater than 30.
* The numbers not crossed out at this point in the list are all the prime numbers below 30:
*/
if(numbers.get(i)){
for(int j = i+i; j<n; j+=i) {
numbers.put(j,false);
}
}
}
for(int i : numbers.keySet()){
for(int j = i+i; j<n && numbers.get(i); j+=i) {
numbers.put(j,false);
}
}
for(int i : numbers.keySet()){
if(numbers.get(i)) {
primes.add(i);
}
}
return primes;
}
为维基百科中说明的每个步骤添加了评论
答案 12 :(得分:0)
我已经使用了HashMap,发现它非常简单
import java.util.HashMap;
import java.util.Map;
/*Using Algorithms such as sieve of Eratosthanas */
public class PrimeNumber {
public static void main(String[] args) {
int prime = 15;
HashMap<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
hashMap.put(0, 0);
hashMap.put(1, 0);
for (int i = 2; i <= prime; i++) {
hashMap.put(i, 1);// Assuming all numbers are prime
}
printPrimeNumberEratoshanas(hashMap, prime);
}
private static void printPrimeNumberEratoshanas(HashMap<Integer, Integer> hashMap, int prime) {
System.out.println("Printing prime numbers upto" + prime + ".....");
for (Map.Entry<Integer, Integer> entry : hashMap.entrySet()) {
if (entry.getValue().equals(1)) {
System.out.println(entry.getKey());
for (int j = entry.getKey(); j < prime; j++) {
for (int k = j; k * j <= prime; k++) {
hashMap.put(j * k, 0);
}
}
}
}
}
}
认为这是有效的
答案 13 :(得分:0)
public static void primes(int n) {
boolean[] lista = new boolean[n+1];
for (int i=2;i<lista.length;i++) {
if (lista[i]==false) {
System.out.print(i + " ");
}
for (int j=i+i;j<lista.length;j+=i) {
lista[j]=true;
}
}
}