Squeak Smalltalk には、Integer 受信者が整数の正確な n 乗である場合nthRoot:
に正確な結果を返すメッセージがあります。Integer
ただし、解が代数根の場合、実装はナイーブにフォールバックしませんn**(1/exp)
。このメソッドは、残差を適切に処理して、最も近い float に丸めます。
関連するコード (MIT ライセンス) をここに転載します。基本アルゴリズムは、切り捨てられた整数の 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 にも適用できますが、最も近い float はもう少し複雑で、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 も、申し訳ありませんが、言語は実際には問題ではありません)。