mk-mode BLOG

このブログは自作の自宅サーバに構築した Debian GNU/Linux で運用しています。
PC・サーバ構築等の話題を中心に公開しております。(クローンサイト: GitHub Pages

ブログ開設日2009-01-05
サーバ連続稼働時間
Reading...
Page View 合計
Reading...
今日
Reading...
昨日
Reading...

Ruby - 多桁計算!

[ プログラミング, 数学 ] [ Ruby ]

こんばんは。

前回は、C++ による「多桁計算」のアルゴリズムを紹介しました。

今日は、同じアルゴリズムを Ruby で実現してみました。
Ruby では桁数(整数型の範囲)をあまり気にしなくても、メモリの許される限り計算できますが、それでも都合が悪いこともあるでしょうし・・・・ アルゴリズムについては、上記リンクの記事を参照してください。

実際、ほとんど同じです。

以下、Ruby によるサンプルスクリプトです。

記録

0. 前提条件

  • Linux Mint 14 Nadia (64bit) での作業を想定。
  • Ruby 2.0.0-p0 を使用。

1. Ruby スクリプト作成

今回作成した Ruby ソースは以下の通りです。

calc_big_digits.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#*********************************************
# 多桁計算
#*********************************************
#
# 配列サイズ
N = 5

class CalcBigDigits
  def initialize
    # 計算する数字(a, b: 加減算用、c, d: 乗除算用)
    @a = [56789012,34567890,12345678,90123456,78901234]
    @b = [12345678,90123456,78901234,56789012,34567890]
    @c = [      12,34567890,12345678,90123456,78901234]
    @d = 99
  end

  # 計算・結果出力
  def calc
    # ロング + ロング
    long_add

    # ロング - ロング
    long_sub

    # ロング * ショート
    long_mul

    # ロング / ショート
    long_div
  end

  # ロング + ロング
  def long_add
    # 計算
    @z = Array.new(N, 0)
    carry = 0
    (N - 1).downto(0) do |i|
      @z[i] = @a[i] + @b[i] + carry
      if @z[i] < 100000000
        carry = 0
      else
        @z[i] = @z[i] - 100000000
        carry = 1
      end
    end

    # 結果出力
    print " "
    display_l(@a)
    print "+"
    display_l(@b)
    print "="
    display_l(@z)
    print "\n"
  end

  # ロング - ロング
  def long_sub
    # 計算
    @z = Array.new(N, 0)
    borrow = 0
    (N - 1).downto(0) do |i|
      @z[i] = @a[i] - @b[i] - borrow
      if @z[i] >= 0
        borrow = 0
      else
        @z[i] += 100000000
        borrow = 1
      end
    end

    # 結果出力
    print " "
    display_l(@a)
    print "-"
    display_l(@b)
    print "="
    display_l(@z)
    print "\n"
  end

  # ロング * ショート
  def long_mul
    # 計算
    @z = Array.new(N, 0)
    carry = 0
    (N - 1).downto(0) do |i|
      w = @c[i]
      @z[i] = (w * @d + carry) % 100000000
      carry = (w * @d + carry) / 100000000
    end

    # 結果出力
    print " "
    display_l(@c)
    print "*"
    display_s(@d)
    print "="
    display_l(@z)
    print "\n"
  end

  # ロング / ショート
  def long_div
    # 計算
    @z = Array.new(N, 0)
    remainder = 0
    0.upto(N - 1) do |i|
      w = @c[i]
      @z[i] = (w + remainder) / @d
      remainder = ((w + remainder) % @d) * 100000000
    end

    # 結果出力
    print " "
    display_l(@c)
    print "/"
    display_s(@d)
    print "="
    display_l(@z)
    print "\n"
  end

  # 結果出力(ロング用)
  def display_l(s)
    0.upto(N - 1) {|i| printf(" %08d", s[i])}
    print "\n"
  end

  # 結果出力(ショート用)
  def display_s(s)
    0.upto(N - 2) {|i| printf(" %8s", " ")}
    printf(" %08d\n", s)
  end
end

# メイン処理
begin
  # 計算クラスインスタンス化
  obj_calc = CalcBigDigits.new

  # 多桁計算
  obj_calc.calc
rescue => e
  # エラーメッセージ
  puts "[例外発生] #{e}"
end

2. 実行

実際に実行して検証してみる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ ruby calc_big_digits.rb
  56789012 34567890 12345678 90123456 78901234
+ 12345678 90123456 78901234 56789012 34567890
= 69134691 24691346 91246913 46912469 13469124

  56789012 34567890 12345678 90123456 78901234
- 12345678 90123456 78901234 56789012 34567890
= 44443333 44444433 33444444 33334444 44333344

  00000012 34567890 12345678 90123456 78901234
*                                     00000099
= 00001222 22221122 22222211 22222222 11222166

  00000012 34567890 12345678 90123456 78901234
/                                     00000099
= 00000000 12470382 72851976 55455792 49281830

C++ 版と同じ結果が得られました。


Ruby の場合、今回のような加減乗除は普通にできますが、もっと大きな数字を扱うような場合(円周率計算等)には、今回のようなアルゴリズムを意識しないといけないでしょう。

以上。

Comments