F# 中不可变集和映射的样式是什么
我刚刚解决了 Project Euler 中的 problem23 ,其中我需要一个设置来存储所有丰富的数字。 F# 有一个不可变的集合,我可以使用 Set.empty.Add(i) 创建一个包含数字 i 的新集合。但我不知道如何使用不可变集来做更复杂的事情。
例如,在下面的代码中,我需要看看数字“x”是否可以写成一组中两个数字的和。我求助于排序数组和数组的二分搜索算法来完成工作。
还请评论我以下节目的风格。谢谢!
let problem23 =
let factorSum x =
let mutable sum = 0
for i=1 to x/2 do
if x%i=0 then
sum <- sum + i
sum
let isAbundant x = x < (factorSum x)
let abuns = {1..28123} |> Seq.filter isAbundant |> Seq.toArray
let inAbuns x = Array.BinarySearch(abuns, x) >= 0
let sumable x =
abuns |> Seq.exists (fun a -> inAbuns (x-a))
{1..28123} |> Seq.filter (fun x -> not (sumable x)) |> Seq.sum
更新后的版本:
let problem23b =
let factorSum x =
{1..x/2} |> Seq.filter (fun i->x%i=0) |> Seq.sum
let isAbundant x = x < (factorSum x)
let abuns = Set( {1..28123} |> Seq.filter isAbundant )
let inAbuns x = Set.contains x abuns
let sumable x =
abuns |> Seq.exists (fun a -> inAbuns (x-a))
{1..28123} |> Seq.filter (fun x -> not (sumable x)) |> Seq.sum
这个版本运行了大约27秒,而前23秒(我运行了好几次)。因此,与使用二分搜索的排序数组相比,不可变的红黑树实际上并没有降低太多速度。集合/数组中的元素总数为 6965
。
I have just solved problem23 in Project Euler, in which I need a set to store all abundant numbers. F# has a immutable set, I can use Set.empty.Add(i)
to create a new set containing number i
. But I don't know how to use immutable set to do more complicated things.
For example, in the following code, I need to see if a number 'x' could be written as the sum of two numbers in a set. I resort to a sorted array and array's binary search algorithm to get the job done.
Please also comment on my style of the following program. Thanks!
let problem23 =
let factorSum x =
let mutable sum = 0
for i=1 to x/2 do
if x%i=0 then
sum <- sum + i
sum
let isAbundant x = x < (factorSum x)
let abuns = {1..28123} |> Seq.filter isAbundant |> Seq.toArray
let inAbuns x = Array.BinarySearch(abuns, x) >= 0
let sumable x =
abuns |> Seq.exists (fun a -> inAbuns (x-a))
{1..28123} |> Seq.filter (fun x -> not (sumable x)) |> Seq.sum
the updated version:
let problem23b =
let factorSum x =
{1..x/2} |> Seq.filter (fun i->x%i=0) |> Seq.sum
let isAbundant x = x < (factorSum x)
let abuns = Set( {1..28123} |> Seq.filter isAbundant )
let inAbuns x = Set.contains x abuns
let sumable x =
abuns |> Seq.exists (fun a -> inAbuns (x-a))
{1..28123} |> Seq.filter (fun x -> not (sumable x)) |> Seq.sum
This version runs in about 27 seconds, while the first 23 seconds(I've run several times). So an immutable red-black tree actually does not have much speed down compared to a sorted array with binary search. The total number of elements in the set/array is 6965
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
你的风格很适合我。算法中的不同步骤是清晰的,这是使某些东西发挥作用的最重要的部分。这也是我用来解决欧拉计划问题的策略。首先使其工作,然后使其快速。
正如已经说过的,用 Set.contains 替换 Array.BinarySearch 使代码更具可读性。我发现在我编写的几乎所有 PE 解决方案中,我只使用数组进行查找。我发现在 F# 中使用序列和列表作为数据结构更加自然。一旦你习惯了它们,就是这样。
我不认为在函数内部使用可变性一定是坏事。我通过一些积极的可变性优化将问题 155 从近 3 分钟缩短到 7 秒。一般来说,我会将其保存为优化步骤,并开始使用折叠/过滤器等来编写它。在问题 155 的示例中,我确实开始使用不可变函数组合,因为它进行了测试,最重要的是,理解,我的做法很简单。
选择错误的算法比首先使用速度稍慢的不可变方法对解决方案的危害要大得多。一个好的算法即使比可变版本慢,仍然很快(沙发你好队长明显!咳嗽)。
编辑:让我们看看您的版本
您的 Problem23b() 在我的电脑上花了 31 秒。
优化1:使用新算法。
这仍然非常符合函数式风格,但在代码中使用更新后的 FactorSum,执行时间从 31 秒缩短到 8 秒。
一切仍然是不可变的风格,但让我们看看使用数组查找而不是集合时会发生什么:
优化 2:使用数组进行查找:
执行时间:0.22 秒(!)。
这就是我非常喜欢 F# 的原因,它仍然允许您使用可变结构来修补算法的底层。但我仍然只是在先完成一些更优雅的工作之后才这样做。
Your style looks fine to me. The different steps in the algorithm are clear, which is the most important part of making something work. This is also the tactic I use for solving Project Euler problems. First make it work, and then make it fast.
As already remarked, replacing Array.BinarySearch by Set.contains makes the code even more readable. I find that in almost all PE solutions I've written, I only use arrays for lookups. I've found that using sequences and lists as data structures is more natural within F#. Once you get used to them, that is.
I don't think using mutability inside a function is necessarily bad. I've optimized problem 155 from almost 3 minutes down to 7 seconds with some aggressive mutability optimizations. In general though, I'd save that as an optimization step and start out writing it using folds/filters etc. In the example case of problem 155, I did start out using immutable function composition, because it made testing and most importantly, understanding, my approach easy.
Picking the wrong algorithm is much more detrimental to a solution than using a somewhat slower immutable approach first. A good algorithm is still fast even if it's slower than the mutable version (couch hello captain obvious! cough).
Edit: let's look at your version
Your problem23b() took 31 seconds on my PC.
Optimization 1: use new algorithm.
This is still very much in functional style, but using this updated factorSum in your code, the execution time went from 31 seconds to 8 seconds.
Everything's still in immutable style, but let's see what happens when an array lookup is used instead of a set:
Optimization 2: use an array for lookup:
Execution time: 0.22 seconds (!).
This is what I like so much about F#, it still allows you to use mutable constructs to tinker under the hood of your algorithm. But I still only do this after I've made something more elegant work first.
您可以根据给定的值序列轻松创建
Set
。因此,
inAbuns
将被重写为Seq.exists
将更改为Set.exists
但数组实现也很好......
请注意,有不需要在
factorSum
中使用可变值,除了它是不正确的,因为您计算的是除数的数量而不是它们的总和:You can easily create a
Set
from a given sequence of values.inAbuns
would therefore be rewritten toSeq.exists
would be changed toSet.exists
But the array implementation is fine too ...
Note that there is no need to use mutable values in
factorSum
, apart from the fact that it's incorrect since you compute the number of divisors instead of their sum:这是一个简单的功能解决方案,它比原来的更短,速度快了 100 倍以上:
第一个技巧是记录在由数字本身索引的数组中哪些数字丰富,而不是使用搜索结构。第二个技巧是注意所有时间都花在生成该数组上,因此要并行执行。
Here is a simple functional solution that is shorter than your original and over 100× faster:
The first trick is to record which numbers are abundant in an array indexed by the number itself rather than using a search structure. The second trick is to notice that all the time is spent generating that array and, therefore, to do it in parallel.