3日がかりのその仕事、3分で終わらせる方法教えます!
パソコンスキルの心技体

エクセルVBAで 1 / 998001 を計算してみる – エクセルマクロ・VB

2012年1月28日
  • このエントリーをはてなブックマークに追加
  • follow us in feedly

エクセルマクロ・VBA達人養成塾 小川です。

今朝、Facebookで「1を998001で割るとおもしろい」という趣旨のリンクシェアを見かけた。
そこで、エクセルで同様の計算をできないか、とやってみた。

Fun with math: Dividing one by 998001 yields a surprising result

Facebookでのリンク元:
1を998001で割るとスゴいことが起きる

どんな結果を得られるのかというと。

000, 001, 002, 003, 004, …, 999, 000, 001, 002, … という循環した数値らしい。

エクセルマクロ達人養成塾塾長ブログ-1 を 998001で割った結果
998001で割った結果。

今日は、思いつきで、これと同じ結果をエクセルで出力するマクロを作ってみたのでご紹介。
前回まで続けて書いていた、給与計算のマクロのお話は、またの機会にします ヾ(´ー`)ノ

今日のお話で登場するマクロは、以下からダウンロードできます。
1 / 98001 の実験サンプルデータ
http://www.exvba.com/download/blog/division98001.zip

ということで。

まず、単純な失敗例から。

以下の2つは、どちらも求める結果を出力できない。

Sub ng1()
    Range(“A1”).Value = 1 / 99801
    Debug.Print 1 / 99801
End Sub

エクセルマクロ達人養成塾塾長ブログ-エクセルのセルに 1 / 99801 の結果を出力。
エクセルのセルに 1 / 99801 の結果を出力。

イミディエイトウィンドウに出力。

何故かっちゅーと、平たく言えば、エクセルの仕様として、そもそも、こんな長い文字列になるような結果の出力を想定していないから。
テキトーに丸められて、話が終わってしまう。

ということで、テキストファイルに出力してみたいと思う。

「ライブラリの参照設定」とかの面倒な説明をいちいちしたくないから、珍しく、FreeFile関数、Openメソッド、Closeメソッドを使って書いてみた。
以下を試してみる。

さあ、どうだ!これで、このファイルのあるのと同じフォルダに、result0.txtというファイルが作られ、そこには、目指していたものが出力されるはずだ!

Sub ng2()
    Dim fl As Integer
    fl = FreeFile
    Open ThisWorkbook.Path & “\result0.txt” For Output As #fl
    Print #fl, 1 / 99801
    Close #fl
End Sub

しかし、これもダメ。結果はこんな感じ↓。

エクセルマクロ達人養成塾塾長ブログ - テキストファイルに、割り算の結果を単純に出力

テキストファイルに、割り算の結果を単純に出力。

…ということで。

自分で、割り算のアルゴリズムを考えなくてはならない。

僕は別に情報工学科の出身とかじゃないので以下が最適なアルゴリズムかどうかは分からないが、まずは書いてみた。

以下、あらかじめ、A列のセルの書式を文字列型にしてから試して欲しい。
(処理をさらに軽くしたいなら、エクセルの設定で、計算方法を、一時的に「自動」に直してからやってみるとよい。何を言っているのか分からない人は、以下に紹介する、Microsoftのサイトを参照。エクセル 2007 やエクセル 2010 でシートの計算結果が更新されない!。Excel 2003までなら、メニューの[ツール] → [オプション] → [計算方法] で「手動」を選択)

Sub calc1()
    Dim tgt As Long
    Dim div As Long
    Dim residue As Long
    Dim result As String
    
    Dim gyo As Long

    tgt = 1
    div = 998001

    result = “”
    residue = tgt
    
    result = result & residue \ div & “.”
    residue = residue Mod div & “0”
    
    gyo = 1
    Do Until residue = 0 Or gyo > 100 ‘[*]
        result = result & residue \ div
        residue = residue Mod div & “0”
        Range(“A” & gyo).Value = result
        gyo = gyo + 1
    Loop
End Sub

アルゴリズムとしては、ざっくり言うとこういう感じ↓。

[1] とりあえず、割り算をして、整数部分から求まる商を調べる。ついでに、あとあとのために、その結果の後ろに “.” を追加。
[2] 同様に、余りを調べる
[3] 余りの値を文字列とみなし、その後ろに “0” を追加する
[4] [3] で得られた文字列を割り算して得られた商を、[1]の解の後ろにくっつける
[5] [3] で得られた文字列を割り算して得られた余りを調べる

以下、なんとなく同様。

事務職の人が普段馴染みのない演算子 「\」, 「Mod」は、それぞれ、割り算の商と余りを求められる。
これらを利用する。

もしも割り切れたなら、ループを抜ける。そのほかにも、所定の回数処理を実行したら、ループを抜けることにした。

[*] のところで 回数を100にしたのは、エクセルが固まって悲鳴を上げる人がいるとかわいそうだから。
32767までの値は、いちおう入れる意味があります。(それ以上の値にしても、手元のエクセルでは、結果的にセルには長さ32767桁の文字列までしか入らないです)

うまく実行できたなら、以下のようになる。

エクセルマクロ達人養成塾塾長ブログ-書式設定を文字列型にしてから実行した結果。

以下のようなら、セルの書式が文字列型になっていないので、セルの書式を文字列型にしてからやりなおしてみてください。

エクセルマクロ達人養成塾塾長ブログ-書式設定がデフォルトのまま実行した結果。

「セルの書式を文字列型にする」ってのが何を言わんとしているか分からない人は…。まあ、ここは読み飛ばしてもらっても大丈夫です。

ということで、テキストファイルに出力してみる。

さきほどのマクロを改変。

Sub calc2()
    Dim tgt As Long
    Dim div As Long
    Dim residue As Long
    Dim result As String
    Dim fl As Integer
    Dim cnt As Long
    fl = FreeFile
    Open ThisWorkbook.Path & “\result1.txt” For Output As #fl

    tgt = 1
    div = 998001

    result = “”
    residue = tgt
    
    result = result & residue \ div & “.”
    residue = residue Mod div & “0”
    Print #fl, result
    cnt = 1
    
    Do Until residue = 0 Or cnt = 100
        result = result & residue \ div
        residue = residue Mod div & “0”
        Print #fl, result
        cnt = cnt + 1
    Loop

    Close #fl
End Sub

得られたテキストファイル「result1.txt」を開いてみると、以下のとおり。

エクセルマクロ達人養成塾塾長ブログ - 1 / 998001 の結果を、テキストファイルに出力してみた

計算の都度結果を出力すると、桁数が多くなると洒落にならないくらい処理が重たくなる。

ということで、今回は、100000回(十万回)計算をした結果だけを出力することにした。
それが以下。

Sub calc3()
    Dim tgt As Long
    Dim div As Long
    Dim residue As Long
    Dim result As String
    Dim fl As Integer
    Dim cnt As Long
    fl = FreeFile
    Open ThisWorkbook.Path & “\result2.txt” For Output As #fl

    tgt = 1
    div = 998001

    result = “”
    residue = tgt
    
    result = result & residue \ div & “.”
    residue = residue Mod div & “0”
‘    Print #fl, result
    cnt = 1
    
    Do Until residue = 0 Or cnt = 100000
        result = result & residue \ div
        residue = residue Mod div & “0”
‘        Print #fl, result
        cnt = cnt + 1
    Loop
    Print #fl, result
    Close #fl
End Sub

あと、最後にいろいろおまけ。

まずは、小数点以下の部分だけ、3桁ごとに結果を取ってきて出力してみた。

Sub calc4()
    Dim tgt As Long
    Dim div As Long
    Dim residue As Long
    Dim result As String
    Dim fl As Integer
    Dim cnt As Long
    fl = FreeFile
    Open ThisWorkbook.Path & “\result3.txt” For Output As #fl

    tgt = 1
    div = 998001

    result = “”
    residue = tgt
    
‘    result = result & residue \ div & “.”
    residue = residue Mod div & “0”
‘    Print #fl, result
    cnt = 1
    
    Do Until residue = 0 Or cnt = 10000
        result = result & residue \ div
        residue = residue Mod div & “0”
        If Len(result) = 3 Then
            Print #fl, result
            result = “”
        End If
        cnt = cnt + 1
    Loop
‘    Print #fl, result
    Close #fl
End Sub

以下ではさらに、直前の値 + 1 の関係になっているかどうかもついでにチェックしてみた。

Sub calc5()
    Dim tgt As Long
    Dim div As Long
    Dim residue As Long
    Dim result As String
    Dim chokuzen As Long
    Dim fl As Integer
    Dim cnt As Long
    fl = FreeFile
    Open ThisWorkbook.Path & “\result4.txt” For Output As #fl

    tgt = 1
    div = 998001

    result = “”
    residue = tgt
    
    chokuzen = -1
    
‘    result = result & residue \ div & “.”
    residue = residue Mod div & “0”
‘    Print #fl, result
    cnt = 1
    
    Do Until residue = 0 Or cnt = 10000
        result = result & residue \ div
        residue = residue Mod div & “0”
        If Len(result) = 3 Then
            Print #fl, result
            If chokuzen >= 0 Then
                If result = “000” Then
                    result = CLng(result) + 1000
                End If
                If Not CLng(result) = chokuzen + 1 Then
                    Debug.Print “failure: ” & cnt
                End If
            End If
            chokuzen = CLng(result) Mod 1000
            result = “”
        End If
        cnt = cnt + 1
    Loop
‘    Print #fl, result
    Close #fl
End Sub

それで、僕もやってみて分かったのだが、例えば、上記のマクロだと、以下の出力結果を得る。

failure: 2997
failure: 5994
failure: 8991

このそれぞれを見て気づいたのだが、997のあと、999が出力されている。998がすっとばされている。

本家にも、同様の指摘をするコメントを見つけた。
以下の記事の、「Goddamn」と、「John-Michael Glenn」の指摘。
http://www.exvba.com/vb/index.php

まー、数学のことはよく分からないが、とりあえず、自分なりにいろいろ調べて、空いている時間の範囲で納得できるところまで突っ込めたので満足 ヾ(´ー`)ノ

あと、最後に。

あまりエクセル得意でない人へ。
上記のマクロをいろいろ試していて、エクセルの動きが重くなって困ってきたなら、以下のマクロを実行してみてください。

たぶん、一気に動作が軽くなるでしょう。

Sub delvalues()
    Range(Range(“A1”), Range(“A1”).SpecialCells(xlCellTypeLastCell)).ClearContents
End Sub

今日のお話で登場するマクロは、以下からダウンロードできます。
1 / 98001 の実験サンプルデータ
http://www.exvba.com/download/blog/division98001.zip


お知らせ:

人気のセミナー2つを、久しぶりに開催します。受講受付開始しました。ふるってご参加ください☆

「親指シフト達人養成塾」 (あと3名)
「エクセルデータ分析7つの上級技」 (あと1名)

「エクセルデータ分析」は、ほとんどリピータの方だけで埋まってしまいまして、早くも、残席1つだけとなりました。

親指シフトも、あと3名くらいです。

●塾長のTwitterはこちらです↓。フォローお待ちしていますね。
  http://twitter.com/kanjizaibosatsu

●無料PDFレポート「誰もが知っているWindowsの、誰も知らない12の技」
  http://www.exvba.com/freereport/index.php

●法人研修のお問い合わせはこちら
  https://sv86.wadax.ne.jp/~exvba-com/closed/toiawase_houjin.php

●ジーザス小川の個人サイト「こねこねのさいと」へはこちらから
  http://www.exvba.com/

キーワード

コメント

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

最新の記事

人気記事

最新記事

カテゴリ

最新コメント

タグクラウド