Python 如何在F#中处理算术运算溢出异常?
我正在做F#中的欧拉计划问题1: 3和5的倍数 如果我们列出10以下的所有自然数,它们是3或5的倍数,我们得到3、5、6和9。这些倍数之和是23 求1000以下所有3或5的倍数之和 以下是我的尝试:Python 如何在F#中处理算术运算溢出异常?,python,f#,Python,F#,我正在做F#中的欧拉计划问题1: 3和5的倍数 如果我们列出10以下的所有自然数,它们是3或5的倍数,我们得到3、5、6和9。这些倍数之和是23 求1000以下所有3或5的倍数之和 以下是我的尝试: [1..999] |>List.filter(乐趣x->x%3*x%5=0) |>List.sum val it:int=233168 我的朋友在Excel中通过将3的倍数和5的倍数相加并提取15的倍数来计算,他用一个更大的上限来挑战我:找到1234567以下所有3或5的倍数之和 我试过这个: [
[1..999]
|>List.filter(乐趣x->x%3*x%5=0)
|>List.sum
val it:int=233168
我的朋友在Excel中通过将3的倍数和5的倍数相加并提取15的倍数来计算,他用一个更大的上限来挑战我:找到1234567以下所有3或5的倍数之和
我试过这个:
[1..1234567]
|>List.filter(乐趣x->x%3*x%5=0)
|>List.sum
System.OverflowException
:算术运算导致溢出
第二次尝试:
让可变结果=0
对于x<1000 do
如果x%3*x%5=0,则结果=结果+x
错误FS0010:模式中存在意外的整数文字。应为中缀运算符、引号符号或其他标记
令我惊讶的是,Python能够很好地处理这一问题,而且效率很高:
sum(如果x%3*x%5==0,则x代表范围(1234567)内的x)
#35563612814L
%时间和(如果x%3*x%5==0,则x代表范围(1234567)内的x)
墙壁时间:401毫秒
输出:35566612814L
问题:
对于大的数字,应该使用bigint,如下所示
[1I..1234567I]
|> List.filter (fun x -> x % 3I * x % 5I = 0I)
|> List.sum
或(可读性较差)
对于数字,您也可以使用int64类型(带L后缀),使用int64可以提高整体性能
[1L..1234567L]
|> List.filter (fun x -> x % 3L * x % 5L = 0L)
|> List.sum
在我的机器上,直接翻译Python代码只需运行一半的时间,158毫秒(Python为317毫秒)
seq {
for x in 0L..1234566L do
if x % 3L * x % 5L = 0L then
yield x
}
|> Seq.sum
这段可以说更惯用的代码仍然比Python(220毫秒)运行得更快
即使是LINQ版本也更快(221ms)
让我试着回答你的问题:
Sum of all natural numbers up to 1234567 divisible by 3 or by 5 is 355,636,612,814
Result was computed in 12 ms
open System.Diagnostics
[<EntryPoint>]
let main argv =
let limit_exclusive = 1234567
let limit = limit_exclusive - 1
let divisor1 = 3
let divisor2 = 5
let stopwatch = Stopwatch.StartNew()
let mutable sum = 0UL
for x = 1 to limit do
if (x % divisor1 = 0) || (x % divisor2 = 0) then
sum <- sum + uint64 x
stopwatch.Stop()
printfn "Sum of all natural numbers up to %d divisible by %d or by %d is %d" limit_exclusive divisor1 divisor2 sum
printfn "Result was computed in %d ms" stopwatch.ElapsedMilliseconds
0 // return an integer exit code
Sum of all natural numbers up to 1234567 divisible by 3 or by 5 is 355,636,612,814
Result was computed in 12 ms
请注意,结果是一个大数字-Python具有任意精度整数(在2.x中,它们在某一点上变长,如您所见),但F#没有-例如,非常感谢
int64(L)
是一个很好的解决方案。我在我的电脑上运行了它,花费了598 ms
。我想知道为什么它比python慢,因为python是解释的,F#是JIT编译的?通过创建[1L..1234567L]
实际上创建了列表,然后过滤元素(在过程中复制列表),然后对结果求和。如果您使用seq{1L..1234567L}
和seq.filter
和seq.sum
,那么内存应该会更少,而且很可能运行得更快。@Danielbabian是的,seq
比列表
快,在我的计算机上花了488 ms
,仍然比Python慢。虽然执行时间在这个问题上并不重要,但性能让我感到惊讶。(谢谢你)这个版本应该会让python代码冒烟,以防你觉得有必要:让x=1到1234567的可变sum=0L,如果x%3*x%5=0,那么求和很好,是和否。是的,肯定会有一些开销,但大约95%的时间与此无关。再加上功能性思维允许您进行高级优化,这是由于可变状态而不允许的。也就是说,当您确实需要性能时,有时可能会以命令式风格重写某些内部循环。(我们做大量的数字运算,除了一些矩阵mul循环外,我们仍然以函数式的方式做所有事情)是的,在我的计算机上是233ms
,大约是Python的一半时间。这是合理的;它解决了我对Interped语言和JIT编译语言性能的混乱理解。非常感谢。
query {
for x in 0L .. 1234566L do
where (x % 3L * x % 5L = 0L)
sumBy x
}
Sum of all natural numbers up to 1234567 divisible by 3 or by 5 is 355,636,612,814
Result was computed in 12 ms
open System.Diagnostics
[<EntryPoint>]
let main argv =
let limit_exclusive = 1234567
let limit = limit_exclusive - 1
let divisor1 = 3
let divisor2 = 5
let stopwatch = Stopwatch.StartNew()
let mutable sum = 0UL
for x = 1 to limit do
if (x % divisor1 = 0) || (x % divisor2 = 0) then
sum <- sum + uint64 x
stopwatch.Stop()
printfn "Sum of all natural numbers up to %d divisible by %d or by %d is %d" limit_exclusive divisor1 divisor2 sum
printfn "Result was computed in %d ms" stopwatch.ElapsedMilliseconds
0 // return an integer exit code
Sum of all natural numbers up to 1234567 divisible by 3 or by 5 is 355,636,612,814
Result was computed in 12 ms
let seq1 = seq { 0 .. divisor1 .. limit }
let seq2 = seq { 0 .. divisor2 .. limit }
let both = Seq.append seq1 seq2
let sum = both
|> Seq.distinct
|> Seq.map uint64
|> Seq.sum
Sum of all natural numbers up to 1234567 divisible by 3 or by 5 is 355,636,612,814
Result was computed in 535 ms