F#接受两个参数为大整数的幂问题
我目前正在试验F#。互联网上的文章很有帮助,但作为一名C#程序员,我有时会遇到一些情况,我认为我的解决方案会有所帮助,但没有或只是部分帮助 因此,我对F#(很可能还有编译器的工作原理)缺乏了解,这可能是我有时完全目瞪口呆的原因 例如,我编写了一个C#程序来确定完美数。它使用了欧几里德证明的已知形式,即梅森素数2p可以形成一个完美数−1(2p−1) (其中2p-1是素数,p表示为的幂)F#接受两个参数为大整数的幂问题,f#,pattern-matching,perfect-numbers,F#,Pattern Matching,Perfect Numbers,我目前正在试验F#。互联网上的文章很有帮助,但作为一名C#程序员,我有时会遇到一些情况,我认为我的解决方案会有所帮助,但没有或只是部分帮助 因此,我对F#(很可能还有编译器的工作原理)缺乏了解,这可能是我有时完全目瞪口呆的原因 例如,我编写了一个C#程序来确定完美数。它使用了欧几里德证明的已知形式,即梅森素数2p可以形成一个完美数−1(2p−1) (其中2p-1是素数,p表示为的幂) 由于F#的帮助说明“**”可以用来计算幂,但使用浮点,因此我尝试创建一个带有位移位运算符的简单函数(我认为定义P
由于F#的帮助说明“**”可以用来计算幂,但使用浮点,因此我尝试创建一个带有位移位运算符的简单函数(我认为定义
PowBigInt
最简单的方法是使用if
而不是模式匹配:
let rec PowBigInt (x : bigint) (y : bigint) =
if y = 0I then 1I
else x * PowBigInt x (y - 1I)
问题是,bigint.Zero
是一个返回值的静态属性,但是模式只能包含(常量)文本或F#active模式。它们不能直接包含属性(或其他)调用。但是,如果您仍然喜欢匹配
,可以在where
子句中编写其他约束:
let rec PowBigInt (x : bigint) (y : bigint) =
match y with
| y when y = bigint.Zero -> 1I
| y -> x * PowBigInt x (y - 1I)
作为旁注,您可能可以使用尾部递归来提高函数的效率(其思想是,如果函数将递归调用作为最后一件事,那么可以更高效地编译它):
关于
PowBitShift
功能,我不知道为什么它会慢一些,但它肯定不能满足您的需要。使用位移位实现功率仅在基数为2时起作用。我无法理解为什么您需要y
成为int64
或bigint
。根据已知的最大的梅森尼编号为p=43112609
,其中p
确实在int
范围内
将y
作为整数,可以使用标准运算符pown:^T->int->^T
,因为:
let Pow (x : int64) y = pown x y
let PowBigInt (x: bigint) y = pown x y
关于模式匹配bigint
,错误消息非常清楚地表明,您可以通过在以下情况下使用模式匹配:
let rec PowBigInt x y =
match y with
| _ when y = 0I -> 1I
| _ -> x * PowBigInt x (y - 1I)
您不需要创建Pow函数。
(**)运算符具有bigint->int->bigint的重载。
只有第二个参数应该是整数,但我认为这对您的情况没有问题。
试试看
bigint 10**32
另一个选项是内联函数,使其与所有数字类型(支持所需运算符:(*)
,(-)
,获取一个
,和获取零
)一起工作)
我可能会建议按照Tomas的建议,将其设置为尾部递归。啊,这很清楚,我还不知道您可以在模式匹配器中设置下划线。关于bigint的需要,我正在编写一个程序,它可以显示第10个(及以上)完美数,实际上不能放在64位整数(或一些人喜欢的长整数)内。您误解了某些内容。2 ^p-1应该是bigint
,但是int
对于p
来说已经足够了。因此参数y
不必是int64
或bigint
。这与pown
具有第二个参数作为int
的原因相同。这确实正确,我真傻。然而,第47个完美数的幂为43.112.609,现在还不知道下一个幂是什么。我发现:我的目标是编写一个程序,至少可以尝试找到所有47个完美数(希望更多,但这可能是一个彻底的尝试)@RvdV79-希望更多!非常有趣的评论。我希望我有一个妻子和一个博客来谈论:D。我需要重新表述一下我的问题,我放置的代码是不正确的,因为计算需要2的幂,我认为在1(1作为基数)上向左移动(指数部分)在我的代码中,x被值1填充,它可以被常量本身所代替。我将不得不做另一个测试,看看它是否更快。谢谢你的广泛回答托马斯,我会尝试一下。你的回答引导我找到了一个较小的错误,我也会尝试修正。原谅感谢我没有留下评论Daniel,我只是错过了你的反应。尾部递归引起了我的兴趣,但你在这里的例子帮助我越来越理解F#语言。我从你们身上学到的越多,我就变得越热情!无论如何,非常感谢你的努力Daniel,非常感谢!第一个问题很好,欢迎使用堆栈溢出!
let rec PowBigInt (x : bigint) (y : bigint) =
if y = 0I then 1I
else x * PowBigInt x (y - 1I)
let rec PowBigInt (x : bigint) (y : bigint) =
match y with
| y when y = bigint.Zero -> 1I
| y -> x * PowBigInt x (y - 1I)
let PowBigInt (x : bigint) (y : bigint) =
// Recursive helper function that stores the result calculated so far
// in 'acc' and recursively loops until 'y = 0I'
let rec PowBigIntHelper (y : bigint) (acc : bigint) =
if y = 0I then acc
else PowBigIntHelper (y - 1I) (x * acc)
// Start with the given value of 'y' and '1I' as the result so far
PowBigIntHelper y 1I
let Pow (x : int64) y = pown x y
let PowBigInt (x: bigint) y = pown x y
let rec PowBigInt x y =
match y with
| _ when y = 0I -> 1I
| _ -> x * PowBigInt x (y - 1I)
val it : System.Numerics.BigInteger =
100000000000000000000000000000000 {IsEven = true;
IsOne = false;
IsPowerOfTwo = false;
IsZero = false;
Sign = 1;}
let rec inline PowBigInt (x:^a) (y:^a) : ^a =
let zero = LanguagePrimitives.GenericZero
let one = LanguagePrimitives.GenericOne
if y = zero then one
else x * PowBigInt x (y - one)
let x = PowBigInt 10 32 //int
let y = PowBigInt 10I 32I //bigint
let z = PowBigInt 10.0 32.0 //float