何となく Blog by Jitta
Microsoft .NET 考

目次

Blog 利用状況
  • 投稿数 - 761
  • 記事 - 18
  • コメント - 37042
  • トラックバック - 222
ニュース
  • IE7以前では、表示がおかしい。div の解釈に問題があるようだ。
    IE8の場合は、「互換」表示を OFF にしてください。
  • 検索エンジンで来られた方へ:
    お望みの情報は見つかりましたか? よろしければ、コメント欄にどのような情報を探していたのか、ご記入ください。
It's ME!
  • はなおか じった
  • 世界遺産の近くに住んでます。
  • Microsoft MVP for Visual Developer ASP/ASP.NET 10, 2004 - 9, 2011
広告

記事カテゴリ

書庫

日記カテゴリ

ギャラリ

その他

わんくま同盟

同郷

 

C Magazine の最後に載っていたパズルを楽しみにされていた方には当たり前のことですが、繰り返し処理をする場合、繰り返さなければならない条件をあらかじめ限定してしまうと、実行スピードが飛躍的に向上します。

例として、1~1000 の範囲で、素数を求めてみます。

素数は、1以上の整数で、1とその数以外に割り切ることのできる数がない数字です。具体的に列挙すると、2, 3, 5, 7, 11, 13, 17, 19, ... という感じ。

こいつを安直に求めるなら、こんな風になります。

2~1000 までのループをする(変数N)
  割り切れたフラグ = false
  2~(N-1)までのループをする(変数M)
    NをMで割り、余りを求める
    あまりがなければ割り切れたフラグを true にして、ブレーク
  変数Mのループ
  割り切れたフラグが false なら、Nを印字する
変数Nのループ


さて、これの、どこが無駄なのでしょうか?

変数Nのループですが、2 以外の“偶数”は、必ず 2 で割れるので、素数ではないことがわかりきっています。そのため、4 以降の偶数をループするのが無駄。Mのループについても、2 で割り切れなかったということは、他の偶数でも割り切れるはずがありません。従って、Mのループも1つ飛ばしにできます。

数学的に詳しい人なら、Mのループ回数をさらに減らせるでしょう。

"2" を印字する
3~1000 までのループをする(変数N、増分 2)
  割り切れたフラグ = false
  3~(N-1)までのループをする(変数M、増分 2)
    NをMで割り、余りを求める
    あまりがなければ割り切れたフラグを true にして、ブレーク
  変数Mのループ
  割り切れたフラグが false なら、Nを印字する
変数Nのループ


単純に考えます。NもMもループする回数が半分になっているので、下のコードでだと、上のコードの1/4の時間で実行が完了することが見込めます。製造時にちょっと悩むことによって、実行時の時間が1/4になる。すばらしいことだと思いませんか?

もちろん、微々たる実行時間が短縮することと、製造時間をかけることのバランスを気にする人もいるでしょう。

しかし。ソフトウェア提供者が提供するのは、単なるソフトウェアなのでしょうか?

あるいは、同じことができる機械、たとえばミュージック プレイヤーを購入するとき、同じような機能なのに iPod が売れている理由は何でしょう?

同じことができるのであるなら、より使いやすい、よりわかりやすいものを選ぶのではないでしょうか。では、同じ「素数を求める」プログラムで、実行時間が 4秒のものと 1秒のものがあるなら、1秒のものを選ぶのではないでしょうか。

製造工程を大幅に修正する必要なく、ちょっと気の利いたことができるなら、それを実装することを“付加価値”とする意味は、あるのではないでしょうか。

タイトルを「条件を刈り込む」としましたが、「設計しましょう」をテーマにお送りしました。


追加:επιστημηさんとえムナウさんのコメントより。

Mのループについて考えます。このループは、Nで割りきれる数を探すものです。36で考えます。

36を素因数分解すると、36 ÷ 2 → 18 ÷ 2 → 9 ÷ 33 で、2, 2, 3, 3 です。ここから、36になるかけ算の組み合わせを列挙します。

2 2 × 3 × 3 2 × 18
3 2 × 2 × 3 3 × 12
2 × 2 3 × 3 4 × 9
2 × 3 2 × 3 6 × 6
3 × 3 2 × 2 9 × 4
2 × 2 × 3 3 12 × 3
2 × 3 × 3 2 18 × 2

こうすると、4行目、6 × 6 の上下で、かけ算の右と左の数字が入れ替わるだけということがわかります。かけ算は、順番が入れ替わっても答えは同じですから、この場合は4行目までの計算をすれば、全組み合わせを検査したことになります。

では、4行目をどのようにして求めましょう?素因数を見ると、左右が同じ数字です。つまり、Nの平方根(√N)です。

投稿日時 : 2007年3月19日 22:12
コメント
  • # re: 条件を刈り込む
    επιστημη
    Posted @ 2007/03/19 22:40
    2の倍数でも3の倍数でもない数は 6の倍数±1 なので素数候補を1/3に減らせて、割ってみる数も√N までで十分だから... なんてな。
  • # re: 条件を刈り込む
    THREE-ONE
    Posted @ 2007/03/20 0:03
    自身の半分以上の数字で割り切れることはありえないので(自分自身を除く)、内部のループは 3 ~ (N-1)/2 で十分ですね。
    ここが一番ムダじゃないかと
  • # re: 条件を刈り込む
    THREE-ONE
    Posted @ 2007/03/20 0:04
    以上じゃなかった、半分より上の数ですね
  • # re: 条件を刈り込む
    επιστημη
    Posted @ 2007/03/20 0:22
    ぃゃぃゃ、N/2より√Nの方が小っさいだ。
  • # re: 条件を刈り込む
    えムナウ
    Posted @ 2007/03/20 6:38
    突っ込もうとしたらエピさんがもう突っ込んでた。
    N=M*L としたら、
    MかLのどちらかは√N以下です。
  • # re: 条件を刈り込む
    とりこびと
    Posted @ 2007/03/20 8:50
    素数に関してはすでにアレなので。

    >タイトルを「条件を刈り込む」としましたが、「設計しましょう」をテーマにお送りしました。

    振り返ってみると、求められた実装についての「言葉」をそのままコードに落とし込んでいるところが多々あります。
    概ね実装はズレにくくなりますが、あとで改善依頼がくることも少なからず・・・(処理速度やら。)

    'ちょっと' 気の利いた感じが出したいと思う今日この頃です。
  • # re: 条件を刈り込む
    Jitta
    Posted @ 2007/03/21 0:27
    本題以外のところへのコメント、ありがとうございます(^-^;

    > 割ってみる数も√N までで十分
    はい、それは知っているのですが、説明ができないので『数学的に詳しい人なら、Mのループ回数をさらに減らせるでしょう。』としました。

    > N=M*L としたら、MかLのどちらかは√N以下です。
    えムナウさん、ありがとうございます。

     んで、書こうと思って書かなかったことを、やっぱり書いておこう。
    「やっぱり、勉強しておくことは必要です」
  • # re: 条件を刈り込む
    επιστημη
    Posted @ 2007/03/21 9:33
    ぁぅ、そいつぁ野暮なこといたしやした ^^;
タイトル
名前
Url
コメント