m
xn
且秩为r
的矩阵,其中元素来自指定的有限集,例如{0,1}
或{1,2,3,4,5}
。在某些非常宽泛的意义上,我希望它们是“随机”的,即我希望从算法中获得各种可能的输出,其分布与具有指定秩的元素集合上的所有矩阵的分布模糊地类似。事实上,我并不关心它的秩是否为
r
,只要它接近秩为r
的矩阵即可(由弗罗贝尼乌斯范数度量)。当处理的集合为实数时,我一直在做以下操作,这对我的需求完全足够:生成大小为
m
xr
的矩阵U
和大小为n
xr
的矩阵V
,其元素独立采样,例如从Normal(0, 2)中。然后UV'
是一个秩为r
的m
xn
矩阵(好吧,小于等于r
,但我认为它具有高概率的r
)。但是如果只做到这一步,然后将其二进制/ 1-5四舍五入,秩就会增加。
还可以通过进行SVD并取前
r
个奇异值来获得矩阵的较低秩近似值。但是,这些值不会位于所需集合中,并且将它们舍入将再次增加秩。与此相关的问题,但接受的答案不是“随机”的,另一个答案建议使用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矩阵或秩较低的更大二进制矩阵。 (例如,获取一个15秩的20x20二进制矩阵需要拒绝42,000次;对于10秩的20x20,需要1.2百万次。)
显然,这是因为前r行所张成的空间在这些情况下与样本空间(例如{0,1}^10)相比太小了。
我们希望第一行到第r行张成的空间与有效值集合的交集。因此,我们可以尝试从张成空间中进行采样并寻找有效值,但由于张成空间涉及实数系数,即使我们进行归一化以使得第一个分量在有效集合中,也永远无法找到有效向量。
也许可以将其表述为整数规划问题或其他什么问题吗?
{0,1,2,3,4}
(对于我的目的来说完全等效),那么标准基向量e_1
,...,e_10
就包含在这 5^10 个向量的集合中,因此您必须需要 10 个向量来跨越它。不过,这是个好观点 -- 我应该使用 0..4 而不是 1..5。 - Danica