在 Squeak Smalltalk 中,如果整数接收器是某个整数的精确 n 次方,则会有一条nthRoot:
消息回答确切的结果。Integer
但是,如果解决方案是代数根,那么实现不会退回到 naive n**(1/exp)
;该方法通过适当处理残差四舍五入到最接近的浮点数。
相关代码(MIT 许可)转载在这里。基本算法是用一些 Newton-Raphson 搜索截断的整数的第 n 根:
Integer>>nthRootTruncated: aPositiveInteger
"Answer the integer part of the nth root of the receiver."
| guess guessToTheNthMinusOne nextGuess |
self = 0 ifTrue: [^0].
self negative
ifTrue:
[aPositiveInteger even ifTrue: [ ArithmeticError signal: 'Negative numbers don''t have even roots.' ].
^(self negated nthRootTruncated: aPositiveInteger) negated].
guess := 1 bitShift: self highBitOfMagnitude + aPositiveInteger - 1 // aPositiveInteger.
[
guessToTheNthMinusOne := guess raisedTo: aPositiveInteger - 1.
nextGuess := (aPositiveInteger - 1 * guess * guessToTheNthMinusOne + self) // (guessToTheNthMinusOne * aPositiveInteger).
nextGuess >= guess ] whileFalse:
[ guess := nextGuess ].
( guess raisedTo: aPositiveInteger) > self ifTrue:
[ guess := guess - 1 ].
^guess
这不是特别聪明,因为在指数很大的情况下收敛可能会很慢,但它确实有效。然后,相同的根从零四舍五入:
Integer>>nthRootRounded: aPositiveInteger
"Answer the integer nearest the nth root of the receiver."
| guess |
self = 0 ifTrue: [^0].
self negative
ifTrue:
[aPositiveInteger even ifTrue: [ ArithmeticError signal: 'Negative numbers don''t have even roots.' ].
^(self negated nthRootRounded: aPositiveInteger) negated].
guess := self nthRootTruncated: aPositiveInteger.
^self * 2 > ((guess + 1 raisedTo: aPositiveInteger) + (guess raisedTo: aPositiveInteger))
ifTrue: [guess + 1]
ifFalse: [guess]
然后在 nthRoot 中测试准确性:
Integer>>nthRoot: aPositiveInteger
"Answer the nth root of the receiver.
Answer an Integer if root is exactly this Integer, else answer the Float nearest the exact root."
| guess excess scaled nBits |
guess := self nthRootRounded: aPositiveInteger.
excess := (guess raisedTo: aPositiveInteger) - self.
excess = 0 ifTrue: [ ^ guess ].
nBits := Float precision - guess highBitOfMagnitude.
nBits <= 0 ifTrue: [ ^(Fraction numerator: guess * 4 - excess sign denominator: 4) asFloat].
scaled := self << (nBits * aPositiveInteger).
guess := scaled nthRootRounded: aPositiveInteger.
excess := (guess raisedTo: aPositiveInteger) - scaled.
^(Fraction numerator: guess * 4 - excess sign denominator: 1 << (nBits + 2)) asFloat
这也可以应用于 Fraction,但是最近的浮点数有点复杂,而且 Squeak 的实现目前还很幼稚。
它适用于大整数,例如:
(10 raisedTo: 600) nthRoot: 300
-> 100
“准确”
(10 raisedTo: 600) + 1 nthRoot: 300
-> 100.0
“不准确”
如果您没有这样的期望,最初的猜测可能会使用 inexact naive n**(1/exp)
。
代码应该很容易在 Python 中移植,并留有很多优化空间。
我没有检查 Python 中可用的内容,但也许您需要正确舍入 LargeInteger -> Float 和 Fraction -> Float,就像在此处解释的那样(Smalltalk 也是,对此感到抱歉,但语言并不重要)。