Algorithm 为集合的(无序)对编制索引
这是一个自动回答的问题,来源于OP在选择错误(IMHO)答案后似乎失去兴趣的地方 我确实检查了之前关于这个问题的问题,但似乎没有一个能解决这个问题 那有什么用? 假设你有4个人:阿卜杜勒、比阿特丽克斯、查理和达里亚。Algorithm 为集合的(无序)对编制索引,algorithm,sorting,Algorithm,Sorting,这是一个自动回答的问题,来源于OP在选择错误(IMHO)答案后似乎失去兴趣的地方 我确实检查了之前关于这个问题的问题,但似乎没有一个能解决这个问题 那有什么用? 假设你有4个人:阿卜杜勒、比阿特丽克斯、查理和达里亚。 您希望存储有关这些人彼此感觉的信息 Abdul and Beatrix are in love Beatrix and Charlie hate each other Abdul and Charlie are good friends Daria and Beatrix don'
您希望存储有关这些人彼此感觉的信息
Abdul and Beatrix are in love
Beatrix and Charlie hate each other
Abdul and Charlie are good friends
Daria and Beatrix don't know each other
etc.
在简练而缺乏诗意的计算机世界中,这可以转化为:
relation (Abdul , Beatrix) = love;
relation (Beatrix, Charlie) = hate;
relation (Abdul , Charlie) = friendship;
etc.
换句话说,如果您想要映射每对人之间的关系,您将需要一个数据结构,允许您为每对人维护一个唯一的值
虽然有几十种方法可以实现合适的数据结构,但在某些情况下,您可能希望该表是一个固定大小的数组,由表示给定关系的对直接索引
一些定义:
在前N个自然整数的集合中,让我们调用PN的所有无序对(a,b)的序列,使得a,b,按字典顺序排序
在(但愿)不那么晦涩的英语中,p列举了I的两个元素之间所有可能的关系
示例(对于N=4):
I4=(0,1,2,3)P4=((0,1)、(0,2)、(0,3)、(1,2)、(1,3)、(2,3)) 注意PN的基数是N(N-1)/2,因此
PN最紧凑的零基索引将在[0..N(N-1)/2-1]范围内 问题: 我们如何以紧凑而高效的方式索引PN 在其他领域
- 定义一个函数pN(a,b),给定IN的一对元素(a,b),该函数产生[0..N(N-1)/2-1]范围内pN的唯一索引
- 定义反向索引函数pN-1,给定pN的索引,该函数将生成相应的(a,b)对
p4(1,3)=4
p4-1(4)=(1,3)这似乎更像是一个数学问题 如果我的计算正确的话
For a pair P (a,b), the number of pairs of type (a,x) [x < b] before P shall be b-a-1.
The number of pairs of type (x,y) [x < a] = (n-1)+(n-2)+(n-3)...+(n-a) = a*n - a(a+1)/2
Hence total number of pairs before P = (b-a-1) + a*n - a(a+1)/2.
对于P(a,b)对,P之前(a,x)[x
因此p=(b-a-1)+a*n-a(a+1)/2的指数。
对于反向索引,首先找到a,因为我们知道对于第一个n-1项,a=0,对于下一个n-2项,a=1,等等
这可以在O(N)时间内完成,方法是迭代这些值并查看它何时超过索引
一旦我们找到a,那么就可以从上面的方程中找到b。指数计算
设a和b是aIN的两个元素
如果我们将p表示为半填充矩阵,那么我们的想法是在每一行的开头添加一个偏移量,以便在下一行的开头获得一个连续的编号
第0行以偏移量0开始,包含N-1个值第1行从N-1开始,包含N-2个值
等等 行
a
的偏移值将是(1..a)中i的N-i
之和
最后一对索引将是偏移量(a)+b
通过使用给出前n个整数之和的公式计算偏移量(a):s(n)=n(n-1)/2
这里的偏移量(a)为=s(N)-s(a)
经过一点数学计算,得出的公式可以写成:
pN(a,b)=a(2N-a-3)/2+b-1
p的伪代码为:
function p (a,b)
{
if (a>b) swap(a,b)
return a * (2 * N - a - 3) / 2 + b - 1
}
反向索引
所有的功劳都归功于Heuster为这个问题找到了一个优雅的解决方案。有关详细信息,请参阅所选答案 以下是伪代码:
const N1 = 2 * N - 1
const N2 = N1 * N1
function reverse_p (p)
{
a = floor( (N1 - sqrt(N2 - 8 * p)) / 2)
b = p - (2 * N - a - 3) * a / 2 + 1
return (a, b)
}
到目前为止,我在这里看到的两个答案都可以很好地进行第一次计算,但是反向计算需要循环,这是不必要的 考虑以下示例,其中包含
n=5
,显示了元素的编号方式
0 1 2 3 4
+---+---+---+---+---+
0 | | | | | |
+---+---+---+---+---+
1 | 0 | | | | |
+---+---+---+---+---+
2 | 1 | 4 | | | |
+---+---+---+---+---+
3 | 2 | 5 | 7 | | |
+---+---+---+---+---+
4 | 3 | 6 | 8 | 9 | |
+---+---+---+---+---+
给定一个元组(x,y)
(假设x
),列x
中的第一个索引由
n-1 + n-2 + ... + n-x = (n-1 + n-x) * x / 2 = (2n - x - 1) * x / 2
该列中的偏移量只是y-x-1
。这将得到总表达式
p_n(x, y) = (2n - x - 1) * x / 2 + y-x-1 = (2n - x - 3) * x / 2 + y-1
现在,走另一条路是很棘手的。我们有一些值
p
和n
,需要找到x
和y
。我们可以通过假设我们正在寻找列中的第一个单元格,即y=x+1
来简化我们的生活。如果我们在上面的公式中加上这个,我们得到
p = (2n - x - 1) * x / 2
重写这个公式会产生
x^2 - (2n-1) * x + 2p = 0
这是一个简单的二次方程,可以求解x:
x = [(2n-1) - Sqrt((2n-1)^2 - 8p)] / 2
当然,我们可能高估了x
,因为我们假设了y
的最低可能值。但是,我们离得不远(仍在右列中),因此将值四舍五入就足以得到真正的x
将我们发现的x
值插入原始公式中,可以得到一个非常简单的y
方程:
x = Floor( [(2n-1) - Sqrt((2n-1)^2 - 8p)] / 2 )
y = p - (2n - x - 3) * x / 2 + 1
可以说,取一个数的平方根是一个缓慢的操作(这是真的),但是这种方法对于更大的
n
值将优于循环,只是术语上的一个注释:如果这样的函数pN存在,你就没有一个集合;你有一个序列。@chepner谢谢。英语不是我的母语,我找不到合适的词:)好的,先生。对于程序员来说,一道数学题的用处可能有限:)不管怎样,我们得出了相同的结论,只是你甚至不需要这个方程就能找到b。我将把这个问题留待一会儿,以防某位杰出的数学家想出更好的办法。我们赢了!我被二次方程式吓倒了,但你很好地驯服了它。我不认为平方根是个问题,除非你是在8位微控制器上运行