Ваш стиль выглядит хорошо для меня. Различные шаги в алгоритме понятны, что является наиболее важной частью заставить что-то работать. Это также тактика, которую я использую для решения задач Project Euler. Сначала заставьте это работать, а затем сделайте это быстро.
Как уже отмечалось, замена Array.BinarySearch на Set.contains делает код еще более читабельным. Я обнаружил, что почти во всех PE-решениях, которые я написал, я использую массивы только для поиска. Я обнаружил, что использование последовательностей и списков в качестве структур данных более естественно в F #. Как только вы к ним привыкнете, то есть.
Я не думаю, что использование изменчивости внутри функции обязательно плохо. Я оптимизировал задачу 155 с почти 3 минут до 7 секунд с некоторыми агрессивными оптимизациями изменчивости. В целом, хотя, я бы сохранил это как шаг оптимизации и начал бы писать его, используя сгибы / фильтры и т. Д. В примере с проблемой 155 я начал использовать композицию неизменяемых функций, потому что она выполняла тестирование и, самое главное, понимание Мой подход легкий.
Выбор неправильного алгоритма гораздо более пагубен для решения, чем сначала использование более медленного неизменяемого подхода. Хороший алгоритм все еще быстр, даже если он медленнее, чем изменяемая версия ( диван привет капитан очевиден! кашель ).
Редактировать: давайте посмотрим на вашу версию
Ваша проблема 23b () заняла 31 секунду на моем ПК.
Оптимизация 1: использовать новый алгоритм.
//useful optimization: if m divides n, (n/m) divides n also
//you now only have to check m up to sqrt(n)
let factorSum2 n =
let rec aux acc m =
match m with
| m when m*m = n -> acc + m
| m when m*m > n -> acc
| m -> aux (acc + (if n%m=0 then m + n/m else 0)) (m+1)
aux 1 2
Это все еще очень функциональный стиль, но с использованием этого обновленного фактора суммы в вашем коде время выполнения увеличилось с 31 до 8 секунд.
Все по-прежнему в неизменном стиле, но давайте посмотрим, что происходит, когда вместо набора используется поиск в массиве:
Оптимизация 2: использовать массив для поиска:
let absums() =
//create abundant numbers as an array for (very) fast lookup
let abnums = [|1..28128|] |> Array.filter (fun n -> factorSum2 n > n)
//create a second lookup:
//a boolean array where arr.[x] = true means x is a sum of two abundant numbers
let arr = Array.zeroCreate 28124
for x in abnums do
for y in abnums do
if x+y<=28123 then arr.[x+y] <- true
arr
let euler023() =
absums() //the array lookup
|> Seq.mapi (fun i isAbsum -> if isAbsum then 0 else i) //mapi: i is the position in the sequence
|> Seq.sum
//I always write a test once I've solved a problem.
//In this way, I can easily see if changes to the code breaks stuff.
let test() = euler023() = 4179871
Время выполнения: 0,22 секунды (!).
Это то, что мне так нравится в F #, оно все еще позволяет вам использовать изменяемые конструкции, чтобы возиться под капотом вашего алгоритма. Но я все еще делаю это после Сначала я сделал что-то более элегантное.