- unibon
- ぬし
- 会議室デビュー日: 2002/08/22
- 投稿数: 1532
- お住まい・勤務地: 美人谷 良回答(20pt)
|
投稿日時: 2007-05-12 17:31
引用: |
|
わちゃさんの書き込み (2007-05-12 16:27) より:
ただ、49円硬貨を付け足したときに、次のような結果になってしまいました。
コード: |
|
target = 1999999999
10000*199997 + 8000*3 + 5000*1 + 1000*0 + 500*1 + 100*4 + 50*0 + 49*2 + 10*0 + 5*0 + 1*1
|
なぜか、50円玉を使わずに、49円と、1円を使っちゃったようです。
|
ありがとうございます。求めた結果を保存する部分のコーディングレベルでバグっていました。(使い回しする変数(amounts)の管理がまずかったです。)
長いですが、修正済みのものを載せます。
(掲示板に「続きを読む...」みたいな機能があれば良いのですが。)
コード: |
|
import java.util.ArrayList;
import java.util.List;
public class Test {
/**
* 札・硬貨の種類 降順であること
*/
private static int[] prices = { 10000, 8000, 5000, 1000, 500, 100, 50, 49, 10, 5, 1 };
/**
* 枚数はこれ未満で足りる。
*/
private static int[] maxAmounts = new int[prices.length];
/**
* 小銭をかき集めて出来る金額。
*/
private static int[] maxCands = new int[prices.length];
/**
* 支払う金額
*/
private static int target = 1999999999;
/**
* ある時点で分かっている最小枚数
*/
private static int minMai = Integer.MAX_VALUE;
/**
* 最小枚数の結果(複数あることもあるのでリストになっている)
*/
private static List<int[]> results = new ArrayList<int[]>();
/**
* 最大公約数 10000 と 8000 ならば 2000
*
* @param a
* @param b
* @return
*/
private static int gcd(int a, int b) {
if (a < b) {
return gcd(b, a);
}
int m = a % b;
if (m == 0) {
return b;
} else {
return gcd(b, m);
}
}
/**
* 最小公倍数 10000 と 8000 ならば 40000
*
* @param a
* @param b
* @return
*/
private static int lcm(int a, int b) {
return (int) ((long) (a * b) / (long) gcd(a, b));
}
/**
* level 以下(等しいものは含む)の枚数を算出する。
*
* @param level
* @param amounts
* @return
*/
private static int calcMai(int level, int[] amounts) {
int mai = 0;
for (int i = 0; i <= level; i++) {
mai += amounts[i];
}
return mai;
}
/**
* level 以下(等しいものは含む)を計算する。
*
* @param level
* @param amounts
* @return
*/
private static int calcCand(int level, int[] amounts) {
int cand = 0;
for (int i = 0; i <= level; i++) {
cand += prices[i] * amounts[i];
}
return cand;
}
/**
* level 以下(等しいものは含む)を出力する。
*
* @param level
* @param amounts
*/
private static void output(int level, int[] amounts) {
String s = "";
for (int i = 0; i <= level; i++) {
if (i > 0) {
s += " + ";
}
s += prices[i] + "*" + amounts[i];
}
System.out.println(s);
}
/**
* level 以下(等しいものは含む)をコピーする。
*
* @param level
* @param amounts
* @return
*/
private static int[] copyAmounts(int level, int[] amounts) {
int[] preservedAmounts = new int[prices.length];
/* 使っている金種の枚数をコピーする。 */
for (int i = 0; i <= level; i++) {
preservedAmounts[i] = amounts[i];
}
/* 使っていない小さな金種はゼロクリアする。 */
for (int i = level + 1; i < prices.length; i++) {
preservedAmounts[i] = 0;
}
return preservedAmounts;
}
/**
* level 以下(等しいものは含む)の中間結果を保存する。 なお、既知の最小枚数より大きければ保存する必要はない。
*
* @param level
* @param amounts
*/
private static void preserve(int level, int[] amounts) {
int mai = calcMai(level, amounts);
if (minMai >= mai) {
if (minMai > mai) {
minMai = mai;
results.clear();
}
int[] preservedAmounts = copyAmounts(level, amounts);
results.add(preservedAmounts);
}
}
/**
* 金種ごとの最大必要枚数を求める。
*/
private static void initMaxAmounts() {
/* 要素の値が n の場合、a 枚 (a < n) まで使用すれば良い。 */
for (int i = 1; i < prices.length; i++) {
int minLcm = Integer.MAX_VALUE;
for (int j = 0; j < i; j++) {
int candLcm = lcm(prices[j], prices[i]);
if (minLcm > candLcm) {
minLcm = candLcm;
}
}
maxAmounts[i] = minLcm / prices[i];
}
/* 最大の金種は枚数に制限がない。 */
maxAmounts[0] = Integer.MAX_VALUE;
}
/**
* ある level 以下(等しいものは含む)のすべての金種を、最大必要枚数かき集めて作れる総額を、金種ごとに求める。
*/
private static void initMaxCands() {
int maxCand = 0;
for (int i = prices.length - 1; i > 0; i--) {
maxCand += prices[i] * (maxAmounts[i] - 1);
/* 累計した値を格納する。 */
maxCands[i] = maxCand;
}
/* 最大の金種は総額に制限がない。 */
maxCands[0] = Integer.MAX_VALUE;
}
/**
* 再帰メソッド
*
* @param level
* ループのネストレベル(0 = もっとも外側(10000円), prices.length - 1 = もっとも内側(1円)
* @param amounts
* 0 <= index <= level の要素は有効であるが、level < index < prices.length
* の要素は不定な値(ゴミ)として扱う。
*/
private static void loop(int level, int[] amounts) {
for (int i = 0; i < maxAmounts[level]; i++) {
amounts[level] = i;
int cand = calcCand(level, amounts);
if (cand >= target) {
if (cand == target) {
preserve(level, amounts);
}
break;
}
/* これよりさらに内側のループに入ることができるかを調べる。 */
if (level + 1 < prices.length) {
/* これより小さい金種まで計算しても無駄ではないかを調べる。 */
int diff = target - cand;
if (diff <= maxCands[level + 1]) {
/* 再帰呼び出しする。 */
loop(level + 1, amounts);
}
}
}
}
/**
* メイン
*
* @param args
*/
public static void main(String[] args) {
/* 金種がちゃんと降順になっているかをチェックする。 */
for (int i = 1; i < prices.length; i++) {
if (prices[i - 1] <= prices[i]) {
throw new IllegalStateException();
}
}
/* 金種ごとの最大必要枚数をあらかじめ求める。 */
initMaxAmounts();
/* 金種ごとに最大必要枚数を使って作れる金額をあらかじめ求める。 */
initMaxCands();
/* 最大必要枚数を表示する。 */
for (int i = 0; i < prices.length; i++) {
System.out.println("" + prices[i] + "円 (最大 (" + maxAmounts[i] + " - 1) 枚必要)");
}
/* 支払う総額を表示する。 */
System.out.println("target = " + target);
/* ループ変数(金種ごとの枚数) */
int[] amounts = new int[prices.length];
/* 再帰メソッドへ突入する。 */
loop(0, amounts);
/* 結果を表示する。 */
System.out.println("最小枚数 = " + minMai);
for (int[] result : results) {
output(result.length - 1, result);
}
/* 終わりのあいさつをする。 */
System.out.println("計算終了。");
}
}
|
以下は、出力結果です。
コード: |
|
10000円 (最大 (2147483647 - 1) 枚必要)
8000円 (最大 (5 - 1) 枚必要)
5000円 (最大 (2 - 1) 枚必要)
1000円 (最大 (5 - 1) 枚必要)
500円 (最大 (2 - 1) 枚必要)
100円 (最大 (5 - 1) 枚必要)
50円 (最大 (2 - 1) 枚必要)
49円 (最大 (50 - 1) 枚必要)
10円 (最大 (5 - 1) 枚必要)
5円 (最大 (2 - 1) 枚必要)
1円 (最大 (5 - 1) 枚必要)
target = 1999999999
最小枚数 = 200008
10000*199997 + 8000*3 + 5000*1 + 1000*0 + 500*1 + 100*4 + 50*1 + 49*1 + 10*0 + 5*0 + 1*0
10000*199999 + 8000*1 + 5000*0 + 1000*1 + 500*1 + 100*4 + 50*1 + 49*1 + 10*0 + 5*0 + 1*0
計算終了。
|
--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}
|
- ぼのぼの
- ぬし
- 会議室デビュー日: 2004/09/16
- 投稿数: 544
|
投稿日時: 2007-05-13 02:42
こんばんは。ぼのぼのです。
引用: |
|
ひろれいさんの書き込み (2007-05-12 09:26) より:
本スレのテーマは「お手本になるようなソースコード」ですよね?
Anonymous Coward さんは、そのテーマを元にソースを書かれたのでしょうか?
「少なくとも現状の要件は満たしてますし」とか「そこまで柔軟性を考慮しなくても良いと言えます」といった言葉に「逃げ」を感じてしまうのは、僕だけでしょうか・・・
|
少なくとも私は、「逃げ」とは感じませんでした。
なぜなら、Anonymous Coward さんが、
「TPOや本人の技量によって求められる理想解も変わってくる」
と書かれているからです。
現実的な状況を考えるなら、柔軟性よりも
「より実行速度の速いものをより少ない時間で作り上げる」
ということの方を優先的に求められる状況は、十分に存在し得ると思うのです。
引用: |
|
僕個人の感想としては、Anonymous Coward さんのソースを保守することになった場合、余程仕様書がしっかり書かれているかコメントが丁寧に書いてないと理解するのに時間がかかるかなぁ、と思いました。保守性の面で厳しいのかな、と。
あと、「他の方の書かれた柔軟性に富んだソースコードよりも実行速度が速いです」は実際に試されてのご意見でしょうか?
|
確かに保守性の面では厳しいですが、実行速度最優先なら、十分にお手本として成立すると思います。
実際に試すことは非常に重要ですが、今回の場合はあまりに明らかだと思います。
繰り返し処理を殆ど使ってませんから。
唯一繰り返し処理が使われているのはCountPaperとCountCoinsの再帰の部分ですが、
この部分の繰り返しの最大数は金種の配列長と同じですから、微々たるものです。
ちなみに、私なりに考えたVer.2の解。
他の方が既に書かれてることを複合しただけなので、ソースは書きません(←単なる横着)。
- 入力値を40000以上の部分と、硬貨部分と、残りに分ける。40000という数字は「全金種の最小公倍数」だが、金種配列が静的なので、わざわざ最小公倍数を動的に算出することはしない。ただし、定数にして関数の外側に出しておく。
- 40000以上の部分は、全て金種の最大値である10000を使用。
- 硬貨部分は、上位金種から順にカウントする方式でカウント。(ここまでは40000を定数にする以外はAnonymous Coward さんと同じ)
- 残りの部分は、最適化した総当り探索方式(←ずるい表現)でカウント。
で、このVer.2の解の、総当り探索に突入する前の絞込みの部分をVer.3へ応用できないかなと考えたのですが。
「全金種の最小公倍数以上の部分は分離させて最大金種でカウント」ってのはすぐに思いついたのですが、
硬貨部分を分離させている部分を応用できないかな、と考えてたら混乱してきました。
「金種を昇順に見た場合、n番目の金種までがある条件を満たしていれば、
入力金額のm未満の部分に関しては、上位金種から順にカウントする方式で唯一の正しい解に到達できる」
この「ある条件」が分かれば、
現実的な殆どの金種は最上位までこの条件に当てはまってくれたりするんではないか
と考えたのですが、頭が悪いせいかその条件が見つけ出せずにいます orz
1,5,10,50,100,500,1000,2000,5000,10000の場合は最上位金額までこの条件を満たし、
これに8000が加わるとmが1000になる…うーん(〜_〜
|
- わちゃ
- 大ベテラン
- 会議室デビュー日: 2005/12/05
- 投稿数: 162
- お住まい・勤務地: 東京
|
投稿日時: 2007-05-13 18:15
いやぁ、やっぱ、いろんな人の考え方って面白いですね。
いろいろやりつつ、他の人の結果と比べて、自分のコードのバグをいろいろ
探してました。
自分のコードだけでは、なかなか気づかないバグや、最適化の方法も、
他の人のをみながら、自分のプログラムを追って行くと、いろいろ考えて面白い。
さて、ひろれいさんのコードですが、{100,50,49,48,47,1} で、242 円の場合に、
100 + 47*3 + 1 になってしまったようでした。
( 100 + 48 + 47 * 2 が最小 )
また、unibon さんのは、おそらく結果は、正しいのだと思いますが、
{10000, 8000, 5000, 1000, 500, 100, 50, 49, 48, 47, 46, 45, 44, 43, 43, 42, 41, 10, 5, 1}
のような金種では、処理がかなり時間がかかるようでした。
で、私のいろいろやってみたコードです。
再帰というと、同じ引数で、呼び出される事が多くなりがちなので、
キャッシュで、同じ引数だったら、同じ結果を返すようにしてみました。
速度的には、かなりいいのですが、キャッシュの容量制限をしていないので、
場合によっては、メモリが爆発するかもしれません。
(いまのところ、キャッシュサイズは大きくても200〜300程度しか確認されていません)
コード: |
|
Private 金種リスト As Integer() = {100, 50, 49, 48, 47, 1}
'Private 金種リスト As Integer() = {10000, 8000, 5000, 1000, 500, 100, 50, 49, 48, 47, 46, 45, 44, 43, 43, 42, 41, 10, 5, 1}
'Dim 金種リスト As Integer() = {100, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 1}
'Dim 金種リスト As Integer() = {10000, 8000, 5000, 2000, 1000, 500, 100, 50, 10, 5, 1}
Private 呼び出し回数 As Integer = 0
Private キャッシュ As New Collections.Generic.Dictionary(Of Integer, Integer())
Public Function 枚数計算(ByVal 金額 As Integer, Optional ByVal 開始時最適回数 As Integer = Integer.MaxValue, Optional ByVal 開始金種 As Integer = 0) As Integer()
呼び出し回数 += 1
' まずは、キャッシュに計算済みの結果がないか調べる
If キャッシュ.ContainsKey(金額 * 金種リスト.Length + 開始金種) Then
Return DirectCast(キャッシュ.Item(金額 * 金種リスト.Length + 開始金種).Clone(), Integer())
End If
Dim 最適支払方法 As Integer() = New Integer(金種リスト.Length - 1) {}
Dim 最適枚数 As Integer = 開始時最適回数
Dim 今回金種金額 As Integer = 金種リスト(開始金種)
Dim 今回金種枚数 As Integer
If 金額 Mod 今回金種金額 = 0 Then
' ぴったりだったら、それで OK
最適支払方法(開始金種) = 金額 \\ 今回金種金額
Else
' ぴったりじゃなかったら、対象金種の多いものから、順に再帰で計算
For 今回金種枚数 = 金額 \\ 今回金種金額 To 0 Step -1
Dim 試行金額 As Integer = 金額 - 今回金種金額 * 今回金種枚数
' 次の金種で、自明に枚数が大きい場合は、ループ終了
If (試行金額 \\ 金種リスト(開始金種 + 1)) + 今回金種枚数 > 最適枚数 Then
Exit For
End If
' 再帰で、計算をしてみる。
Dim 試行結果 As Integer() = 枚数計算(試行金額, 最適枚数 - 今回金種枚数, 開始金種 + 1)
' 合計枚数を計算
Dim 試行結果枚数 As Integer = 0
Dim 枚数 As Integer
For Each 枚数 In 試行結果
試行結果枚数 += 枚数
Next
' 回数が少なかったら、代入( 0 だったら、解なしなので、それはとばす )
If 試行結果枚数 > 0 AndAlso 最適枚数 > (今回金種枚数 + 試行結果枚数) Then
最適支払方法 = 試行結果
最適支払方法(開始金種) = 今回金種枚数
最適枚数 = 今回金種枚数 + 試行結果枚数
End If
Next
End If
キャッシュ.Item(金額 * 金種リスト.Length + 開始金種) = DirectCast(最適支払方法.Clone, Integer())
Return 最適支払方法
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
'*** 初期化
呼び出し回数 = 0
キャッシュ.Clear()
Dim t As Integer = Environment.TickCount
'*** 計算
Dim 結果 As Integer() = 枚数計算(CInt(TextBox1.Text))
'*** 統計データ表示
Console.Write("回数=" & 呼び出し回数.ToString & " ")
Console.Write("キャッシュサイズ=" & キャッシュ.Count.ToString & " ")
Console.WriteLine("時間=" & (Environment.TickCount - t))
'*** 結果表示
Dim sb As New System.Text.StringBuilder
Dim i As Integer
sb.Append("( ")
For i = 0 To 結果.Length - 1
sb.Append(結果(i).ToString)
sb.Append(" "c)
Next
sb.Append(")")
Console.WriteLine(sb.ToString)
End Sub
|
さて、私自身は、最適化は好きなのですが、なんかの本に
書いてありました。
(出展を忘れてしまったので、内容も正確じゃないと思います)
引用: |
|
コードを書き終わって、パフォーマンスに若干の問題があった時に
することは、、
1.最適化しない。
2.最適化しない。
3.最適化しない。
|
いまどき、最適化なんて、保守性を下げるだけの場合の方が
多そうですからね。
|
- lalupin4
- 大ベテラン
- 会議室デビュー日: 2004/07/26
- 投稿数: 163
|
投稿日時: 2007-05-13 18:48
引用: |
| わちゃさんの書き込み (2007-05-13 18:15) より:引用: |
| コードを書き終わって、パフォーマンスに若干の問題があった時に
することは、、
1.最適化しない。
2.最適化しない。
3.最適化しない。
|
|
孫引きで恐縮ですがこんなんありました:
引用: |
| Jackson の最適化の法則
法則1:最適化しない
法則2:まだ最適化しない
―― 完全に明白で最適化を使用しないで解決策が見つかるまでは(上級者向け)
― M. A. Jackson
| McConnell, Stave
「コードの改良」
『Code Complete ―― 完全なプログラミングを目指して』第2版 下
2005年 日経BPソフトプレス p163
// ていうかどなたか↑の出典ご存じないですか?
[ メッセージ編集済み 編集者: lalupin4 編集日時 2007-05-13 18:49 ]
|
- わちゃ
- 大ベテラン
- 会議室デビュー日: 2005/12/05
- 投稿数: 162
- お住まい・勤務地: 東京
|
投稿日時: 2007-05-13 19:21
引用: |
| lalupin4さんの書き込み (2007-05-13 18:48) より:
引用: |
| Jackson の最適化の法則
法則1:最適化しない
法則2:まだ最適化しない
―― 完全に明白で最適化を使用しないで解決策が見つかるまでは(上級者向け)
― M. A. Jackson
| McConnell, Stave
「コードの改良」
『Code Complete ―― 完全なプログラミングを目指して』第2版 下
2005年 日経BPソフトプレス p163
|
あ、たぶん、私が見たのもそれです。
手元にある Code Complete 初版にのってたかなぁと思って探したのですが、
見つけられませんでした、、、orz
|
- unibon
- ぬし
- 会議室デビュー日: 2002/08/22
- 投稿数: 1532
- お住まい・勤務地: 美人谷 良回答(20pt)
|
投稿日時: 2007-05-13 22:31
引用: |
|
わちゃさんの書き込み (2007-05-13 18:15) より:
また、unibon さんのは、おそらく結果は、正しいのだと思いますが、
{10000, 8000, 5000, 1000, 500, 100, 50, 49, 48, 47, 46, 45, 44, 43, 43, 42, 41, 10, 5, 1}
のような金種では、処理がかなり時間がかかるようでした。
|
たしかにそうですね。私のは、素数っぽいのはまったく苦手です。(なお上記では 43 がダブっています。)
1万円の支払いも計算が終わらなくなってしまいます。
引用: |
|
わちゃさんの書き込み (2007-05-13 18:15) より:
で、私のいろいろやってみたコードです。
|
これ、短いコードなのにものすごく速いですよね。どういう仕組みなんでしょう。
なお、バグ報告になりますが、金種リストが {30, 10, 7} の時に 135 を計算しようとすると IndexOutOfRangeException が出るようです。(ちなみにこの解は 30*3 + 10*1 + 7*5 です。)
#以下、あとで追加。
それとも、思ったのですが、最小の金種が1円であることが必要、という仕様なのでしょうか?7円玉が最小だと、実生活で困りますし。切手やクレジットカードのポイントなら最小が1より大きいということはありそうですが。
#さらに、あとで追加。
金種計算の際に、金種の最小が1円かどうかで、自由度が大きく違ってくるような気がします。1円玉が存在することにより、総額の微調整が格段にしやすくなりそうな感じですので。このあたりはアルゴリズムの検討の際に大きく関わってくることなのでしょうか。気になります。
--
unibon {B73D0144-CD2A-11DA-8E06-0050DA15BC86}
[ メッセージ編集済み 編集者: unibon 編集日時 2007-05-13 22:37 ]
[ メッセージ編集済み 編集者: unibon 編集日時 2007-05-13 22:48 ]
|
- Anonymous Coward
- 会議室デビュー日: 2007/05/09
- 投稿数: 8
|
投稿日時: 2007-05-13 22:35
こんばんは。スレ主です。
引用: |
|
ひろれいさんの書き込み (2007-05-12 09:26) より:
ちょっと気になったので書かせて下さい。
本スレのテーマは「お手本になるようなソースコード」ですよね?
Anonymous Coward さんは、そのテーマを元にソースを書かれたのでしょうか?
「少なくとも現状の要件は満たしてますし」とか「そこまで柔軟性を考慮しなくても良いと言えます」といった言葉に「逃げ」を感じてしまうのは、僕だけでしょうか・・・
確かに、金種なんてそうそう変わるものではありませんが、2,000円札の廃止なんて結構現実的だと思ってみたり
僕個人の感想としては、Anonymous Coward さんのソースを保守することになった場合、余程仕様書がしっかり書かれているかコメントが丁寧に書いてないと理解するのに時間がかかるかなぁ、と思いました。保守性の面で厳しいのかな、と。
|
ご指摘ありがとうございます。
コメントはなるべく丁寧に書いたつもりですが、仰る通り、保守性の面では厳しいものがあると思います。
しかし、ぼのぼのさんも仰っている通り、私の投稿しましたソースコードは、
柔軟性や保守性を犠牲にして、実行速度を優先したものです。
また、このソースコードは生産性の面も考慮したつもりでした。
事実として、私の能力では、現実的な使用に耐えうる速度を確保しつつ、総当り方式でのコードは、
他の方の助けなしては一日では到底書き上げることができませんが、
手作業方式のソースコードは、私のレベルでもなんとか一日で書き上げることができました。
しかし、この「手作業で見つけ出した解をハードコーディングする」という方法は、
アプローチの仕方としては参考になるかもしれませんが、
結果としてできあがったコードがお手本になるかと言えば、違うのかもしれませんね。
引用: |
|
あと、「他の方の書かれた柔軟性に富んだソースコードよりも実行速度が速いです」は実際に試されてのご意見でしょうか?
|
重要なことを書かなくて申し訳ありません。
他の方の書かれた全てのソースコードを試したわけではなかったのですが、
unibonさんが 2007-05-11 12:46 に書かれたソースコードは、C#に直して試しました。
#Javaは全然詳しくなかったのですが、数箇所直しただけでコンパイルを通ったのでちょっと驚きでした。
試した結果ですが、小さな数では殆ど速度差は感じられなかったものの、
入力値を大きくしていくと39000くらいでも体感ではっきり分かるくらいの差がありました。
999999などの大きな数では完全に明らかでした。
実はunibonさんのソースコードを試しましたのは、実行速度の比較の他に、
自分の書いたソースコードの正当性を検証する目的もあり、出力結果を比較したところ、
手作業での調査に抜けがあり、自分のソースコードのバグを発見してしまったのも事実です。
図らずも、バグを作り込みやすい方法であることも証明してしまったのでした(苦笑)。
投稿しましたのは、バグ修正後のものです。
しかし、Ver.3に関しては、もはや私にとっては息抜きにならないです(汗)。
なにしろ、本職の仕事の方より難易度が高いものですから(苦笑)。
でも、他の皆さんの書き込みを拝見しているだけで本当に勉強になっています。
ですので、このお題に関する議論が完全に落ち着くまでは、私は参戦できないかもしれませんが、
少なくとも私の方からお題を変えるようなことはしないようにしようと思っております。
|
- ぼのぼの
- ぬし
- 会議室デビュー日: 2004/09/16
- 投稿数: 544
|
投稿日時: 2007-05-13 22:56
引用: |
|
unibonさんの書き込み (2007-05-13 22:31) より:
それとも、思ったのですが、最小の金種が1円であることが必要、という仕様なのでしょうか?7円玉が最小だと、実生活で困りますし。切手やクレジットカードのポイントなら最小が1より大きいということはありそうですが。
|
「最小の金種が1円であることは仕様上の大前提か」は、私も考えました。
これが否になると、当然「この入力値は現在の金種では表現できません」てパターンがでてきて、
問題がますます複雑化しそうですね(〜_〜
しかし、私はずっとクレジットカードのポイントではなく通貨の方が頭にあったので、
最小が1でなければそもそも「1円」という金額が表現できない時点でNGだろ、と勝手に決め付けてましたw
|