IT女子がお届けするオフィスワーク効率化・VBA技術紹介

ノンプログラマーによるノンプログラマーのためのやさしい解説付き

スポンサーリンク

【ノンプロ研_お題】世界のナベアツ問題をVBAとPythonで書いてみた

2018/6~ノンプロ研のSlackで「今週のお題」チャンネルがスタートしました。

1つのお題に対してみんなで解答を投稿してワイワイやるチャンネルです。

プログラミングっておもしろいもので、学校のテストみたいに「これが正解!」ってものがないので、互いにコードを見てガヤガヤすることで勉強になるんですね。

栄えある第1回目は、かの有名な?「世界のナベアツ問題」です。

勉強がてらVBAとPythonの2言語で書いてみました。


f:id:excel-accounting:20180603201951p:plain:w400

世界のナベアツ問題とは?

【問題】
1~100までの数字を出力する

ただし、下記の場合は「Aho」と出力する
「3の倍数」または「3の数字がつく」

最後に「オモロー!」と出力する

VBAで書くと

Sub 世界のナベアツ問題()

    Dim num As Long
    For num = 1 To 100
    
        '3の倍数、または、3を含む数値
        If num Mod 3 = 0 Or InStr(Str(num), "3") > 0 Then
            Debug.Print "Aho"
        Else
            Debug.Print num
        End If
    
    Next num
    
    Debug.Print "オモロー!" '最後に

End Sub

Pythonで書くと

for n in range(1, 101):
    #3の倍数、または、3を含む数値
    if n % 3 == 0 or str(n).find('3') >= 0:
        print('Aho')
    else:
        print(n)
print('オモロー!')

シンプル!

Python解説

for n in range(1, 101):
1以上・101未満の間繰り返す
→1~100まで繰り返す

n % 3 == 0
nを3で割った時の余りが0
→n=3の倍数

このあたりは、VBAvsPython算術演算子もご覧ください!

str(n).find('3') >= 0
文字列に'3'が含まれるかチェック

①str(n)
nはint型なので、str関数で文字列に変換してからfindします。

②find検索
検索文字列が、
・見つかった場合:0からはじまる文字列位置を返す
・見つからない場合:-1を返す

出力結果

VBA,Python同じです。

1
2
Aho
4
5
Aho
7
8
Aho
10
11
Aho
Aho
14
Aho



95
Aho
97
98
Aho
100
オモロー!

※30番台はすべてAho

VBAで処理速度の計測

このナベアツ問題のキモは、

「3の倍数」または「3の数字がつく」

の評価方法です。

If文で2つの条件を判定するので、
num Mod 3 = 0
InStr(Str(num), "3") > 0

2通りの実装方法が考えられます。

  • 複数条件を同時に評価する方法
  • 条件を1つずつ評価する方法


これら2パターンの処理速度を計測してみます。

※ノイズ除去のため、条件に合致しない数字の出力は無しとする

【パターン1】同時評価

 '3の倍数、または、3を含む数値
If num Mod 3 = 0 Or InStr(Str(num), "3") > 0 Then
    Debug.Print "Aho"
End If

【パターン2】個別評価

If num Mod 3 = 0 Then '3の倍数
    Debug.Print "Aho"
ElseIf InStr(Str(num), "3") > 0 Then '3を含む数値
    Debug.Print "Aho"
End If

結果は?

For文で10万回繰り返した結果です。

(単位:秒)

同時評価 個別評価
12.328 12.636
12.576 12.356
12.603 12.505
11.445 12.494
12.479 12.431

差、なし・・・

個別評価のほうが速そうな気がしたのでなんだかこれでは納得いかず…

f:id:excel-accounting:20180426143057p:plain:w150

そういえば、VBAって書き込み系の処理が遅いんだよな…

ってことは、差が付かない理由はDebug.printかな?

Debug.printやめます!

(特に意味はないですが)条件に合致した場合をカウントします。

【パターン1】同時評価

'3の倍数、または、3を含む数値
If num Mod 3 = 0 Or InStr(Str(num), "3") > 0 Then
    cnt = cnt + 1
End If

【パターン2】個別評価

If num Mod 3 = 0 Then '3の倍数
    cnt = cnt + 1
ElseIf InStr(Str(num), "3") > 0 Then '3を含む数値
    cnt = cnt + 1
End If

For文で100万回繰り返した結果です。
(10万回ではそこまで差が出ず)

(単位:秒)

同時評価 個別評価
0.434 0.292
0.436 0.292
0.43 0.284
0.457 0.285
0.437 0.288


差、出ました!

f:id:excel-accounting:20180428154537p:plain:w150


やっぱり個別評価の方が速いですね!


「どうすれば処理速度が改善するか」を考えるのはもちろんのこと、

「どういう条件下ならばノイズなく測定できるか」を考えるのも、またおもしろいですね!


こちらの記事もご覧になってくださいね。
www.excel-prog.com

スポンサーリンク