ほっとひといき給湯室

ほっとひといき給湯室の掲示板です。お気軽にどうぞ!
  • 掲示板への投稿には会員登録(無料)が必要です。会員登録がまだの方はこちら
  • 掲示板ご利用上のお願い」に反するご記入はご遠慮ください。
  • Q&A掲示板の使い方はこちらをご覧ください
トピックに返信
質問

 
商、剰余、べき乗 | 〜 車輪の再発明シリーズ 〜
投稿日時: 13/03/03 13:32:32
投稿者: 月
投稿者のウェブサイトに移動

テストコードなしw
 

Option Explicit

Function Quotient(ByVal Dividend, ByVal Divisor) As Variant
    'Quotient = Dividend \ Divisor
    Quotient = CDec(Fix(Dividend / Divisor))
End Function

Function Modular(ByVal Dividend, ByVal Divisor) As Variant
    'Modular = Dividend Mod Divisor
    Modular = Dividend - Quotient(Dividend, Divisor) * Divisor
End Function

Function Power(ByVal Base As Long, ByVal Exponent As Long)
    'Power = Base ^ Exponent

    Dim Sum As Variant
    Dim n As Long
    Dim i As Long
    
    If Exponent = 0 Then
        Sum = 1
    Else
        Sum = Base
        n = Exponent - 1
        For i = 1 To n
            Sum = CDec(Sum * Base)
        Next i
    End If
    
    Power = Sum
End Function

回答
投稿日時: 13/03/07 22:56:11
投稿者: たらのり

やりすぎ?! 本当に無駄!! な、コードを公開したいだけの車輪の再発明w
 
除算ルーチンを自作してみました。
ただし・・・
 

Function Divide(ByVal d As Variant, ByVal e As Variant, _
                ByRef q As Variant, ByRef r As Variant) As Boolean
' @params
'   d   ; (in)被除数
'   e   ; (in)除数
'   q   ; (out)商
'   r   ; (out)剰余

    Dim w As Variant

    Divide = True

    q = CDec(0)
    r = CDec(0)

    If (e = 0) Then     ' ゼロ除算
        Divide = False  ' エラー
        Exit Function
    End If

    If (d = 0) Then     ' 被除数 = 0
        Exit Function
    End If

    If (d < e) Then     ' 被除数 < 除数
        r = d
        Exit Function
    End If

    w = CDec(1)         ' 桁の重み
    Do While (d - e >= e)
        e = e + e       ' e = e * 2 左シフト
        w = w + w       ' w = w * 2
    Loop

    Do While (w)
        If (d >= e) Then
            d = d - e
            q = q + w
        End If
        e = Int(e / 2)  ' ★ 右シフト
        w = Int(w / 2)
    Loop

    r = d
End Function

Sub test_driver()   ' テストコードのエントリーポイント
    Const MAX_DIGITS As Long = 28

    Dim q, r
    Dim d, e

    Dim i As Long
    Dim n As Long

    For i = 1 To 10000
        n = GetRandom(1, MAX_DIGITS)    ' 被除数の桁
        d = CDec(RandomNumberString(n))
        
        n = GetRandom(1, n)             ' 除数の桁は、被除数以下とする
        e = CDec(RandomNumberString(n))
        
        If (Divide(d, e, q, r)) Then
            Debug.Assert q = Quotient(d, e) And r = Modular(d, e)
            
            If (i Mod 100 = 0) Then
                Debug.Print d & " / " & e & " = " & q & "..." & r
            End If
        End If
    Next i

    Debug.Print "$"
End Sub

Function RandomNumberString(ByVal n As Long) As String
' @params
'   n   ; 桁数

    Dim i As Long
    Dim s As String

    s = ""
    For i = 1 To n
        s = s & CStr(GetRandom(0, 9))
    Next i

    RandomNumberString = s
End Function

Function GetRandom(ByVal lo As Long, ByVal hi As Long) As Long
' @params
'   lo  ; 下限値
'   hi  ; 上限値
    
    GetRandom = Int((hi - lo + 1) * Rnd() + lo)
End Function

・・・ただし、「出来た!!」の「た〜」の辺りで気づいてしまった
のですが、除算の実装(Divide)の中で、除算演算子("/")を使って
しまっているではないですか・・・orz (★ のところ)
 
右シフトがしたいだけなのに。。。
 
e と w は、ともに左シフトして、それをまた後戻りするだけなので、
行き(左シフト)の値を退避して、帰り(右シフト)に使用することに。
 
' その2
Function Divide(ByVal d As Variant, ByVal e As Variant, _
                ByRef q As Variant, ByRef r As Variant) As Boolean
' @params
'   d   ; 被除数
'   e   ; 除数
'   q   ; 商
'   r   ; 剰余

    Dim w As Variant
    
    Dim ae(96) As Variant   ' 左シフトの値を退避
    Dim aw(96) As Variant   '
    Dim n As Long           '

    Divide = True

    q = CDec(0)
    r = CDec(0)

    If (e = 0) Then     ' ゼロ除算
        Divide = False  ' エラー
        Exit Function
    End If

    If (d = 0) Then     ' 被除数 = 0
        Exit Function
    End If

    If (d < e) Then     ' 被除数 < 除数
        r = d
        Exit Function
    End If

    w = CDec(1)         ' 桁の重み
    
    n = 1               ' 左シフトの値を退避
    ae(n) = e           '
    aw(n) = w           '
    
    Do While (d - e >= e)
        e = e + e       ' e = e * 2  左シフト
        w = w + w       ' w = w * 2
        
        n = n + 1       ' 左シフトの値を退避
        ae(n) = e       '
        aw(n) = w       '
    Loop

    Do While (w)
        If (d >= e) Then
            d = d - e
            q = q + w
        End If
''''    e = Int(e / 2)  ' ★ 右シフト
''''    w = Int(w / 2)
        
        e = ae(n)       ' 退避した左シフトの値を復元
        w = aw(n)       ' (右シフトの代用)
        n = n - 1       '
    Loop

    r = d
End Function

かなり不恰好になりましたw
 
仮に、Long型の配列をひとつの大きな整数とするような
ユーザ定義型(※)を作成し、その型を操作するいくつかの
シンプルな機能:
 
  その型同士の比較、
  加算、
  減算、
  左右シフト、
  数字列をその型へ変換、
  その型を数字列へ変換
 
を実装すれば、かなり大きな値を扱うことが可能になり
そうです(本当かな?)。
 
(※) たとえば
Type BigInteger
    v(1 To ?) As Long
End Type

加算時のキャリーのことなどを考えると、各要素の下位 16ビット
だけを使用するなどすると、扱いやすいのか。
 
# 何をひとりで盛り上がってるんだ → オレww
 
長々とスレ汚し、失礼いたしました〜 m(_ _)m
 
# テストドライバ中の Quotient、Modular は 月 さんのソレです

回答
投稿日時: 13/03/07 22:59:27
投稿者: たらのり

符号のことは無視しているんですけどね。

トピックに返信