2016-10-11 2 views
5

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

public class Problem12 { 

    public static void main(String[] args) { 
     int num; 

     for (int i = 1; i < 15000; i++) { 
      num = i * (i + 1)/2; 
      int counter = 0; 

      for (int x = 1; x <= num; x++) { 
       if (num % x == 0) { 
        counter++; 
       } 
      } 
      System.out.println("[" + i + "] - " + num + " is divisible by " + counter + " numbers."); 
     } 
    } 
} 

EDIT: Ниже приведен новый код, который экспоненциально быстрее. Также удалена постоянная печать, чтобы ускорить ее.

public class Problem12 { 

    public static void main(String[] args) { 
     int num; 

     outerloop: 
     for (int i = 1; i < 25000; i++) { 
      num = i * (i + 1)/2; 
      int counter = 0; 

      double root = Math.sqrt(num); 
      for (int x = 1; x < root; x++) { 
       if (num % x == 0) { 
        counter += 2; 
        if (counter >= 500) { 
         System.out.println("[" + i + "] - " + num + " is divisible by " + counter + " numbers."); 
         break outerloop; 
        } 
       } 
      } 
     } 
    } 
} 
+1

Попробуйте использовать более быстрый язык, как C, и попытаться использовать bitshifts вместо разделения и умножения, если они доступны в Java –

+0

ли этот алгоритм работа на всех? так что, если 'i = 3', то' num = 6', а затем 'counter = 4', что неверно – afsafzal

+4

@ShaheAnsar, как вы можете предположить, что C для Java быстрее, чем Java, если вы не знаете достаточно, чтобы узнать, поддерживаются бит-сдвиги? – njzk2

ответ

5

Для начала, если смотреть на делителях, вы никогда не должны идти дальше, чем корень квадратный из числа, так как каждый делитель ниже квадратного корня имеет эквивалент выше.

n = a * b => a <= sqrt(n) or b <= sqrt(n) 

Затем вам нужно рассчитывать на другую сторону разделения:

double root = Math.sqrt(num); 
for (int x = 1; x < root; x++) { 
    if (num % x == 0) { 
     counter += 2; 
    } 
} 

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

if ((double) ((int) root) == root) { 
    counter += 1; 
} 
+0

Это сделало трюк. Однако я не знаю, как это сделать. Не могли бы вы подробнее остановиться на том, почему это работает? – jxshu

+1

каждый делитель работает как пара, поэтому мы подсчитываем каждый раз дважды. Для каждой пары один из элементов находится ниже квадратного корня, другой - выше. Это легко увидеть: если оба элемента выше, результат также выше. Если оба они ниже, результат также ниже. Пример: '10 = 2 * 5 = 1 * 10'. 2 и 1 ниже 3,16, 5 и 10 выше. – njzk2

+0

althoug, я ошибался в том, что там нет треугольных чисел _and_ square, теперь редактирование – njzk2

0

Вы просто необходимо разложить число. p^a * q^b * r^c имеет (a+1)*(b+1)*(c+1) делителей. Вот некоторые основные реализации, используя эту идею:

static int Divisors(int num) { 
    if (num == 1) { 
     return 1; 
    } 

    int root = (int) Math.sqrt(num); 
    for (int x = 2; x <= root; x++) { 
     if (num % x == 0) { 
      int c = 0; 
      do { 
       ++c; 
       num /= x; 
      } while (num % x == 0); 
      return (c + 1) * Divisors(num); 
     } 
    } 

    return 2; 
} 

public static void test500() { 
    int i = 1, num = 1; 
    while (Divisors(num) <= 500) { 
     num += ++i; 
    } 

    System.out.println("\nFound: [" + i + "] - " + num); 
} 
Смежные вопросы