Я хотел бы сгенерировать матрицы размера m
x n
и ранга r
с элементами, поступающими из заданный конечный набор, например {0,1}
или {1,2,3,4,5}
. Я хочу, чтобы они были «случайными» в каком-то очень широком смысле этого слова, т. Е. Я хочу получить множество возможных выходов из алгоритма с распределением, отдаленно похожим на распределение всех матриц по этому набору элементов с указанным рангом.
На самом деле меня не волнует, что она имеет ранг r
, просто она близка к матрице ранга r
(, измеряемой нормой Фробениуса ).
Когда под рукой имеется множество вещественных чисел, я делаю следующее, что вполне соответствует моим потребностям :генерирую матрицы U
размера m
x r
и V
из n
x r
, с элементами, взятыми независимо, например, из Нормальный (0, 2 ). Тогда U V'
— это матрица m
x n
рангаr
(ну, <= 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 матриц или гораздо больших двоичных матриц с более низким рангом. (Например, для получения бинарной матрицы 20x20 ранга 15 мне потребовалось 42 000 отказов; с 20х20 10 ранга ушло 1,2 млн.)
Очевидно, это связано с тем, что пространство, занимаемое первыми r
строками, является слишком малой частью пространства, из которого я делаю выборку, например. {0,1}^10
, в этих случаях.
Нам нужно пересечение диапазона первых r
строк с набором допустимых значений. Таким образом, мы могли бы попробовать выполнить выборку из диапазона и найти допустимые значения,но поскольку диапазон включает в себя реальные -значения коэффициентов, которые никогда не найдут нам действительные векторы (, даже если мы нормализуем так, что, например. первый компонент находится в допустимом наборе ).
Может быть, это можно сформулировать как задачу целочисленного программирования или что-то в этом роде?