我需要遍历所有满足条件0<=i,
0<=j和
i的二元组,其中
n
是正整数。
问题是我只能通过另一个变量k
来循环。我可以控制k
的范围。因此这个问题就是确定两个算术方法f(k)
和g(k)
,使得当k
按其连续值遍历时,i=f(k)
和j=g(k)
遍历所有合法的二元组。
有什么简单的方法可以做到这一点吗?
我需要遍历所有满足条件0<=i,
0<=j和
i的二元组,其中
n
是正整数。
问题是我只能通过另一个变量k
来循环。我可以控制k
的范围。因此这个问题就是确定两个算术方法f(k)
和g(k)
,使得当k
按其连续值遍历时,i=f(k)
和j=g(k)
遍历所有合法的二元组。
有什么简单的方法可以做到这一点吗?
我觉得我明白了(在Python中):
def get_ij(n, k):
j = k // (n - 1) # // is integer (truncating) division
i = k - j * (n - 1)
if i >= j:
i = (n - 2) - i
j = (n - 1) - j
return i, j
for n in range(2, 6):
print n, sorted(get_ij(n, k) for k in range(n * (n - 1) / 2))
我认为我找到了另一种方法,可以按字典顺序给出成对的数。注意,在这里i > j
而不是i < j
。
基本上,该算法由以下两个表达式组成:
i = floor((1 + sqrt(1 + 8*k))/2)
j = k - i*(i - 1)/2
将j,k作为k
的函数给出。这里k
是从0开始的索引。
优点:按字典顺序给出对。
缺点:依赖于浮点算术。
原理:
我们想要在下表中实现映射:
k -> (i,j)
0 -> (1,0)
1 -> (2,0)
2 -> (2,1)
3 -> (3,0)
4 -> (3,1)
5 -> (3,2)
....
(i,j) -> k
。很容易意识到:k = i*(i-1)/2 + j
。由于 j < i
,因此对于所有固定的 i
和 j
,与其对应的 k
值满足:i*(i-1)/2 <= k < i*(i+1)/2
k
,i=f(k)
返回最大的整数i
,使得i*(i-1)/2 <= k
。经过一些代数运算:i = f(k) = floor((1 + sqrt(1 + 8*k))/2)
在我们找到值i
之后,j
可以轻松地给出。
j = k - i*(i-1)/2
我不确定是否完全理解问题,但总的来说,如果0 <= i < n,0 <= j < n,则您想遍历0 <= k < n * n。
for (int k = 0; k < n*n; k++) {
int i = k / n;
int j = k % n;
// ...
}
[编辑] 我刚刚看到 i < j;因此,这个解决方案并不是最优的,因为少于 n*n 次迭代是必要的...
如果我们从数字三角形的角度来考虑我们的解决方案,其中k
是序列
1
2 3
4 5 6
7 8 9 10
11 12 13 14 15
...
然后j
将是我们的(非零基)行号,也就是最大的整数,使得
j * (j - 1) / 2 < k
解决j
的问题:
j = ceiling ((sqrt (1 + 8 * k) - 1) / 2)
而 i
将是 k
在该行中的 (从零开始的) 位置
i = k - j * (j - 1) / 2 - 1
k
的范围为:
1 <= k <= n * (n - 1) / 2
你真的需要两个算术函数 f(k) 和 g(k) 来完成这个任务吗?因为你可以先创建一个列表,例如
L = []
for i in range(n-1):
for j in range(n):
if j>i:
L.append((i,j))
这将为您提供您所请求的所有配对。现在,您的变量k可以沿着列表的索引运行。例如,如果我们取n=5,
for x in L:
print(x)
给我们
(0,1), (0,2), (0,3), (0,4), (1,2), (1,3), (1,4), (2,3), (2,4), (3,4)
for k in range(2, 5)
print L[k]
(0,3), (0,4), (1,2)
F(k)(=(f(k),g(k)))
的方法,它接受参数k
并返回一对(i,j)
,使得i < j
,0<=i<n
和0<=j<n
,并且当k
在适当的范围内取值时,所有这样的对恰好出现一次。 - a06ef(k)
和g(k)
的时间复杂度为O(n)
,那么可以很容易地通过暴力破解来解决此问题。否则,我有一种感觉,可以巧妙地将下三角矩阵折叠起来,使其易于遍历(例如,变成矩形)。 - NPE