在固定的元素集上生成某个等级的“随机”矩阵

时间:2012-04-12 22:07:31

标签: python matlab math numpy linear-algebra

我想生成大小为m x n和等级r的矩阵,其元素来自指定的有限集,例如{0,1}{1,2,3,4,5}。我希望它们在某个非常松散的意义上是“随机的”,即我希望从算法获得各种可能的输出,其分布模糊地类似于具有指定等级的那组元素上的所有矩阵的分布。

事实上,我实际上并不关心它是否具有等级r,只是它关闭到等级r的矩阵(由Frobenius规范测量)

当手头的设置是实时时,我一直在做以下操作,这完全足以满足我的需求:生成大小为U x m的基质r和{ V x n的{​​1}},其元素独立地从例如正常(0,2)。然后,r是排名为U V'的{​​{1}} x m矩阵(嗯,n,但我认为r的概率很高。

如果我这样做然后再转到二进制/ 1-5,则排名会增加。

通过执行SVD并获取第一个<= r奇异值,也可以获得矩阵的低秩近似。但是,这些值不会出现在所需的集合中,对它们进行舍入将再次提高等级。

This question是相关的,但接受的答案不是“随机的”,另一个答案表明SVD,这在这里不起作用。

我想到的一种可能性是从集合中生成r线性独立的行或列向量,然后通过它们的线性组合得到矩阵的其余部分。不过,我不是很清楚如何获得“随机”线性独立向量,或者在此之后如何以准随机方式组合它们。

(不是说这是超级相关的,但我是在笨拙地做这件事。)


更新:我已经在评论中尝试了EMS建议的方法,这个简单的实现:

r

对小型矩阵很快起作用,但对于10x10 - 它似乎陷入了第6或第7级,至少在数十万次迭代中停滞不前。

看起来这可能会更好地用更好的(即不太平坦的)目标函数,但我不知道它会是什么。


我还尝试了一种简单的拒绝方法来构建矩阵:

r

这适用于例如具有任何等级的10x10二进制矩阵,但不适用于0-4矩阵或具有较低等级的更大二进制数。 (例如,获得排名15的20x20二进制矩阵花了我42,000个拒绝;排名10的20x20,花了120万。)

这显然是因为第一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) 行所跨越的空间太小,我正在采样的空间的一部分,例如, 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 ,在这些情况下。

我们希望第一个r行的范围与有效值集的交集。 因此,我们可以尝试从跨度中采样并寻找有效值,但由于跨度涉及实值系数,它们永远不会找到有效的向量(即使我们进行归一化,例如第一个组件在有效集中)。 / p>

也许这可以表示为整数编程问题,或者什么?

3 个答案:

答案 0 :(得分:2)

我的朋友,上面评论过的丹尼尔约翰逊提出了一个想法,但我发现他从未发布过这个想法。它不是很充实,但你可以适应它。

  

如果A是m-by-r且B是r-by-n且两者都具有等级r,则AB具有等级r。现在,我们只需选择AB,以便AB仅在给定集合中具有值。最简单的情况是S = {0,1,2,...,j}

     

一种选择是使A二进制文件具有适当的行/列总和   这保证了正确的排名和B,列总和增加到否   超过j(以便产品中的每个字词都在S)和行总和   选择导致等级r(或至少鼓励它,因为拒绝可以   使用)。

     

我认为我们可以提出两个独立的抽样方法   AB上的方案比尝试更简单,更快捷   立即攻击整个矩阵。不幸的是,我所有的矩阵   采样代码在另一台计算机上。我知道这很容易概括   允许条目大于{0,1}的条目(即S),但我不能   记住计算如何与m*n一起缩放。

答案 1 :(得分:2)

这样怎么样?

rank = 30
n1 = 100; n2 = 100
from sklearn.decomposition import NMF
model = NMF(n_components=rank, init='random', random_state=0)
U = model.fit_transform(np.random.randint(1, 5, size=(n1, n2)))
V = model.components_
M = np.around(U) @ np.around(V)

答案 2 :(得分:1)

我不确定这个解决方案有多么有用,但你可以构建一个矩阵,允许你在另一个只有0和1作为条目的矩阵上搜索解决方案。如果你在二进制矩阵上随机搜索,它相当于随机修改最终矩阵的元素,但是有可能提出一些比随机搜索更好的规则。

如果您想在元素集 E 上生成m - by - n矩阵,其元素为e i ,{{1你开始使用0<=i<k - by - m矩阵, A

Generator matrix

显然,这个矩阵的等级 m 。现在,您可以构建另一个矩阵B,它在某些位置具有1,以从集合 E 中选择元素。该矩阵的结构是:

Selector matrix

每个 B i k*m - by - k矩阵。那么, A B 的大小为n - by - m rank( A B min(m,rank( B ))。如果我们希望输出矩阵只包含我们的集合 E 中的元素,那么 B i 的每一列必须只有一个元素集到n,其余设置为1

如果你想随机搜索B上的某个等级,你需要从最高等级的有效 B 开始,并旋转一个随机列 j 随机量随机 B i 。这相当于将 A * B 的列 i j 更改为我们的集合中的随机元素,因此它不是一个非常有用的方法。

但是,你可以用矩阵做一些技巧。例如,如果0为2,并且 B 0 B 1 的第一行没有重叠,您可以通过添加这两个子矩阵的第一行来生成线性相关的行。第二行也将线性地依赖于这两个矩阵的行。我不确定这是否会轻易推广到大于2的k,但我相信你可以采用其他技巧。

例如,最多生成排名k的一种简单方法(当km时)是获得随机有效 B 0 ,继续旋转此矩阵的所有行以使 B 1 变为 B m-2 ,将 B m-1 的第一行设置为全1,其余行设置为全0。等级不能小于k+1(假设{ {1}}&gt; k),因为n列只有1个非零元素。矩阵的其余行都是这些行的线性组合(实际上是几乎所有子矩阵的精确副本)。最后一个子矩阵的第一行是第一个子矩阵的所有行的总和,其余的所有行都是零。对于较大的k值,您可以使用 B 0 行的排列,而不是简单的旋转。

一旦你生成了一个满足排名约束的矩阵,你就可以随意改变它的行和列来生成其他矩阵。