如何生成条目为 Uniform 的 Rank 5 矩阵?

How to generate a Rank 5 matrix with entries Uniform?

我想用从 np.random.uniform(0, 20) 采样的所有条目在 numpy 中生成一个 5 阶 100x600 矩阵,以便所有条目均匀分布在 [0, 20) 之间。 python 中最好的方法是什么?

我看到这里有一种受 SVD 启发的方法 (https://math.stackexchange.com/questions/3567510/how-to-generate-a-rank-r-matrix-with-entries-uniform),但我不确定如何对其进行编码。 我正在寻找这种受 SVD 启发的方法的工作示例,以获取均匀分布的条目。

实际上,我通过垂直堆叠五个 20x100 秩 1 矩阵,然后打乱垂直索引,设法编写了一个 5 秩 100x100 矩阵。但是,生成的 100x100 矩阵没有均匀分布的条目 [0, 20).

这是我的代码(我最好的尝试):

import numpy as np
def randomMatrix(m, n, p, q):
    # creates an m x n matrix with lower bound p and upper bound q, randomly.
    count = np.random.uniform(p, q, size=(m, n))
    return count

Qs = []
my_rank = 5
for i in range(my_rank):
  L = randomMatrix(20, 1, 0, np.sqrt(20))
  # L is tall
  R = randomMatrix(1, 100, 0, np.sqrt(20)) 
  # R is long
  Q = np.outer(L, R)
  Qs.append(Q)

Q = np.vstack(Qs)
#shuffle (preserves rank 5 [confirmed])
np.random.shuffle(Q)

这不是一个完美的解决方案,我必须承认。但它很简单,而且非常接近。
我创建了 5 个向量,它们将跨越矩阵的 space,并创建随机线性组合来填充矩阵的其余部分。 我最初的想法是,一个简单的解决方案是将这些向量复制 20 次。
为了改进这一点,我创建了它们的线性组合,权重从均匀分布中提取,但随后矩阵中条目的分布变得正常,因为加权平均值基本上导致中心极限定理生效。
琐碎的方法和第二种不起作用的方法之间的中间点是使用有利于其中一个向量而不是其他向量的权重集。您可以通过将任何向量传递给具有适当高温参数的 softmax 函数来生成这些类型的权重向量。
分布几乎是均匀的,但向量仍然非常接近基向量。您可以使用温度参数来找到适合您目的的最佳点。

from scipy.stats import ortho_group
from scipy.special import softmax
import numpy as np
from matplotlib import pyplot as plt
N    = 100
R    = 5
low  = 0
high = 20
sm_temperature = 100

p       = np.random.uniform(low, high, (1, R, N))
weights = np.random.uniform(0, 1, (N-R, R, 1))
weights = softmax(weights*sm_temperature, axis = 1)
p_lc    = (weights*p).sum(1)

rand_mat = np.concatenate([p[0], p_lc])

plt.hist(rand_mat.flatten())

我无法接受我以前的解决方案(“选择”方法)并没有真正产生严格均匀分布的条目,但有时只是接近到足以欺骗统计测试。然而,渐近情况几乎肯定不会均匀分布。但我确实想到了另一个同样糟糕的疯狂想法,但以另一种方式 - 它并不是真正随机的。
在这个解决方案中,我做的 smth 类似于 OP 形成秩为 1 的 R 矩阵然后连接它们的方法,但略有不同。我创建每个矩阵的方法是将一个基向量堆叠在自身顶部乘以 0.5,然后将它们堆叠在同一个基向量上,偏移均匀分布的动态范围的一半。这个过程继续乘以三分之一、三分之二和 1,然后移动等等,直到我在矩阵的那部分有所需的向量数。
我知道这听起来难以理解。但是,不幸的是,我找不到更好的解释方法。希望通过阅读代码可以得到更多启示。
希望这种“阶梯式”的方法更靠谱,更有用。

import numpy as np 
from matplotlib import pyplot as plt

'''
params:
    N    - base dimention
    M    - matrix length
    R    - matrix rank
    high - max value of matrix
    low  - min value of the matrix
'''
N    = 100
M    = 600
R    = 5
high = 20
low  = 0

# base vectors of the matrix
base = low+np.random.rand(R-1, N)*(high-low)

def build_staircase(base, num_stairs, low, high):
    '''
    create a uniformly distributed matrix with rank 2 'num_stairs' different 
    vectors whose elements are all uniformly distributed like the values of 
    'base'.
    '''
    l = levels(num_stairs)
    vectors = []
    for l_i in l:
        for i in range(l_i):
            vector_dynamic = (base-low)/l_i
            vector_bias    = low+np.ones_like(base)*i*((high-low)/l_i)
            vectors.append(vector_dynamic+vector_bias)
    return np.array(vectors)


def levels(total):
    '''
    create a sequence of stritcly increasing numbers summing up to the total.
    '''
    l = []
    sum_l = 0
    i = 1
    while sum_l < total:
        l.append(i)
        i +=1
        sum_l = sum(l)
    i = 0
    while sum_l > total:
        l[i] -= 1
        if l[i] == 0:
            l.pop(i)
        else:
            i += 1
        if i == len(l):
            i = 0
        sum_l = sum(l)
    return l
        
n_rm = R-1 # number of matrix subsections
m_rm = M//n_rm
len_rms = [ M//n_rm for i in range(n_rm)]
len_rms[-1] += M%n_rm
rm_list = []
for len_rm in len_rms:
    # create a matrix with uniform entries with rank 2
    # out of the vector 'base[i]' and a ones vector.
    rm_list.append(build_staircase(
        base = base[i], 
        num_stairs = len_rms[i], 
        low = low,
        high = high,
    ))

rm = np.concatenate(rm_list)
plt.hist(rm.flatten(), bins = 100)

举几个例子:

现在 N = 1000,M = 6000 以凭经验证明近渐近行为: