我想生成大小为m
xn
和 rank的矩阵r
,其中元素来自指定的有限集,例如{0,1}
or {1,2,3,4,5}
。我希望它们在该词的某种非常松散的意义上是“随机的”,即我希望从算法中获得各种可能的输出,其分布与具有指定等级的该组元素上的所有矩阵的分布模糊相似。
实际上,我实际上并不关心它是否具有 rank r
,只是它接近于rank 矩阵r
(由 Frobenius 范数测量)。
当手头的集合是实数时,我一直在执行以下操作,这完全可以满足我的需要:生成U
大小为m
xr
和xV
的矩阵,其中元素独立地从 Normal(0, 2) 中采样。然后是一个x秩矩阵(嗯,,但我认为它很有可能)。n
r
U V'
m
n
r
<= r
r
但是,如果我只是这样做然后四舍五入到二进制/ 1-5,则排名会增加。
r
也可以通过执行 SVD 并获取第一个奇异值来获得矩阵的低秩近似。但是,这些值不会位于所需的集合中,并且将它们四舍五入将再次提高排名。
这个问题是相关的,但接受的答案不是“随机的”,另一个答案建议 SVD,如前所述,它在这里不起作用。
我想到的一种可能性是r
从集合中生成线性独立的行或列向量,然后通过它们的线性组合得到矩阵的其余部分。不过,我不太清楚如何获得“随机”线性独立向量,或者之后如何以准随机方式组合它们。
(并不是说它是超级相关的,但我在 numpy 中这样做。)
更新:我已经尝试了 EMS 在评论中建议的方法,这个简单的实现:
real = np.dot(np.random.normal(0, 1, (10, 3)), np.random.normal(0, 1, (3, 10)))
bin = (real > .5).astype(int)
rank = np.linalg.matrix_rank(bin)
niter = 0
while rank > des_rank:
cand_changes = np.zeros((21, 5))
for n in range(20):
i, j = random.randrange(5), random.randrange(5)
v = 1 - bin[i,j]
x = bin.copy()
x[i, j] = v
x_rank = np.linalg.matrix_rank(x)
cand_changes[n,:] = (i, j, v, x_rank, max((rank + 1e-4) - x_rank, 0))
cand_changes[-1,:] = (0, 0, bin[0,0], rank, 1e-4)
cdf = np.cumsum(cand_changes[:,-1])
cdf /= cdf[-1]
i, j, v, rank, score = cand_changes[np.searchsorted(cdf, random.random()), :]
bin[i, j] = v
niter += 1
if niter % 1000 == 0:
print(niter, rank)
它适用于小型矩阵,但在例如 10x10 时会分崩离析——它似乎卡在 6 或 7 级,至少在数十万次迭代中。
看起来这可能与更好(即不太平坦)的目标函数一起工作得更好,但我不知道那会是什么。
我还尝试了一种简单的拒绝方法来构建矩阵:
def fill_matrix(m, n, r, vals):
assert m >= r and n >= r
trans = False
if m > n: # more columns than rows I think is better
m, n = n, m
trans = True
get_vec = lambda: np.array([random.choice(vals) for i in range(n)])
vecs = []
n_rejects = 0
# fill in r linearly independent rows
while len(vecs) < r:
v = get_vec()
if np.linalg.matrix_rank(np.vstack(vecs + [v])) > len(vecs):
vecs.append(v)
else:
n_rejects += 1
print("have {} independent ({} rejects)".format(r, n_rejects))
# fill in the rest of the dependent rows
while len(vecs) < m:
v = get_vec()
if np.linalg.matrix_rank(np.vstack(vecs + [v])) > len(vecs):
n_rejects += 1
if n_rejects % 1000 == 0:
print(n_rejects)
else:
vecs.append(v)
print("done ({} total rejects)".format(n_rejects))
m = np.vstack(vecs)
return m.T if trans else m
这适用于例如具有任何等级的 10x10 二进制矩阵,但不适用于 0-4 矩阵或更大的具有较低等级的二进制矩阵。(例如,获得一个 20x20 的 15 级二进制矩阵需要 42,000 次拒绝;使用 20x20 的 10 级,需要 120 万次。)
这显然是因为第一r
行所跨越的空间在我从中采样的空间的一部分中太小了,例如{0,1}^10
,在这些情况下。
我们想要第一r
行的跨度与一组有效值的交集。所以我们可以尝试从跨度中采样并寻找有效值,但由于跨度涉及实值系数,它永远不会找到我们有效的向量(即使我们进行归一化,例如第一个分量在有效集合中)。
也许这可以表述为一个整数规划问题,或者什么?