別のコレクションで数字に合わせて数字のセットを見つける
-
02-10-2019 - |
質問
私が作っているゲームの場合、私は数字のリストがある状況を持っています - たとえば[7、4、9、1、15、2](名前が付けられました A
このため) - および別の数字のリスト - たとえば[11、18、14、8、3](名前付き B
) - 私に提供されました。目標は、数字のすべての組み合わせを見つけることです A
それは数になります B
. 。例えば:
- 1 + 2 = 3
- 1 + 7 = 8
- 2 + 9 = 11
- 4 + 7 = 11
- 1 + 2 + 4 + 7 = 14
- 1 + 2 + 15 = 18
- 2 + 7 + 9 = 18
...等々。 (これの目的のために、 1 + 2
と同じです 2 + 1
.)
このような小さなリストの場合、組み合わせをブルートフォースするだけで些細なことですが、私はこれらの数千から数万の数値を見る可能性に直面しており、アプリケーションの寿命を越えてこのルーチンを繰り返し使用しています。 100%のカバレッジで合理的な時間でこれを達成するために利用できるエレガントなアルゴリズムはありますか?これに失敗すると、合理的な時間で「十分な」組み合わせセットを与えることができるまともなヒューリスティックがありますか?
擬似コードまたはきちんと人気のある読みやすい言語でアルゴリズムを探しています(「と「そこに…;;)、またはこの種の検索を実装する方法の英語の説明に注意してください。
追加するために編集:
これまでに提供されている多くの良い情報。みんなありがとう!今のところ要約:
- 問題はNPコンプリートであるため、妥当な時間に100%の精度を得るためのブルートフォース以外の方法はありません。
- 問題は、 サブセット合計 また ナップザック 問題。この問題に適応できる可能性のある両方によく知られているヒューリスティックがあります。
アイデアを来てください!そしてもう一度ありがとう!
解決
この問題はNPコンプリートです...これは、NPコンプリートであることが知られているサブセット合計問題のある程度のバリエーションです(実際、サブセット合計問題はあなたよりも簡単です)。
詳細については、こちらをご覧ください。http://en.wikipedia.org/wiki/subset_sum_problem
他のヒント
1から30の範囲の数字のコメントで述べたように、問題には高速な解決策があります。私はそれをCでテストしましたが、あなたの与えられた例では、ミリ秒のみが必要であり、非常にうまく拡張します。複雑さはo(n+k)で、nはリストの長さです A
およびkリストの長さ B
, 、しかし、高い一定の因子を持っています(1から30の合計を取得するには28.598の可能性があります)。
#define WIDTH 30000
#define MAXNUMBER 30
int create_combination(unsigned char comb[WIDTH][MAXNUMBER+1],
int n,
unsigned char i,
unsigned char len,
unsigned char min,
unsigned char sum) {
unsigned char j;
if (len == 1) {
if (n+1>=WIDTH) {
printf("not enough space!\n");
exit(-1);
}
comb[n][i] = sum;
for (j=0; j<=i; j++)
comb[n+1][j] = comb[n][j];
n++;
return n;
}
for (j=min; j<=sum/len; j++) {
comb[n][i] = j;
n = create_combination(comb, n, i+1, len-1, j, sum-j);
}
return n;
}
int main(void)
{
unsigned char A[6] = { 7, 4, 9, 1, 15, 2 };
unsigned char B[5] = { 11, 18, 14, 8, 3 };
unsigned char combinations[WIDTH][MAXNUMBER+1];
unsigned char needed[WIDTH][MAXNUMBER];
unsigned char numbers[MAXNUMBER];
unsigned char sums[MAXNUMBER];
unsigned char i, j, k;
int n=0, m;
memset(combinations, 0, sizeof combinations);
memset(needed, 0, sizeof needed);
memset(numbers, 0, sizeof numbers);
memset(sums, 0, sizeof sums);
// create array with all possible combinations
// combinations[n][0] stores the sum
for (i=2; i<=MAXNUMBER; i++) {
for (j=2; j<=i; j++) {
for (k=1; k<=MAXNUMBER; k++)
combinations[n][k] = 0;
combinations[n][0] = i;
n = create_combination(combinations, n, 1, j, 1, i);
}
}
// count quantity of any summands in each combination
for (m=0; m<n; m++)
for (i=1; i<=MAXNUMBER && combinations[m][i] != 0; i++)
needed[m][combinations[m][i]-1]++;
// count quantity of any number in A
for (m=0; m<6; m++)
if (numbers[A[m]-1] < MAXNUMBER)
numbers[A[m]-1]++;
// collect possible sums from B
for (m=0; m<5; m++)
sums[B[m]-1] = 1;
for (m=0; m<n; m++) {
// check if sum is in B
if (sums[combinations[m][0]-1] == 0)
continue;
// check if enough summands from current combination are in A
for (i=0; i<MAXNUMBER; i++) {
if (numbers[i] < needed[m][i])
break;
}
if (i<MAXNUMBER)
continue;
// output result
for (j=1; j<=MAXNUMBER && combinations[m][j] != 0; j++) {
printf(" %s %d", j>1 ? "+" : "", combinations[m][j]);
}
printf(" = %d\n", combinations[m][0]);
}
return 0;
}
1 + 2 = 3
1 + 7 = 8
2 + 9 = 11
4 + 7 = 11
1 + 4 + 9 = 14
1 + 2 + 4 + 7 = 14
1 + 2 + 15 = 18
2 + 7 + 9 = 18
ナップサックの問題のように聞こえます(参照してください http://en.wikipedia.org/wiki/knapsack_problem. 。そのページでは、問題が一般的にNP不完全であることも説明しています。
これは、すべての有効な組み合わせを見つけたい場合、多くの時間が必要であることを意味すると思います。
これは小さな一般化です サブセット合計問題. 。一般に、それはNP不完全ですが、すべての数値が整数であり、最大数がである限り B
比較的小さいです、私がリンクしたウィキペディアの記事に記載されている疑似総類似の解は、トリックを行うべきです。