Algorithm 为集合的(无序)对编制索引

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'

这是一个自动回答的问题,来源于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'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)对
PN的排列方式不太重要,但字典顺序可能是最方便的

例如:

P4=((0,1)、(0,2)、(0,3)、(1,2)、(1,3)、(2,3))
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位微控制器上运行