- - PR -
お手本になるようなソースコード
投稿者 | 投稿内容 | ||||||||
---|---|---|---|---|---|---|---|---|---|
|
投稿日時: 2007-05-11 23:44
>カーニーさん
ああっ、早くも新たなお題が…と思ったら、 Ver2を現実的なシチュエーションに書き換えてくださったのですね。 ありがとうございます。 私も考えてみました。 皆さんとは違うアプローチで、柔軟性より速度重視です。 #というと聞こえはいいですが、手作業を介入させた泥くさいやり方です。 まず、紙幣を10000,8000,5000,2000,1000に限定して考えれば、 残額が40000未満になるまでは、万券を使うのが最も枚数が少なくなるので、 入力値を40000以上の部分、硬貨部分、残りの3つに分けました。 すると残りの部分は39000以下の1000の倍数となります。 次に、あまり頭の良い方法ではないのかもしれませんが、 手作業で1000〜39000まで総当たりで枚数最小パターンを調べてみました。 すると、パターンには以下の4種類あることがわかりました。 (1) 解が1つで、かつそれがVer1と同じロジックにて求められる(例:35000) (2) 解が1つで、かつそれはVer1と同じロジックでは求められない(例:34000) (3) 解が複数あり、その全てが1枚以上の万券を含む(例:31000) (4) 解が複数あり、かつそのいずれかが万券を1枚も含まない(例:37000) しかし、それぞれのパターンが現れる数字の間に法則性は見出せなかったので、 switchで分岐させてベタ書きすることにしました。 ただし、(3)と万券を含む(2)はあらかじめ万券分を差し引いて分岐の数を減らしてます。 柔軟性は全然ありませんが、ループで候補を探索するプログラムより速度は速いです。 以下がC#のコードです。
余談ですが、このソースコードは柔軟性もないですし、結果の一部をベタ書きしてるため、 手作業での調査不足やタイプミスによるバグも作り込み易いです。 しかし、少なくとも現状の要件は満たしてますし、 他の方の書かれた柔軟性に富んだソースコードよりも実行速度が速いです。 また、クレジットカードのポイントとなると話は別ですが、 紙幣に関しては現実的にはそんな簡単に新たな金種が追加されることはなく、 そこまで柔軟性を考慮しなくても良いと言えます。 先日の書き込みで、私が 「拡張性、可読性、実行速度、生産性などのバランスを考慮すると奥が深い」 「自分が新人の頃には気づかなかった再発見がある」 と書きましたのは、業務プログラマとしての経験を通して、 必ずしも最もスマートなアルゴリズムだけが求められるものではなく、 TPOや本人の技量によって求められる理想解も変わってくる、という意味も含んでいました。 従って、このスレでは単にスマートなアルゴリズムだけを追求するのではなく、 そういった現実的なTPOも加味して、「この状況/前提ならこれで十分だよね」 ですとか、「このロジックは確かにすごいけど実行速度の面で現実的ではないよね」 みたいな議論も交わして頂けると、情報交換の場としてより有用なのかな、と思います。 | ||||||||
|
投稿日時: 2007-05-12 09:26
ちょっと気になったので書かせて下さい。 本スレのテーマは「お手本になるようなソースコード」ですよね? Anonymous Coward さんは、そのテーマを元にソースを書かれたのでしょうか? 「少なくとも現状の要件は満たしてますし」とか「そこまで柔軟性を考慮しなくても良いと言えます」といった言葉に「逃げ」を感じてしまうのは、僕だけでしょうか・・・ 確かに、金種なんてそうそう変わるものではありませんが、2,000円札の廃止なんて結構現実的だと思ってみたり(^_^;) 僕個人の感想としては、Anonymous Coward さんのソースを保守することになった場合、余程仕様書がしっかり書かれているかコメントが丁寧に書いてないと理解するのに時間がかかるかなぁ、と思いました。保守性の面で厳しいのかな、と。 あと、「他の方の書かれた柔軟性に富んだソースコードよりも実行速度が速いです」は実際に試されてのご意見でしょうか? せっかくのいい流れに水を差すようでごめんなさいm(_ _)m じゃあ言うなよ!って?w | ||||||||
|
投稿日時: 2007-05-12 09:48
すいません。Ver.2 の金種対応しか考えていませんでしたから(^_^;) じゃあ!ってことで、バージョンアップしてみましたw
わちゃさんのおっしゃる通り、ここまで来るとやっぱり総当りチェックしかないんでしょうかねぇ | ||||||||
|
投稿日時: 2007-05-12 11:11
そうかもしれません。 ならAnonymous Cowardさんも言われている方法で、探索範囲を狭めてしまえば良さそうです。 ・最初に最大金額のもの(10000円)と2番目のもの(8000円)との最小公倍数(40000円)を確保できるだけ確保してしまって、残額(40000円未満)についてのみ探索を行う。 と改変すれば大丈夫でしょう、たぶん。 | ||||||||
|
投稿日時: 2007-05-12 11:38
複雑な問題は、ほんと、どこかにパターンをみつけて、最適化をしないと
ダメかもしれないですよね。 私も、自分のプログラムは、あまりに遅いので、最適化を試みています。 10倍ぐらい早くなったのですが、前回52万回の再帰呼び出しが、3万ぐらいには 減ったのですが、それでも速いとは言い難いです。 そういう意味では、Anonymous Coward さんのようなアプローチが、実際に使う 場合にはいいのかもしれません。 むしろ、わりきって、40000 で割った余りの、千の位と、一万の位の40パターンで、 結果のテーブルをハードコーディングするぐらいに割り切ってもいいのかも。 ひろれいさんの、コードは、241円を支払い場合に、 100円 + 47円 * 3 が最適解ですが、うまくいかないみたいです。 びしばしさんのも、Ver.2( 8000円つき ) では、よいかと思いますが、 Ver.3( 任意貨幣 )では、うまく動かないかと思います。 # {100, 50, 49, 48, 47, 1} で、141 円や、241 円をやるとわかるかと思います。 | ||||||||
|
投稿日時: 2007-05-12 13:41
「お題 Ver.2」対応版です(要求仕様はあくまでも総枚数を減らすことに特化しています)。
前回投稿したのを改良しました。概要はつぎのとおりです。 ・金種の種類数の分だけ多段ループをするのは前回と同様ですが、多段ループをシンプルに表現するために再帰を使うようにしました。ネストレベルは金種の種類数までです。 ・多段ループで無駄に内側のループをやらないようにするために、早めに打ち切れるケースを見つけて打ち切るようにしました。1円玉を5枚以上使う必要はない、とか、8000円札は5枚以上使う必要はない、という上限値を、事前に最小公倍数を使って厳密に求めています。 常識的な金種ならば、20億円でも高速に動きましたので、実使用では問題ないと思います。金種が大きな素数だと速度面ではダメですが、計算結果はどんな金種でも正確だと思っています。 もっとも、ケアレスミス的なバグは残っているかもしれません。
これを動かした出力結果も載せておきます。
-- unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86} | ||||||||
|
投稿日時: 2007-05-12 15:07
#仕事しなさい!>俺w
Ver.3 のバグ対応が完了しました。が、正直自信ありません。ごめんなさい m(_ _)m ここ(Ver.3)まで来ると、みなさんが仰っているように何かしらの条件をソース上に加える必要性を感じてしまいます。 僕の改良版でも 7桁(9,999,999)まではそこそこの回答速度ですが、8桁(99,999,999)になると返ってきません(だから、試さないで下さいw)。 単純ループだと、もう限界だと思います。実用性からは、ほど遠いですね・・・orz ダメダメじゃん>俺 Anonymous Coward さん、ごめんなさい。 Anonymous Coward の考え方の方が正しいのかもしれません。
| ||||||||
|
投稿日時: 2007-05-12 16:27
unibon さんの、最小公倍数、最大公約数を使うのは、
自分もアイデアとしては、あっても、具体的にアルゴリズムにできそうな 気がしなかったので、すげ〜と思いました。 ただ、49円硬貨を付け足したときに、次のような結果になってしまいました。
なぜか、50円玉を使わずに、49円と、1円を使っちゃったようです。 ひろれいさん、お仕事お疲れ様です。 私は、普通に休みです。 ひろれいさんの、新しいのは、まだ見てませんが、個人的には単純なループでは、 難しいと思っているので、もうちょっと見てみたいと思っています。 自分のも、再帰が多かったのですが、キャッシュの考え方を使ったら、 予想外に早くなりました。 変なパターンがないか、もうちょっと検証したいのですが、 そろそろ、でかけますので、また後日にでも、、、 |