Я пытаюсь оптимизировать небольшую программу, которая вычисляет идеальные числа по заданной экспоненте.
Программа работает (почти) идеально, но когда я открываю диспетчер задач, она по-прежнему работает в одном потоке. Это означает, что я, должно быть, делаю что-то не так, но мои знания F # все еще находятся на начальной стадии.
Я постараюсь сформулировать этот вопрос как можно яснее, но если мне это не удастся, пожалуйста, дайте мне знать.
Идеальное число - это число, в котором сумма всех его делителей (кроме самого числа) равна самому числу (например, 6 идеально, поскольку сумма его делителей 1, 2 и 3 равна 6).
Я использую простые числа для ускорения вычислений, то есть меня не интересуют (огромные) списки, в которых хранятся все делители. Для этого я использую формулу, правильность которой доказал Евклид: (2 * (степень числа - 1)) * (2 * (степень числа - 1)), где последнее является простым числом Мерсенна. Я использовал очень быстрый алгоритм из stackoverflow (от @Juliet), чтобы определить, является ли данное число простым.
Поскольку я прочитал несколько статей (я еще не купил хорошую книгу, так что позор мне) в Интернете, я обнаружил, что последовательности работают лучше, чем списки. Вот почему я впервые начал создавать функцию, которая генерирует последовательность идеальных чисел:
let perfectNumbersTwo (n : int) =
seq { for i in 1..n do
if (PowShift i) - 1I |> isPrime
then yield PowShift (i-1) * ((PowShift i)-1I)
}
Вспомогательная функция PowShift реализована следующим образом:
let inline PowShift (exp:int32) = 1I <<< exp ;;
Я использую оператор побитового сдвига, так как основа всех вычислений мощности от 2, поэтому это может быть простой способ. Конечно, я все еще благодарен за вклад в ответ на вопрос, который я задал об этом: Проблемы с питанием F #, которые принимают оба аргумента как важные> Проблемы F # Power, которые принимают оба аргумента как важные
Функция, созданная Джульеттой (заимствована здесь) выглядит следующим образом:
let isPrime ( n : bigint) =
let maxFactor = bigint(sqrt(float n))
let rec loop testPrime tog =
if testPrime > maxFactor then true
elif n % testPrime = 0I then false
else loop (testPrime + tog) (6I - tog)
if n = 2I || n = 3I || n = 5I then true
elif n <= 1I || n % 2I = 0I || n % 3I = 0I || n % 5I = 0I then false
else loop 7I 4I;;
Используя этот код без параллелизма, на моем ноутбуке требуется около 9 минут, чтобы найти 9-е совершенное число (которое состоит из 37 цифр и может быть найдено со значением 31 для показателя степени). Поскольку у моего ноутбука процессор с двумя ядрами, и только одно из них работает на 50 процентов (полная нагрузка на одно ядро), я подумал, что могу ускорить вычисления, вычисляя результаты параллельно.
Поэтому я изменил свою функцию perfectnumber следующим образом:
//Now the function again, but async for parallel computing
let perfectNumbersAsync ( n : int) =
async {
try
for x in 1.. n do
if PowShift x - 1I |> isPrime then
let result = PowShift (x-1) * ((PowShift x)-1I)
printfn "Found %A as a perfect number" result
with
| ex -> printfn "Error%s" (ex.Message);
}
Чтобы вызвать эту функцию, я использую небольшую вспомогательную функцию для ее запуска:
let runPerfects n =
[n]
|> Seq.map perfectNumbersAsync
|> Async.Parallel
|> Async.RunSynchronously
|> ignore
Если результат асинхронного вычисления игнорируется, поскольку я показываю его в функции perfectNumbersAsync.
Приведенный выше код компилируется и запускается, однако по-прежнему использует только одно ядро (хотя при вычислении 9-го совершенного числа он работает на 10 секунд быстрее). Боюсь, что это связано с вспомогательными функциями PowShift и isPrime, но я не уверен. Должен ли я помещать код этих вспомогательных функций в асинхронный блок perfectNumbersAsync? Это не улучшает читаемость ...
Чем больше я играю с F #, тем больше я учусь ценить этот язык, но, как и в этом случае, мне иногда нужны специалисты :).
Заранее спасибо, что прочитали это, я только надеюсь, что я немного ясно выразился ...
Роберт.
2^p-1
было простым числом Мерсенна,p
также должно быть простым, так что вы можете сначала проверить это. Кроме того, тест Лукаса-Ремера намного эффективнее для простых чисел Мерсенна. См. en.wikipedia.org/wiki/Lucas%E2%80%93Lehmer_primality_test а> - person Jeffrey Sax   schedule 03.12.2011