裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

「電卓」の仕様(その3)

2016年01月15日 | ブログラミング

「ほどほどの大きさの整数を扱える,足し算,引き算,掛算,累乗ができる。ただし,挿入演算子を使うのではない」という仕様の電卓プログラム

digits = 100
carry = function(res) {
    len = length(res)
    cry = 0
    for (i in 1:len) {
        t = res[i] + cry
        res[i] =t %% 10
        cry = t %/% 10
    }
    if (cry) res[len+1] = cry
    res
}

add = function(a, b) {
    if (length(a) == 1 && class(a) != "LongInt") a = as.LongInt(a)
    if (length(b) == 1 && class(b) != "LongInt") b = as.LongInt(b)
    if (a[digits] == 9) a = -compliment(a)
    if (b[digits] == 9) b = -compliment(b)
    res = carry(a+b)[1:digits]
    class(res) = "LongInt"
    res
}

sub = function(a, b) add(a, -b)

compliment = function(a) {
    add(9-a, c(1, rep(0, digits-1)))[1:digits]
}

operand = function(a) {
    if (length(a) == 1 && class(a) != "LongInt") a = as.LongInt(a)
    sign = 1
    if (a[digits] == 9) {
        a = compliment(a)
        sign = -1
    }
    return(list(sign= sign, operand=cutZero(a)))
}

mult = function(a, b) {
    a0 = operand(a)
    a = a0$operand
    b0 = operand(b)
    b = b0$operand
    len.a = length(a)
    len.b = length(b)
    res = integer(len.a+len.b)
    for (j in 1:len.b) {
        for (i in 1:len.a) {
            res[i+j-1] = res[i+j-1] + a[i]*b[j]
        }
    }
    res = fillZero(carry(res))
    if (length(res) > digits) {
        stop("Overflow")
    } else if (a0$sign * b0$sign == -1) {
        res = compliment(res)
    }
    class(res) = "LongInt"
    res
}

cutZero = function(a) {
    a = rev(a)
    res = rev(a[cumsum(a) > 0])
    res
}

fillZero = function(a) {
    res = a
    len = length(a)
    if (digits > len) res = c(res, rep(0, digits-len))
    res
}

as.LongInt = function(a) {
    res = rev(unlist(strsplit(as.character(a), "")))
    len = length(res)
    if (res[len] == "-") {
        res = compliment(fillZero(as.integer(res[-len])))
    } else {
        res = as.integer(res)
    }
    res = fillZero(res)
    class(res) = c("LongInt", "integer")
    res
}

print.LongInt = function(a) {
    if (all(a == 0)) {
        a = 0
    } else {
        Sign = ""
        if (a[digits] == 9) {
            a = compliment(a)
            Sign = "-"
        }
        a = paste0(Sign, paste(rev(cutZero(a)), collapse=""))
    }
    cat(a, "\n", sep="")
    return(a)
}

power = function(a, n) {
    if (length(a) == 1 && class(a) != "LongInt") a = as.LongInt(a)
    result = as.LongInt(1)
    for (i in 1:n) {
        result = mult(result, a)
    }
    result
}
square = function(a) power(a, 2)
cubic = function(a) power(a, 3)

###

> add(-13, 3)
-10
> mult(123, 5)
615
> sub(add(square(3), square(4)), square(5))
0
>
> a = as.LongInt(5999856)
> b = as.LongInt("99992800") # 大きな数値は文字型で与えると安心
> c = as.LongInt("100000000")
> sub(add(cubic(a), cubic(b)), cubic(c))
-2985984
>
> # 階乗
> a = as.LongInt(1) # これは必須
> for (i in 1:60) a = mult(a,i)
> a
8320987112741390144276341183223364380754172606361245952449277696409600000000000000
> library(gmp)
> factorialZ(60)
Big Integer ('bigz') :
[1] 8320987112741390144276341183223364380754172606361245952449277696409600000000000000
>
> # 2 のべき乗
> power(2, 300)
2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397376
> as.bigz(2)^300
Big Integer ('bigz') :
[1] 2037035976334486086268445688409378161051468393665936250636140449354381299763336706183397376
>
> # フィボナッチ数列
> n = 400
> a = as.LongInt(1)
> b = as.LongInt(1)
> for (i in 3:n) {
+     c = add(a, b)
+     a = b
+     b = c
+ }
> c
176023680645013966468226945392411250770384383304492191886725992896575345044216019675
> fibnum(n)
Big Integer ('bigz') :
[1] 176023680645013966468226945392411250770384383304492191886725992896575345044216019675


コメント    この記事についてブログを書く
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする
« 「電卓」の仕様(その2) | トップ | GAWK で mpfr を使いたいんだ... »
最新の画像もっと見る

コメントを投稿

ブログラミング」カテゴリの最新記事