我知道有一种算法允许,在给定数字组合(无重复,无顺序)的情况下,计算字典顺序的索引。
对我的应用来说,加速事情非常有用......
例如:
combination(10, 5)
1 - 1 2 3 4 5
2 - 1 2 3 4 6
3 - 1 2 3 4 7
....
251 - 5 7 8 9 10
252 - 6 7 8 9 10
我需要算法返回给定组合的索引
es:index( 2, 5, 7, 8, 10 )
- >指数
再次感谢大家: - )
答案 0 :(得分:8)
这是一个可以完成工作的片段。
#include <iostream>
int main()
{
const int n = 10;
const int k = 5;
int combination[k] = {2, 5, 7, 8, 10};
int index = 0;
int j = 0;
for (int i = 0; i != k; ++i)
{
for (++j; j != combination[i]; ++j)
{
index += c(n - j, k - i - 1);
}
}
std::cout << index + 1 << std::endl;
return 0;
}
假设你有一个功能
int c(int n, int k);
将返回从n个元素中选择k个元素的组合数。 循环计算给定组合之前的组合数。 通过在最后添加一个,我们得到实际的索引。
对于给定的组合有 c(9,4)= 126个含有1的组合,因此以字典顺序排在前面。
在包含2作为最小数字的组合中有
c(7,3)= 35个组合,其中3为第二小数
c(6,3)= 20个组合,其中4为第二小数
所有这些都在给定的组合之前。
在包含2和5作为两个最小数字的组合中,有
c(4,2)= 6种组合,其中6为第三小数。
所有这些都在给定的组合之前。
等
如果在内部循环中放置一个print语句,您将获得数字 126,35,20,6,1。 希望能够解释代码。
答案 1 :(得分:6)
将您的号码选择转换为factorial base number。此数字将是您想要的索引。从技术上讲,这会计算所有排列的词典索引,但是如果你只给它组合,那么索引仍然是有序的,只是每个组合之间的所有排列都有一些大的间隙。
编辑:删除了伪代码,这是不正确的,但上面的方法应该有效。现在太累了想出正确的伪代码。
编辑2:这是一个例子。假设我们从一组10个元素中选择了5个元素的组合,就像上面的例子一样。如果组合为2 3 4 6 8
,您将获得相关的阶乘基数,如下所示:
取出未选中的元素并计算你要经过的数量以获得你正在选择的元素。
1 2 3 4 5 6 7 8 9 10
2 -> 1
1 3 4 5 6 7 8 9 10
3 -> 1
1 4 5 6 7 8 9 10
4 -> 1
1 5 6 7 8 9 10
6 -> 2
1 5 7 8 9 10
8 -> 3
因此,因子基数中的指数为1112300000
在十进制基数中,它是
1*9! + 1*8! + 1*7! + 2*6! + 3*5! = 410040
答案 2 :(得分:3)
这是Kreher和Stinson的组合算法第44页的算法2.7 kSubsetLexRank 。
r = 0
t[0] = 0
for i from 1 to k
if t[i - 1] + 1 <= t[i] - 1
for j from t[i - 1] to t[i] - 1
r = r + choose(n - j, k - i)
return r
数组t保存您的值,例如[5 7 8 9 10]。函数choose(n,k)计算数字“n选择k”。结果值r将是索引,示例为251。其他输入是n和k,例如它们将是10和5。
答案 3 :(得分:2)
零基,
# v: array of length k consisting of numbers between 0 and n-1 (ascending)
def index_of_combination(n,k,v):
idx = 0
for p in range(k-1):
if p == 0: arrg = range(1,v[p]+1)
else: arrg = range(v[p-1]+2, v[p]+1)
for a in arrg:
idx += combi[n-a, k-1-p]
idx += v[k-1] - v[k-2] - 1
return idx
答案 4 :(得分:1)
Null Set有正确的方法。索引对应于序列的阶乘基数。您可以像任何其他基数一样构建因子基数,但每个数字的基数都会减少。
现在,析因基数中每个数字的值是小于尚未使用的元素数。所以,对于组合(10,5):
(1 2 3 4 5) == 0*9!/5! + 0*8!/5! + 0*7!/5! + 0*6!/5! + 0*5!/5!
== 0*3024 + 0*336 + 0*42 + 0*6 + 0*1
== 0
(10 9 8 7 6) == 9*3024 + 8*336 + 7*42 + 6*6 + 5*1
== 30239
逐步计算索引应该很容易。
答案 5 :(得分:1)
还有另一种方法可以做到这一切。您可以生成所有可能的组合并将它们写入二进制文件,其中每个梳子由从零开始的索引表示。然后,当您需要查找索引并给出组合时,可以对文件应用二进制搜索。这是功能。它是用VB.NET 2010为我的乐透程序编写的,它与以色列彩票系统配合使用,因此有一个奖励(第7个)号码;只是忽略它。
Public Function Comb2Index( _
ByVal gAr() As Byte) As UInt32
Dim mxPntr As UInt32 = WHL.AMT.WHL_SYS_00 '(16.273.488)
Dim mdPntr As UInt32 = mxPntr \ 2
Dim eqCntr As Byte
Dim rdAr() As Byte
modBinary.OpenFile(WHL.WHL_SYS_00, _
FileMode.Open, FileAccess.Read)
Do
modBinary.ReadBlock(mdPntr, rdAr)
RP: If eqCntr = 7 Then GoTo EX
If gAr(eqCntr) = rdAr(eqCntr) Then
eqCntr += 1
GoTo RP
ElseIf gAr(eqCntr) < rdAr(eqCntr) Then
If eqCntr > 0 Then eqCntr = 0
mxPntr = mdPntr
mdPntr \= 2
ElseIf gAr(eqCntr) > rdAr(eqCntr) Then
If eqCntr > 0 Then eqCntr = 0
mdPntr += (mxPntr - mdPntr) \ 2
End If
Loop Until eqCntr = 7
EX: modBinary.CloseFile()
Return mdPntr
End Function
P.S。在Core 2 Duo上生成1600万个梳子需要5到10分钟。在SATA驱动器上使用文件二进制搜索查找索引需要397毫秒。
答案 6 :(得分:1)
对于我的项目我也需要相同的,我找到的最快的解决方案是(Python):
import math
def nCr(n,r):
f = math.factorial
return f(n) / f(r) / f(n-r)
def index(comb,n,k):
r=nCr(n,k)
for i in range(k):
if n-comb[i]<k-i:continue
r=r-nCr(n-comb[i],k-i)
return r
我的输入“comb”包含按升序排列的元素您可以使用例如:
来测试代码import itertools
k=3
t=[1,2,3,4,5]
for x in itertools.combinations(t, k):
print x,index(x,len(t),k)
如果梳子=(a1,a2,a3 ......,ak)(按递增顺序),则不难证明:
index = [nCk-(n-a1 + 1)Ck] + [(n-a1)C(k-1) - (n-a2 + 1)C(k-1)] + ... = nCk - (n-a1)Ck - (n-a2)C(k-1) - .... - (n-ak)C1
答案 7 :(得分:0)
让你的组合成为( 1 , 2 ,..., k-1 , k < / sub>)其中a 1 &lt; a 2 &lt; ......&lt;一个<子>ķ子>。如果a> = b则选择(a,b)= a!/(b!*(a-b)!),否则为0。然后,您要查找的索引是
选择(a k -1,k)+选择(a k-1 -1,k-1)+选择(a k-2 -1,k-2)+ ... +选择(a 2 -1,2)+选择( 1 -1,1)+ 1
第一项计算k元素组合的数量,使得最大元素小于 k 。第二项计算(k-1)元素组合的数量,使得最大元素小于 k-1 。等等。
请注意,要从(在您的示例中为10)中选择的元素的Universe大小在索引的计算中不起作用。你能明白为什么吗?
答案 8 :(得分:0)
假设最大setSize不是太大,您可以简单地生成一个查找表,其中输入以这种方式编码:
int index(a,b,c,...)
{
int key = 0;
key |= 1<<a;
key |= 1<<b;
key |= 1<<c;
//repeat for all arguments
return Lookup[key];
}
要生成查找表,请查看this "banker's order" algorithm。生成所有组合,并存储每个nItem的基本索引。 (对于p6上的示例,这将是[0,1,5,11,15])。请注意,通过以与示例相反的顺序存储答案(LSB首先设置)您将只需要一个表,大小为最大可能集。
通过遍历执行Lookup[combination[i]]=i-baseIdx[nItems]
答案 9 :(得分:0)
如果你有一组正整数0&lt; = x_1&lt; X_2&LT; ......&lt; x_k,那么你可以使用一种叫做压扁顺序的东西:
I = sum(j=1..k) Choose(x_j,j)
压扁顺序的美妙之处在于它独立于父集中的最大值。
压扁的订单不是您要查找的订单,但它是相关的。
使用压扁顺序来获取{1,...,n)的k子集集合中的词典顺序是
1 <= x1 < ... < x_k <=n
计算
0 <= n-x_k < n-x_(k-1) ... < n-x_1
然后计算(n-x_k,...,n-k_1)的压扁顺序索引
然后从Choose(n,k)中减去压扁的顺序索引,得到你的结果,即词典索引。
如果你有一个相对较小的n和k值,你可以缓存所有的值用a选择(a,b)
答案 10 :(得分:0)
样本解决方案:
class Program
{
static void Main(string[] args)
{
// The input
var n = 5;
var t = new[] { 2, 4, 5 };
// Helping transformations
ComputeDistances(t);
CorrectDistances(t);
// The algorithm
var r = CalculateRank(t, n);
Console.WriteLine("n = 5");
Console.WriteLine("t = {2, 4, 5}");
Console.WriteLine("r = {0}", r);
Console.ReadKey();
}
static void ComputeDistances(int[] t)
{
var k = t.Length;
while (--k >= 0)
t[k] -= (k + 1);
}
static void CorrectDistances(int[] t)
{
var k = t.Length;
while (--k > 0)
t[k] -= t[k - 1];
}
static int CalculateRank(int[] t, int n)
{
int k = t.Length - 1, r = 0;
for (var i = 0; i < t.Length; i++)
{
if (t[i] == 0)
{
n--;
k--;
continue;
}
for (var j = 0; j < t[i]; j++)
{
n--;
r += CalculateBinomialCoefficient(n, k);
}
n--;
k--;
}
return r;
}
static int CalculateBinomialCoefficient(int n, int k)
{
int i, l = 1, m, x, y;
if (n - k < k)
{
x = k;
y = n - k;
}
else
{
x = n - k;
y = k;
}
for (i = x + 1; i <= n; i++)
l *= i;
m = CalculateFactorial(y);
return l/m;
}
static int CalculateFactorial(int n)
{
int i, w = 1;
for (i = 1; i <= n; i++)
w *= i;
return w;
}
}
幕后的想法是将k子集与从n尺寸集中绘制k元素的操作相关联。它是一种组合,因此可能的项目总数将为(n k)。我们可以在Pascal Triangle中寻求解决方案,这是一个线索。经过一段时间将手动编写的示例与Pascal三角形中的相应数字进行比较后,我们将找到模式,从而找到算法。
答案 11 :(得分:0)
我使用了user515430的答案并将其转换为python3。另外,它支持非连续值,因此您可以将[1,3,5,7,9]作为您的池而不是range(1,11)
from itertools import combinations
from scipy.special import comb
from pandas import Index
debugcombinations = False
class IndexedCombination:
def __init__(self, _setsize, _poolvalues):
self.setsize = _setsize
self.poolvals = Index(_poolvalues)
self.poolsize = len(self.poolvals)
self.totalcombinations = 1
fast_k = min(self.setsize, self.poolsize - self.setsize)
for i in range(1, fast_k + 1):
self.totalcombinations = self.totalcombinations * (self.poolsize - fast_k + i) // i
#fill the nCr cache
self.choose_cache = {}
n = self.poolsize
k = self.setsize
for i in range(k + 1):
for j in range(n + 1):
if n - j >= k - i:
self.choose_cache[n - j,k - i] = comb(n - j,k - i, exact=True)
if debugcombinations:
print('testnth = ' + str(self.testnth()))
def get_nth_combination(self,index):
n = self.poolsize
r = self.setsize
c = self.totalcombinations
#if index < 0 or index >= c:
# raise IndexError
result = []
while r:
c, n, r = c*r//n, n-1, r-1
while index >= c:
index -= c
c, n = c*(n-r)//n, n-1
result.append(self.poolvals[-1 - n])
return tuple(result)
def get_n_from_combination(self,someset):
n = self.poolsize
k = self.setsize
index = 0
j = 0
for i in range(k):
setidx = self.poolvals.get_loc(someset[i])
for j in range(j + 1, setidx + 1):
index += self.choose_cache[n - j, k - i - 1]
j += 1
return index
#just used to test whether nth_combination from the internet actually works
def testnth(self):
n = 0
_setsize = self.setsize
mainset = self.poolvals
for someset in combinations(mainset, _setsize):
nthset = self.get_nth_combination(n)
n2 = self.get_n_from_combination(nthset)
if debugcombinations:
print(str(n) + ': ' + str(someset) + ' vs ' + str(n2) + ': ' + str(nthset))
if n != n2:
return False
for x in range(_setsize):
if someset[x] != nthset[x]:
return False
n += 1
return True
setcombination = IndexedCombination(5, list(range(1,10+1)))
print( str(setcombination.get_n_from_combination([2,5,7,8,10])))
返回188