#4~#8です。
--------------------------------------------
■1.コメントを数字の隣に表記したときに、検索結果として一緒に出力
>結果を見たときに何の金額なのかわかっていいな~
ん~、#8の「元リストと同位置に書出し」オプションは
そういう趣旨でつけてみたんですが…。
「結果を2列セットで吐く」というのは、技術的には難しくはありませんが、
「取り回しが悪い」というか「データとして汚い」感じがするので
趣味とか美意識(笑)の問題として、私としては対応しかねます^^;;
とりあえず、C列を「コメント」列に使うものとして、
列使用設定をずらし、B7セルで「表示値形式」を選べるようにしました。
・0または空白 : 使用する要素の数値を表示
・1 : 使用する要素のコメントを表示
・2 : 数値とコメントを単一の文字列として表示
・3 : "●"を表示(「書出形式」を「元リストと同位置」にする前提)
なお、要望4・5に対応する関係もあって「書出形式」オプションを
・0または空白 : 上詰
・1 : 元リストと同位置
に変更しました。
--------------------------------------------
■2.処理途中で「ESC」で、抜けたときも、そこまでの結果を表示できないか。
ESCキーで中断した際にそれまでの結果を表示するようにしました。
※ESCを押した時点で確認を求めず直ちに中止します(続行不可)
--------------------------------------------
■3.組み合わせ個数の少ないものから順番に表記できないか。
まず「個数の少ないものから順に探索する」のは【原理的に困難】です。
次に「個数の少ないものから順に出力する」のは【仕様上困難】です。
※全ての結果を得る(全件探索する)ことが前提なら、
最後に個数を調べて(列方向に)ソートすれば済む話ですが、
全ての結果を得ていない(探索を途中で打ち切った)場合は、
探索しなかった部分について個数を知ることができません。
というわけで、
「個数を指定した探索を、繰り返し(連続して)行う」
という方式にしました。
B4セルが
0または空白 : 一括探索
1 : 個数の少ない順に連続探索
2 : 個数の多い順に連続探索
--------------------------------------------
■4.リストにある金額をあらかじめ指定できないか
■5.検索に使うB列のリスト範囲を指定
「どのように指定するか」いろいろ方法はありますが、
とりあえず、E列を「指定列」として、
・E列が"○"の要素 : 必ず使用する
・E列が"×"の要素 : 使用しない
という仕様にしました。
要素「2」の行のE列を"○"とすれば、「2」を含む組を探索します。
11行目以降のE列を"×"とすれば、1行目~10行目の要素のみを使う組を探索します。
なお、上限・下限設定は「必ず使用する」要素の個数を含めて設定してください。
-------------------------------------------------------------------
仕様まとめ
・A列:設定項目見出列
・B列:設定項目入力列
・C列:リスト見出(コメント)入力列
・D列:リスト入力列
・E列:使用/不使用要素指定列
・F列以降:結果表示列
・B1 : 合計
・B2 : 使用個数の上限
・B3 : 使用個数の下限
・B4 : 使用個数の最少/最多から連続探索(0/1/2)
・B5 : 探索件数の上限
・B6 : 書出形式(0/1)
・B7 : 表示値形式(0/1/2/3)
'----------------------↓ ココカラ ↓----------------------
Private Const optCln As Long = 2 'B列:設定列の列番号
Private Const cmtCln As Long = 3 'C列:コメント列の列番号
Private Const itmCln As Long = 4 'D列:リスト列の列番号
Private Const spcCln As Long = 5 'E列:使用指定列の列番号
Private Const rtnCln As Long = 6 'F列:結果表示範囲最左列の列番号
Private itmCnt As Long '候補リスト個数
Private itmAry() As Long '候補リスト配列
Private rtnCnt As Long '結果個数
Private rtnAry() As Boolean '結果配列
Private sumAry() As Long '後方和配列
Private tmpAry() As Boolean '経過配列
Private rtnLmt As Long '探索件数上限
'------------↓↑ ツヅク ↑↓------------
Sub Sample()
Dim i As Long, j As Long, k As Long
Dim t As Variant 'タイムカウンタ
Dim orgCnt As Long '元リスト個数
Dim orgAry() As Variant '表示値配列
Dim sumOpt As Long '初期合計
Dim uprOpt As Long '上限設定
Dim lwrOpt As Long '下限設定
Dim seqOpt As Long '連続探索設定
Dim rtnOpt As Long '探索件数設定
Dim dspOpt As Long '書出形式設定
Dim valOpt As Long '表示値形式設定
Dim spcCnt As Long '必須要素個数
Dim spcSum As Long '必須要素合計
Dim cntMax As Long '最多個数
Dim cntMin As Long '最少個数
Dim tmpSwp As Long '入替退避用
Dim dspAry As Variant '書出データ
Dim dspLmt As Long '書出件数上限
Dim rvsFlg As Boolean '反転フラグ
t = Timer
Range(Columns(rtnCln), Columns(Columns.Count)).Clear
'設定取得
sumOpt = Cells(1, optCln).Value
uprOpt = Cells(2, optCln).Value
lwrOpt = Cells(3, optCln).Value
seqOpt = Cells(4, optCln).Value
rtnOpt = Cells(5, optCln).Value
dspOpt = Cells(6, optCln).Value
valOpt = Cells(7, optCln).Value
Application.ScreenUpdating = False
orgCnt = Cells(Rows.Count, itmCln).End(xlUp).Row
Erase orgAry
ReDim orgAry(1 To orgCnt, 0 To 1)
For i = 1 To orgCnt
'表示値
Select Case valOpt
Case 0
orgAry(i, 0) = Cells(i, itmCln).Value
Case 1
orgAry(i, 0) = Cells(i, cmtCln).Value
Case 2
orgAry(i, 0) = _
String(Len(Str(sumOpt)) - _
Len(Str(Cells(i, itmCln).Value)), " ") & _
Cells(i, itmCln).Value & ":" & Cells(i, cmtCln).Value
Case 3
orgAry(i, 0) = "●"
End Select
'作業列書出
Select Case Cells(i, spcCln).Value
Case "○"
orgAry(i, 1) = True
spcCnt = spcCnt + 1
spcSum = spcSum + Cells(i, itmCln).Value
Case "×" '無視
Case Else
Cells(i, rtnCln + 0).Value = Cells(i, itmCln).Value
Cells(i, rtnCln + 1).Value = i
End Select
Next i
Columns(rtnCln).Resize(, 2).Sort _
Key1:=Cells(1, rtnCln), Order1:=xlAscending, Header:=xlNo
itmCnt = Cells(Rows.Count, rtnCln).End(xlUp).Row
Erase sumAry
ReDim itmAry(0 To itmCnt, 0 To 1)
ReDim tmpAry(1 To itmCnt)
ReDim sumAry(1 To itmCnt + 1)
'候補リスト配列取得
For i = 1 To itmCnt
itmAry(i, 0) = Cells(i, rtnCln + 0).Value
itmAry(i, 1) = Cells(i, rtnCln + 1).Value
Next i
Columns(rtnCln).Resize(, 2).Delete
Application.ScreenUpdating = True
'設定値調整
With Application.WorksheetFunction
sumOpt = sumOpt - spcSum
uprOpt = uprOpt - spcCnt
lwrOpt = lwrOpt - spcCnt
End With
If (uprOpt <= 0) Or (itmCnt < uprOpt) Then uprOpt = itmCnt
If (lwrOpt <= 0) Or (itmCnt < lwrOpt) Then lwrOpt = 0
If rtnOpt = 0 Then rtnOpt = Columns.Count - rtnCln + 1
'後方和配列
For i = 1 To itmCnt
For j = i To itmCnt
sumAry(i) = sumAry(i) + itmAry(j, 0)
Next j
Next i
'予備探索(最少・最多調査)
rtnLmt = 1
ReDim rtnAry(1 To itmCnt, 1 To rtnLmt)
rtnCnt = 0
For i = 1 To itmCnt
Call SampleScl(0, sumOpt, i, i)
If rtnCnt = 1 Then Exit For
Next i
cntMin = i
rtnCnt = 0
For i = itmCnt To 1 Step -1
Call SampleScl(0, sumOpt, i, i)
If rtnCnt = 1 Then Exit For
Next i
cntMax = i
'上限・下限値調整
If lwrOpt <= cntMin Then lwrOpt = 0
If uprOpt >= cntMax Then uprOpt = itmCnt
'反転判定(基準はてきとー^^;)
If (0 < lwrOpt) Eqv (uprOpt < itmCnt) Then
rvsFlg = sumOpt * 2 > sumAry(1)
ElseIf 0 < lwrOpt Then
rvsFlg = sumOpt * 3 > sumAry(1) * 2
Else
rvsFlg = sumOpt * 3 > sumAry(1)
End If
'反転するなら設定値入替
If rvsFlg Then
sumOpt = sumAry(1) - sumOpt
tmpSwp = uprOpt
uprOpt = itmCnt - lwrOpt
lwrOpt = itmCnt - tmpSwp
End If
'中断処理登録
On Error GoTo ERR1
Application.EnableCancelKey = xlErrorHandler
rtnLmt = rtnOpt
rtnCnt = 0
Erase rtnAry
ReDim rtnAry(1 To itmCnt, 1 To rtnLmt)
'子P呼び出し
Select Case seqOpt
Case 0
If (lwrOpt = 0) And (uprOpt = itmCnt) Then
Call SampleScn(0, sumOpt)
Else
Call SampleScl(0, sumOpt, uprOpt, lwrOpt)
End If
Case Is > 0
For i = lwrOpt To uprOpt
j = IIf(rvsFlg Eqv (seqOpt = 2), i, uprOpt + lwrOpt - i)
Call SampleScl(0, sumOpt, j, j)
Next i
End Select
'中断処理解除
On Error GoTo 0
Application.EnableCancelKey = xlInterrupt
'書出し
If rtnCnt > 0 Then
dspLmt = Columns.Count - rtnCln + 1
dspLmt = IIf(rtnCnt < dspLmt, rtnCnt, dspLmt)
ReDim dspAry(1 To orgCnt, 1 To dspLmt)
'書出しテーブル作成
For i = 1 To dspLmt
k = 1
For j = 1 To orgCnt
If orgAry(j, 1) Then
dspAry(IIf(dspOpt = 0, k, j), i) = orgAry(j, 0)
k = k + 1
End If
Next j
For j = 1 To itmCnt
If rtnAry(j, i) <> rvsFlg Then
If dspOpt = 0 Then
dspAry(k, i) = orgAry(itmAry(j, 1), 0)
Else
dspAry(itmAry(j, 1), i) = orgAry(itmAry(j, 1), 0)
End If
k = k + 1
End If
Next j
Next i
Cells(1, rtnCln).Resize(orgCnt, dspLmt) = dspAry
End If
MsgBox _
"所要時間 " & Timer - t & " 秒" & vbCr & vbCr & _
rtnCnt & " 件 " & IIf(rtnCnt < rtnLmt, "", "以上") & " あります"
Application.StatusBar = False
Exit Sub
ERR1: '中断処理
If Err.Number = 18 Then
rtnCnt = rtnCnt - 1
rtnLmt = rtnCnt
Resume Next
Else
MsgBox Err.Number & " " & Err.Description, vbCritical
Application.EnableCancelKey = xlInterrupt
Application.StatusBar = False
End If
End Sub
'------------↓↑ ツヅク ↑↓------------
'無制限モード
Private Sub SampleScn(ByVal itmIdx As Long, ByVal tmpSum As Long)
Dim i As Long
If rtnCnt = rtnLmt Then Exit Sub
tmpSum = tmpSum - itmAry(itmIdx, 0)
If tmpSum = 0 Then
rtnCnt = rtnCnt + 1
For i = 1 To itmIdx
If tmpAry(i) Then rtnAry(i, rtnCnt) = True
Next i
Application.StatusBar = "探索中 " & rtnCnt & " 件見つけました"
Exit Sub
End If
For i = itmIdx + 1 To itmCnt
If sumAry(i) < tmpSum Then Exit Sub
If itmAry(i, 0) > tmpSum Then Exit Sub
tmpAry(i) = True
Call SampleScn(i, tmpSum)
tmpAry(i) = False
Next i
End Sub
'------------↓↑ ツヅク ↑↓------------
'制限モード
Private Sub SampleScl(ByVal itmIdx As Long, ByVal tmpSum As Long, ByVal uprLmt As Long, ByVal lwrLmt As Long)
Dim i As Long
If rtnCnt = rtnLmt Then Exit Sub
tmpSum = tmpSum - itmAry(itmIdx, 0)
If tmpSum = 0 Then
rtnCnt = rtnCnt + 1
For i = 1 To itmIdx
If tmpAry(i) Then rtnAry(i, rtnCnt) = True
Next i
Application.StatusBar = "探索中 " & rtnCnt & " 件見つけました"
Exit Sub
End If
If uprLmt < itmCnt - itmIdx - 1 Then
If uprLmt = 0 Then Exit Sub
If sumAry(itmCnt - uprLmt + 1) < tmpSum Then Exit Sub
uprLmt = uprLmt - 1
End If
If lwrLmt > 0 Then lwrLmt = lwrLmt - 1
For i = itmIdx + 1 To itmCnt - lwrLmt
If sumAry(i) < tmpSum Then Exit Sub
If itmAry(i, 0) > tmpSum Then Exit Sub
If lwrLmt > 0 Then
If sumAry(i) - sumAry(i + lwrLmt + 1) > tmpSum Then Exit Sub
End If
tmpAry(i) = True
Call SampleScl(i, tmpSum, uprLmt, lwrLmt)
tmpAry(i) = False
Next i
End Sub
'----------------------↑ ココマデ ↑----------------------
乗りかかった船なので一応対応してみましたが、
正直「高速化をしたい」という当初のご質問からは
少しずれてきたかなぁという印象です。
「作業依頼」や「続きの質問」で削除対象になっても詰まらないので、
【私としてはこれが最終案】ということにさせてください。
「追加要望」には応じられませんが、
バグがあれば対応しますのでご遠慮なく。
以上ご参考まで。長乱文・長乱コード陳謝。
お礼
_Kyleさんのおっしゃる通り、当初のテーマとは私自身の発言もずれてきてしまいました。 _Kyleにどっぷりと甘えてしまいました。 どんどん、要望通りにマクロを修正できるスキルの高さに、ついつい興奮して要望を出してしまいました。 今回の回答には、本当に助かりました。ありがとうございます。 あとはマクロを少しずつ勉強して、自分で理解して使わせていただきたいと思います。 最終版も大事に使わせていただいて、わからないところがあればマクロについて質問させていただきます。 本当にありがとうございました。
補足
申し訳ありませんが、オーバーフローをしてしまう場合があるのですが、リストの数字の桁数が多いのでしょうか? >sumAry(i) = sumAry(i) + itmAry(j, 0) の場所で止まってしまいます。 該当の変数のLomg型をもっと大きいものに変えればいいのでしょうか。