我有一个说3000个单词的单词语料库,例如[hello,who,this ..]。 我想从该语料库中找到第n个3个单词的组合。只要算法给出一致的输出,我对任何顺序都可以。 该算法的时间复杂度如何?
我看到了this的答案,但正在寻找简单的东西。
答案 0 :(得分:1)
(请注意,我将在此答案中使用基于1的索引和排名。)
要从n个元素的列表中生成3个元素的所有组合,我们将1到n-2的所有元素作为第一个元素,然后对于每个元素,我们将第一个元素之后的所有元素都作为取n-1作为第二个元素,那么对于每个元素,我们将第二个元素之后的所有元素取为n作为第三个元素。这给了我们固定的顺序,以及等级与特定组合之间的直接关系。
如果我们将元素i作为第一个元素,则第二个和第三个元素存在(n-i选择2)种可能性,因此,将i作为第一个元素的(n-i选择2个)组合。如果我们然后将元素j作为第二个元素,则第三个元素有(n-j个选择1)= n-j个可能性,因此将i和j作为前两个元素的n-j个组合。
在二项式系数表中进行线性搜索
使用这些二项式系数的表,我们可以快速找到特定组合,并赋予其等级。让我们看一个包含10个元素的简化示例。这些是元素i为第一个元素的组合数:
i
1 C(9,2) = 36
2 C(8,2) = 28
3 C(7,2) = 21
4 C(6,2) = 15
5 C(5,2) = 10
6 C(4,2) = 6
7 C(3,2) = 3
8 C(2,2) = 1
---
120 = C(10,3)
这些是元素j为第二个元素的组合数:
j
2 C(8,1) = 8
3 C(7,1) = 7
4 C(6,1) = 6
5 C(5,1) = 5
6 C(4,1) = 4
7 C(3,1) = 3
8 C(2,1) = 2
9 C(1,1) = 1
因此,如果我们要寻找例如排名96,我们将查看第一个元素i的每个选择的组合数量,直到找到排名96的组合属于哪个组合组:
i
1 36 96 > 36 96 - 36 = 60
2 28 60 > 28 60 - 28 = 32
3 21 32 > 21 32 - 21 = 11
4 15 11 <= 15
因此,我们知道第一个元素i为4,并且在i = 4的15个组合中,我们正在寻找第11个组合。现在我们来看第二个元素j的每个选择的组合数量,从4开始:
j
5 5 11 > 5 11 - 5 = 6
6 4 6 > 4 6 - 4 = 2
7 3 2 <= 3
因此,我们知道第二个元素j为7,第三个元素是j = 7(即k = 9)的第二个组合。因此,等级为96的组合包含元素4、7和9。
在二项式系数的总和表中进行二分搜索
与其创建一个二项式系数表然后执行线性搜索,不如创建一个二项式系数的总和表,然后对其进行二值搜索,这当然会更有效率。这将改善从O(N)到O(logN)的时间复杂度;在N = 3000的情况下,可以以log 2 (3000)= 12步进行两次查找。
因此,我们将存储:
i
1 36
2 64
3 85
4 100
5 110
6 116
7 119
8 120
和:
j
2 8
3 15
4 21
5 26
6 30
7 33
8 35
9 36
请注意,在第二张表中找到j时,必须从和中减去与i对应的和。让我们再次看一下等级96和组合[4,7,9]的示例;我们发现第一个大于或等于等级的值:
3 85 96 > 85
4 100 96 <= 100
所以我们知道i = 4;然后,我们减去i-1之前的和,得到:
96 - 85 = 11
现在,我们在表中查找j,但我们在j = 4之后开始,并从这些和中减去与4对应的和,即21。再一次,我们发现第一个值大于或等于我们要寻找的等级(现在是11):
6 30 - 21 = 9 11 > 9
7 33 - 21 = 12 11 <= 12
所以我们知道j = 7;我们减去与j-1对应的先前和,得到:
11 - 9 = 2
因此,我们知道第二个元素j为7,第三个元素是j = 7(即k = 9)的第二个组合。因此,等级为96的组合包含元素4、7和9。
对查询表进行硬编码
当然,每次我们想要执行查找时,都不必再次生成这些查找表。我们只需要生成它们一次,然后将它们硬编码到秩组合算法中;这应该只需要2998 * 64位+ 2998 * 32位= 35kB的空间,并且可以使算法变得异常快。
逆算法
逆算法,在给定元素[i,j,k]组合的情况下查找排名,则意味着:
查找列表中元素的索引;如果列表已排序(例如,单词按字母顺序排序),则可以通过在O(logN)中进行二进制搜索来完成。
在表中找到与i-1对应的i的总和。
在表中添加与j-1对应的j的总和,减去与i对应的总和。
添加到该k-j。
让我们再看一下同一示例,其中包含元素[4,7,9]的组合:
i=4 -> table_i[3] = 85
j=7 -> table_j[6] - table_j[4] = 30 - 21 = 9
k=9 -> k-j = 2
rank = 85 + 9 + 2 = 96
N = 3000的查询表
此代码段生成查找表,其中包含i = 1至2998的二项式系数的总和:
function C(n, k) { // binomial coefficient (Pascal's triangle)
if (k < 0 || k > n) return 0;
if (k > n - k) k = n - k;
if (! C.t) C.t = [[1]];
while (C.t.length <= n) {
C.t.push([1]);
var l = C.t.length - 1;
for (var i = 1; i < l / 2; i++)
C.t[l].push(C.t[l - 1][i - 1] + C.t[l - 1][i]);
if (l % 2 == 0)
C.t[l].push(2 * C.t[l - 1][(l - 2) / 2]);
}
return C.t[n][k];
}
for (var total = 0, x = 2999; x > 1; x--) {
total += C(x, 2);
document.write(total + ", ");
}
此代码段生成查找表,其中包含j = 2到2999的二项式系数的总和:
for (var total = 0, x = 2998; x > 0; x--) {
total += x;
document.write(total + ", ");
}
代码示例
这是一个快速的代码示例,不幸的是,由于没有关于SO的答案的大小限制,因此没有完整的硬编码查找表。运行上面的代码片段,然后将结果粘贴到iTable和jTable数组中(在前导零之后),以使用硬编码的查找表获得更快的版本。
function combinationToRank(i, j, k) {
return iTable[i - 1] + jTable[j - 1] - jTable[i] + k - j;
}
function rankToCombination(rank) {
var i = binarySearch(iTable, rank, 1);
rank -= iTable[i - 1];
rank += jTable[i];
var j = binarySearch(jTable, rank, i + 1);
rank -= jTable[j - 1];
var k = j + rank;
return [i, j, k];
function binarySearch(array, value, first) {
var last = array.length - 1;
while (first < last - 1) {
var middle = Math.floor((last + first) / 2);
if (value > array[middle]) first = middle;
else last = middle;
}
return (value <= array[first]) ? first : last;
}
}
var iTable = [0]; // append look-up table values here
var jTable = [0, 0]; // and here
// remove this part when using hard-coded look-up tables
function C(n,k){if(k<0||k>n)return 0;if(k>n-k)k=n-k;if(!C.t)C.t=[[1]];while(C.t.length<=n){C.t.push([1]);var l=C.t.length-1;for(var i=1;i<l/2;i++)C.t[l].push(C.t[l-1][i-1]+C.t[l-1][i]);if(l%2==0)C.t[l].push(2*C.t[l-1][(l-2)/2])}return C.t[n][k]}
for (var iTotal = 0, jTotal = 0, x = 2999; x > 1; x--) {
iTable.push(iTotal += C(x, 2));
jTable.push(jTotal += x - 1);
}
document.write(combinationToRank(500, 1500, 2500) + "<br>");
document.write(rankToCombination(1893333750) + "<br>");