[ 初めての方へ | 一覧(最新更新順) | 全文検索 | 過去ログ ]
『レシートの金額合計を8万円にしたい』(kiyoko)Windows Vista Excel2007
買い物を行ったレシートがあります。 レシートの合計金額8万円以上で商品券1枚と引き換えができます。
シートの枚数を0〜100枚(入力セル:A1〜A100)の間として、この合計金額を8万円〜8万10円の金額におさめたいと思っています。 A1〜A100の金額全てを使用しても、一部分を使用しても構いませんので、8万円〜8万10円の金額におさめたいと思っています。 もちろん合計金額8万円ちょうどのほうがお得ですので、8万>8万1円>8万2円>...>8万10円の優先順位で結果を出したいと考えています。 結果は8万円に一番近い金額結果のみを表示したいです。 この結果の表示はA1〜A100以外のセルであればどこでも構いません。 その結果と同時に、A1〜A100セルの「8万円に一番近い金額結果」の計算に使用したセルの文字を赤色に表示させたいです。 また、8万円〜8万10円の間の組み合わせが存在しないときは、「8万円〜8万10円の組み合わせは存在しません」と表示させたいです。
どなたかご教授お願い致します。
これは、機械的にやろうと思っても無理でしょうね。 100枚のレシートをしらみつぶしに調べようとしたら、 その組み合わせ総数はとんでもない数字ですから。
30枚なら約10億通りで、このくらいならマクロで出来るでしょうけど。
それとも、100枚の内、何枚以上の組み合わせは 調べなくてもいいというような条件があるのでしょうか? (それでも10枚制限で194億通り) (純丸)(o^-')b
レシートの金額の範囲は? 最小 \200 〜 最大 \5,000 等。 (NB)
Private Sub CommandButton1_Click()
Dim X As Integer, Z As Integer, C As Integer, A As Integer
For A = 1 To 100 '入力されているデータの最後のセルを調べる If Sheet1.Cells(1, A) = "" Then A = A - 1 Exit For End If Next A Z = 0 For X = 1 To A '合計を計算 Z = Sheet1.Cells(1, X) + Z Cells(1, X).Select '文字色を”赤”に変更 With Selection.Font .Color = -16776961 .TintAndShade = 0 End With Select Case Z '80000〜80010までを調べ、合計を表示 Case Z = 80000 Sheet1.Cells(2, 1) = Z Case Z = 80001 Sheet1.Cells(2, 1) = Z Case Z = 80002 Sheet1.Cells(2, 1) = Z Case Z = 80003 Sheet1.Cells(2, 1) = Z ' 'z=80010まで書く。 めんどいので自分で同じように書いてね! ' ' End Select Next X
If Z < 80000 Then '表示と文字色を戻す MsgBox " 「8万円〜8万10円の組み合わせは存在しません」" For X = 1 To A Cells(1, X).Select With Selection.Font .ColorIndex = xlAutomatic .TintAndShade = 0 End With Next X End If If Z > 80000 Then '表示と文字色を戻す MsgBox " 「8万円〜8万10円の組み合わせは存在しません」" For X = 1 To A Cells(1, X).Select With Selection.Font .ColorIndex = xlAutomatic .TintAndShade = 0 End With Next X End If
End Sub
この例では、毎日1〜2枚のレシートをスーパーで買い物をして持ち帰り
その金額をセルに記入していったものを想定しています。
またコマンドボタンをシート上に配置しています。
(犬のしっぽ)
個人用なので、セル位置とかはA1〜A100にしていません アンド
8万円〜8万10円との事でしたが、設定した金額に一番近いものを出すロジックです。
レシートのリストをD5,D6,D7... のように
設定金額(80000)をB5セルに設定後、下記マクロを動かすボタンでも配置して下さい。
'金額選択マクロ
Dim moneys() As Long '金額リスト
Dim moneySet As Long '設定金額
Dim moneySelect As Long '選択金額合計
Dim moneyTotal As Long '合計金額
Dim money As Long '金額
Dim count As Long '金額リスト数
Dim seq As Long '選択対象となったパターン番号
Dim StartLine As Long
Private Sub CommandButton1_Click()
'金額設定開始行数 StartLine = 5
'設定金額 moneySet = Cells(StartLine, 2).Value
moneyTotal = 0 count = 0
'金額取込 Do While count < 65536 money = Cells(count + StartLine, 4).Value If money = 0 Then Exit Do End If count = count + 1 ReDim Preserve moneys(count) moneys(count) = money moneyTotal = moneyTotal + money Loop
moneySelect = moneyTotal
'金額算出 For i = 1 To 2 ^ count money = 0 For j = 1 To count flg = Application.WorksheetFunction.RoundUp(i / (2 ^ (j - 1)), 0) Mod 2 If flg = 0 Then money = money + moneys(j) End If Next j '最小値か? If (moneySet <= money) And (money < moneySelect) Then moneySelect = money seq = i '設定金額そのものなら終了 If moneySet = money Then Exit For End If End If Next i
'出力 For k = 1 To count flg = Application.WorksheetFunction.RoundUp(seq / (2 ^ (k - 1)), 0) Mod 2 If flg = 0 Then Cells(k + StartLine - 1, 4).Interior.ColorIndex = 3 Else Cells(k + StartLine - 1, 4).Interior.ColorIndex = xlColorIndexNone End If Next k Cells(StartLine + 2, 2).Value = "選択金額合計" Cells(StartLine + 3, 2).Value = moneySelect
End Sub
(茜たん)
以前に作成した組合せリスト作成サブルーチンで簡単に出来るかなあ と思い、 あれこれ試していたら、既作プログラムに、バグがあることを発見できました。
バグが直せたということで久しぶりにコードの投稿です。
新規ブックにて、
標準モジュール(Module1)に組合せリスト作成プログラム
'========================================================================= Option Explicit Private c_svsn As Long '抜き取り数保存 Private c_svsmpn As Long '標本数保存 Private c_idx() As Long '配列のカレントポインタ Private cs_x() As Long '配列の基盤ポインタ Private c_eof As Boolean '========================================================================= Function init_comb(smpnum As Long, seln As Long) As Double '組合せ処理ルーチンの初期化 'Input : smpnum 標本数 seln 抜き取り数 'output : init_comb 組合せ数 Dim g0 As Long c_svsn = seln c_svsmpn = smpnum Erase c_idx() Erase cs_x() g0 = 1 ReDim cs_x(1 To seln) ReDim c_idx(1 To seln) For g0 = 1 To UBound(c_idx()) cs_x(g0) = g0 c_idx(g0) = g0 Next c_idx(UBound(c_idx())) = c_idx(UBound(c_idx())) - 1 init_comb = WorksheetFunction.Combin(smpnum, seln) c_eof = False End Function '========================================================================= Function get_comb(ans()) As Long '組合せリストのインデックスを配列として返す 'input : なし 'output: ans() 組合せリストのインデックスを格納する ' get_comb 0:正常に取得 1 リストの終わり Dim g0 As Long Dim g1 As Long get_comb = 1 If c_eof Then Exit Function For g0 = UBound(c_idx()) To LBound(c_idx()) Step -1 If c_idx(g0) + 1 <= c_svsmpn - c_svsn + g0 Then c_idx(g0) = c_idx(g0) + 1 get_comb = 0 Exit For Else c_idx(g0) = cs_x(g0) + 1 cs_x(g0) = cs_x(g0) + 1 For g1 = g0 + 1 To UBound(cs_x()) cs_x(g1) = cs_x(g1 - 1) + 1 c_idx(g1) = cs_x(g1) Next g1 End If Next If get_comb = 0 Then For g0 = LBound(c_idx()) To UBound(c_idx()) ans(g0) = c_idx(g0) Next Else c_eof = True End If End Function '========================================================================= Sub close_comb() '組合せ処理ルーチンの終了処理 Erase c_idx() Erase cs_x() End Sub '========================================================================= Function skip_comb(i_num As Long, ans() As Variant) As Long '指定したインデックスを一つ増加させる 'input: i_num:増加させるインデックス 'output: ans()組合せリストのインデックスを格納する ' skip_comb 0:正常に取得 1 リストの終わり '組合せリストのインデックスを配列として返す 'input : なし 'output: ans() 組合せリストのインデックスを格納する ' get_comb 0:正常に取得 1 リストの終わり Dim g0 As Long Dim g1 As Long skip_comb = 1 g1 = c_svsmpn If c_eof Then Exit Function For g0 = UBound(c_idx()) To i_num + 1 Step -1 c_idx(g0) = g1 g1 = g1 - 1 Next skip_comb = get_comb(ans()) End Function
別の標準のジュール(Module2)に上記のコードを呼び出して、レシートの計算をする メインプログラム等及び、サンプルデータ作成プログラム
'========================================================== Sub main1() Dim Tdata As Variant Dim rng As Range Dim snum As Long Dim nuki As Long Dim 解 As Long Dim g0 As Long Dim g1 As Long Dim g2 As Long Dim ret As Long Dim asum As Long Dim tm As Double Dim Mret As Long Dim bestsum As Long Dim inttm As Variant With Columns(3) .ClearContents .ColumnWidth = 32 .Interior.ColorIndex = xlNone End With 解 = 80000 解 = Application.InputBox("目標値を入力してください", , 解, , , , , 1) If 解 = 0 Then 解 = 80000 End If Call mk_sample inttm = Application.InputBox("確認インターバルをhh:mm:ss形式で指定してください", , "00:05:00", , , , , 2) If TypeName(inttm) = "Boolean" Then inttm = "00:05:00" End If tm = [now()] Mret = vbOK Range("a:b").Interior.ColorIndex = xlNone Set rng = Range("a1", Cells(Rows.count, "a").End(xlUp)).Resize(, 2) rng.Sort Key1:=Range("b1"), Order1:=xlDescending, Header:=xlNo Set rng = rng.Columns(2) Tdata = Application.Transpose(rng.Value) snum = UBound(Tdata) asum = 0 For g0 = UBound(Tdata) To LBound(Tdata) Step -1 asum = asum + Tdata(g0) If asum > 解 + 10 Then snum = UBound(Tdata) - g0 Exit For End If Next bestsum = 解 + 100 For g0 = 1 To snum Call init_comb(UBound(Tdata), g0) ReDim ans(1 To g0) ret = get_comb(ans()) Do While ret = 0 asum = 0 For g1 = LBound(ans()) To UBound(ans()) asum = asum + Tdata(ans(g1)) If asum > 解 + 10 Then Exit For Next nuki = g1 If nuki >= UBound(ans()) Then If asum < 解 Then nuki = chk_seq(ans()) Debug.Print nuki & " :" & asum If nuki > 0 Then ret = skip_comb(nuki, ans()) Else Exit Do End If Else If asum >= 解 And asum <= 解 + 10 Then If asum < bestsum Then Range("a:c").Interior.ColorIndex = xlNone For g1 = LBound(ans()) To UBound(ans()) rng.Cells(ans(g1)).Offset(0, -1).Resize(, 2).Interior.ColorIndex = 4 Next Cells(g2 + 1, 3).Interior.ColorIndex = 4 bestsum = asum End If Cells(g2 + 1, 3).Value = UBound(ans()) & "件 合計 : " & asum g2 = g2 + 1 tm = [now()] If asum = 解 Then Mret = vbCancel Exit Do End If End If ret = get_comb(ans()) End If
Else ret = skip_comb(nuki, ans()) End If If [now()] - tm > TimeValue(inttm) Then Mret = MsgBox("抜き出し " & g0 & "件 実行中 続けますか?", vbOKCancel) tm = [now()] If Mret = vbCancel Then Exit Do End If Loop Call close_comb If Mret = vbCancel Then Exit For Next Set rng = Range("a1", Cells(Rows.count, "a").End(xlUp)).Resize(, 2) rng.Sort Key1:=Range("a1"), Order1:=xlAscending, Header:=xlNo Set rng = Nothing Erase ans() Erase Tdata End Sub '======================================================= Sub mk_sample() With Range("a1:b100") .Formula = Array("=row()", "=int(rand()*5000)+1") .Value = .Value .Interior.ColorIndex = xlNone End With End Sub '======================================================== Function chk_seq(ans()) As Long chk_seq = 0 Dim g0 As Long For g0 = UBound(ans()) To LBound(ans()) + 1 Step -1 If ans(g0) <> ans(g0 - 1) + 1 Then chk_seq = g0 - 1 Exit For End If Next End Function
まっさらなシートをアクティブにしてmain1を実行してみてください。
まず、目標値を入力します。規定値で80000と入力されています。そのままでよければ、 OKボタンです。
次にサンプルデータがa1:b100に作成されます。
A列は、連番、B列にランダムな数値、すなわち、レシートの金額になります。
サンプルデータに習って実際には、データを作成してください。
御自分でデータを作成するときは、main1内の Call mk_sample は、コメント化すること。
次に 確認メッセージを出す間隔をhh:mm:ss形式で入力します。
規定値で 00:05:00 つまり、5分が設定されています。このままでよければ、OKボタンをクリックです。
これで検索開始です。
C列に 合計値とレシート枚数が表示されます。 目標値に最も近いリストに色が付けられます。
目標値〜目標値+10が見つからず、インタ-バル時間(上記の例だと5分)を過ぎると、続行・中止を選択するメッセージは 表示されますから、処理が長い場合、オペレータが判断してください。
目標値ぴったりの値とリストが見つかれば、その時点で処理は、終わりになります。
ichinose
プログラムの訂正投稿tです
[ 一覧(最新更新順) ]
YukiWiki 1.6.7 Copyright (C) 2000,2001 by Hiroshi Yuki.
Modified by kazu.