Вычисление простых чисел до N целых чисел - PullRequest
1 голос
/ 28 ноября 2010

Я пытаюсь написать небольшой скрипт, чтобы вычислить все простые числа до n (аргумент, предоставленный пользователем), и был бы признателен за небольшую помощь. Я хочу использовать ArrayLists для написания этой функции и, надеюсь, сделать ее настолько эффективной, насколько это возможно - еще одна важная вещь, которую я не могу понять, это делать все как обычно и с хорошей практикой (т.е. иметь классы заглавными буквами и т. Д. ) поэтому, если вы не возражаете, пожалуйста, укажите на любые ошибки в этом отношении, чтобы я мог их исправить.

Вот код, который я написал до сих пор, но я знаю, что есть много способов его оптимизации - в частности, у меня есть логический массив, хранящий, является ли число простым или нет. Конечно, есть лучший способ сделать это, чем перебирать массив и делать все простыми, а затем избавляться от чисел, которые не являются?

Большое спасибо за ваше время! :)

(tl; dr - запись сценария на простые числа компьютера вплоть до N. Я хочу использовать ArrayLists для этого. Как я могу улучшить свой код, особенно неэффективный логический массив).

import java.util.*;
public class Prime {

  public static void main( String[] args ){

  }
  public static void prime( int initialCapacity){
    int index=0;
    ArrayList<Integer> listOfPrimeNumbers = new ArrayList<Integer>( );
    boolean[] isPrimeNumber = new boolean[initialCapacity + 1]; // boolean defaults to
    // false
    while ( index <= listOfPrimeNumbers.size() )
    {
      isPrimeNumber[index] = true;
      if (isPrimeNumber[index]) {
        listOfPrimeNumbers.add(index);
      }
      for (int j = index; j * index <= initialCapacity; j++) {
        isPrimeNumber[index * j] = false;
      }

      // Now mark the multiple of i as non-prime number
      index++;
    }
  }

}

Ответы [ 6 ]

2 голосов
/ 28 ноября 2010

Вы можете установить начальную емкость listOfPrimeNumbers, потому что вы можете оценить, сколько простых чисел меньше N. См.

http://en.wikipedia.org/wiki/Prime_number_theorem

но в основном n / (ln n) должно быть начальной емкостью listOfPrimeNumbers. Это гарантирует, что ваша программа не будет постоянно изменять размер списка под обложками, что может быть дорого.

То есть, если вы действительно хотите быть эффективными. Если вам все равно, то просто установите начальную емкость этого списка на что-то более высокое. Прямо сейчас у вас установлено значение по умолчанию, что означает, что ваш listOfPrimeNumbers будет иметь расширение.

РЕДАКТИРОВАТЬ - Я думаю, у вас есть ошибка - строка

isPrimeNumber[index] = true;

должен выполняться только, если index - простое число, поэтому поместите его в соответствующий оператор if. Опять же, я не знаю, как работает ваш материал, но я думаю, что это проблема.

Альтернативой может быть использование карты в качестве средства проверки isPrimeNumber.

1 голос
/ 29 ноября 2010

Этот ответ разделен на 2 части: ваш главный решатель (сито чисел) и массив, содержащий ваши числа.

Solver: Основная область оптимизации, на которой вам нужно сосредоточиться, - это «решетка чисел». Это метод, с помощью которого вы сами определяете, что число простое. В настоящее время невозможно превратить ваше сито в одно выражение, чтобы определить, является ли оно простым, поэтому вы должны стремиться к быстрому устранению. Причина этого в том, что деление и умножение - две самые дорогие арифметические функции, которые вы можете выполнять. Устранение необходимости делать это везде, где это возможно, сэкономит вам больше всего времени. Ниже приведены некоторые основные правила простых чисел (некоторые могут показаться очевидными).

  • Число не может быть простым, если оно четное (делится на 2). Самый быстрый способ проверить это:
    if ((i && 1) == 1) //prime candidate
  • Число является простым, если оно не делится на любые предыдущие простые числа. Другими словами, вам нужно только проверить, используя простые числа, которые вы уже нашли.
    for (currentPrime in listOfPrimes) {
    if (i mod currentPrime == 0) {
        //not Prime: Exit For loop
    }
    } 
  • Вам нужно только проверить предыдущие простые числа на 1/3 от текущего i, который вы проверяете (вы уже удалили делится на 2)
  • Конечно, есть и другие небольшие оптимизации, но это основная их часть (в настоящее время). Вот скорректированная петля сверху.
    if ((i && 1) == 1) {
    threshHold = (int)(i / 3); //So you don't have to keep recalcing
    isPrime = true; //Assume true until told otherwise
    for (currentPrime in listOfPrimes) {
        if ((i mod currentPrime) == 0) {
            isPrime = false; //Flag as non prime
            break; //Exit For Loop early
        }
        if (currentPrime > threshHold) {
            break;
        }
    }
    if (isPrime) {
        //Record new Prime
    }
    }

ArrayList: Что касается вашего ArrayList, самый эффективный способ оптимизировать это - не хранить ненужную вам информацию. Поскольку (согласно вашему описанию) вы ищете простые, а не простые числа, хранение логического ArrayList для определения, является ли оно простым, не является вашей лучшей ставкой.

Было бы гораздо эффективнее хранить только те простые числа, которые вы найдете, поскольку все, что отсутствует в списке, определенно не является простым. В конце концов, число либо простое, либо нет, поэтому если в списке это простое число, а если нет, то это не так. Это особенно верно, так как ваше числовое сито должно проверяться только против предварительно определенных простых чисел. Это приводит к меньшему количеству записей в ArrayList и меньшему количеству элементов в целом. Кроме того, ваш принудительный вывод о хранении только простых чисел делает ваше логическое значение всегда равным истинному значению, то есть нет необходимости проверять его.

Моя рекомендация такова: измените ваш ArrayList на целое (или длинное) и используйте его как для проверки, так и для вывода, поскольку он служит обеим целям. Существует ряд эффективных способов настроить ваш вывод на основе этого, поэтому это никоим образом не ограничивает вас. Дополнительные функции, позволяющие определить, является ли данное число простым (с учетом вашего списка), - это просто, даже учитывая ArrayList.

FuzzicalLogic

1 голос
/ 28 ноября 2010

Я хочу использовать ArrayLists для написания этой функции и, надеюсь, сделать ее максимально эффективной.

На самом деле, boolean[] будет более эффективным для этого приложения, чем ArrayList<Boolean>. Массив логических значений занимает меньше места, а операции выполняются быстрее. (ArrayList лучше, если вам требуется List или если вам нужна массивоподобная структура данных, которая может увеличиваться при добавлении в нее элементов.)

Еще одна важная вещь для меня, которую я, кажется, не могу понять, это делать все как стандартную и хорошую практику (т.е. иметь классы заглавными буквами и т. Д.), Поэтому, если вы не возражаете, пожалуйста, укажите на любые ошибки в этом отношении чтобы я мог их починить.

Я могу сделать лучше, чем это. Вот ссылка на оригинальное Sun Java Style Guide .

Конечно, есть лучший способ сделать это, чем циклически проходить по массиву и делать все простыми, а затем избавляться от чисел, которых нет?

Я полагаю, что вы можете изменить массив на массив "не простых чисел" и полагаться на тот факт, что массив boolean инициализируется для всех false. Но это делает код немного искаженным.

Вы также можете заменить boolean[] на int[] и использовать сдвиг / маскирование для установки / очистки отдельных битов. Это позволит вам просеивать больше простых чисел с тем же объемом памяти, но не обязательно заставит программу работать быстрее.

0 голосов
/ 28 ноября 2010

Не похоже, что это должно работать правильно.Вы говорите, что каждое число является простым числом с isPrimeNumber[index] = true, а затем проверяет, является ли isPrimeNumber[index] истинным;очевидно, это произойдет, поскольку вы просто установите его в значение true.

Тем не менее алгоритм, который вы пытаетесь использовать, - это тот, который я собирался предложить, прежде чем я понял, что вы делаете;это хорошо и очень просто.Вы можете оптимизировать его, чтобы быть более эффективным, хотя;нет необходимости в умножении.

Вот вам какой-то псевдокод, чтобы помочь вам.

make a new int[initialCapacity] // or boolean[]

for i = 0 to initialCapacity-1
    array[i] = 1 // or = i, or whatever, even i+2 and you could start iterating next loop at 0
/* optionally, you can skip the above step and rely on the fact that all values will
    default to 0/false, and you can treat 0/false as prime and >0/true as not prime */

for i = 2 to initialCapacity-1
    if array[i] = 0 continue
    i is prime, do something with it here
    for j = i+i to initialCapacity-1 step i
        array[i] = 0

При пересечении массива вместо сложения используется сложение.Основное отличие здесь заключается в том, что он проверяет, является ли текущее число простым, а затем воздействует на него вместо того, чтобы сказать «это простое число. Теперь, когда я объявил его простым, я сказал, что оно простое?установить дальше, независимо от того, что вам нужно ". Кроме того, вы должны убедиться, что вы не уменьшаете набор на 1, поэтому вы пропускаете 1 и начинаете с 2.

0 голосов
/ 28 ноября 2010

Ваше текущее решение выглядит очень разбитым и, если я не ошибаюсь, пойдет в бесконечный цикл. Вы можете легко исправить это, переместив строку isPrimeNumber[index] = true; за пределы цикла while (см. [1] ниже), то есть инициализируйте все, кроме 0 и 1, до true в начале и начните с index=2, в противном случае вы мы собираемся установить все на false, так как все кратно 1. То, что вы ввели, называется Сито Эратосфена . Вы можете стать немного более эффективным с Сито Аткинса , но, вероятно, вы за пределами вашей досягаемости.

[1] Чтобы объяснить, что я имел в виду, переместив линию за пределы цикла while (я также переключаюсь на более подходящий цикл for одновременно):

for (int i = 0; i <= initialCapacity; i++)
  isPrimeNumber[i] = true;
for (int index = 0; index <= initialCapacity; index++) {
  if (isPrimeNumber[index]) {
  // rest of code here...
}
0 голосов
/ 28 ноября 2010

Глупое решение.

boolean[] notPrime = new boolean[n];
for (int i = 2; i <= n; i++) {
    for (int j = i * 2; j <= n; j+=i) {
        notPrime[j-1] = true;
    }
}
ArrayList<Integer> primes = new ArrayList<Integer>();
for (int i = 0; i < n; i++) {
    if (!notPrime[i]) {
        primes.add(i+1);
    }
}
...