生成垂直于一组给定向量的随机向量
Generate random vector which is perpendicular to a set of given vectors
我有一组成对垂直的向量。然后我想生成一个新的向量,它应该垂直于给定集合中的所有向量,并且应该随机选择。基本上,如果 d
是问题的维数,m
是给定向量的数量,那么垂直于这些向量的向量集跨越 (d-m)
维子空间。我想从这个子空间中随机抽取一个向量。
我可以使用 np.linalg.lstsq
来找到一个垂直于所有其他向量的向量,但结果不会是随机的:
import numpy as np
rng = np.random.default_rng(seed=0)
d = 8 # number of dimensions
vectors = [rng.normal(size=d)]
for _ in range(d-1):
vectors.append(np.linalg.lstsq(
np.stack(vectors, axis=0),
np.zeros(len(vectors)),
rcond=None,
)[0])
import itertools as it
for i, j in it.combinations(range(d), r=2):
assert abs(vectors[i] @ vectors[j]) < 1e-16
所以我认为我应该随机抽样 (d-m)
个组件,然后用 np.linalg.solve
确定其他组件。例如:
import numpy as np
rng = np.random.default_rng(seed=0)
d = 10 # number of dimensions
vectors = [rng.normal(size=d)]
for _ in range(d-1):
random_indices = rng.choice(np.arange(d), size=d-len(vectors), replace=False)
random_mask = np.zeros(d, dtype=bool)
random_mask[random_indices] = True
random_components = rng.normal(size=random_mask.sum())
matrix = np.stack(vectors, axis=0)
other_components = np.linalg.solve(
matrix[:, ~random_mask],
-(matrix[:, random_mask] @ random_components),
)
new = np.empty_like(vectors[-1])
new[random_mask] = random_components
new[~random_mask] = other_components
for v in vectors:
assert abs(v @ new) < 1e-12, f'dot product: {v @ new}'
vectors.append(new)
但是,为了使上述工作正常,我必须根据维数放宽垂直条件 v @ new == 0
。例如,对于 d = 10
,我只能要求 v @ new < 1e-12
,而对于 d = 20
,阈值是 1e-10
。对于最小二乘解,使用与 d
无关的相同阈值就足够了(事实上,所有点积都为零)。
此外,我不确定上述算法(即首先随机抽取待随机化组件的索引,然后确定其他组件)是否会从该子空间中产生真正随机的向量。我该如何验证?
您可以使用 Gram-Schmidt process
迭代地执行此操作
n = 100;
d = 20;
v = np.random.rand(n);
v = v / np.sqrt(np.sum(np.abs(v)**2));
V = [v]
for i in range(d):
v = np.random.rand(n);
# orthogonalize
v = v - sum(vi * np.sum(vi.conj() * v) for vi in V);
# normalize
v = v / np.sqrt(np.sum(np.abs(v)**2))
V.append(v);
A = np.array(V)
np.allclose(A @ A.T, np.eye(d+1)) # check the result
在这个例子中,我从一个空基开始。
如果您已经有了基础并且想要一个向量,您只需重复我在上一次迭代中所做的即可。
我有一组成对垂直的向量。然后我想生成一个新的向量,它应该垂直于给定集合中的所有向量,并且应该随机选择。基本上,如果 d
是问题的维数,m
是给定向量的数量,那么垂直于这些向量的向量集跨越 (d-m)
维子空间。我想从这个子空间中随机抽取一个向量。
我可以使用 np.linalg.lstsq
来找到一个垂直于所有其他向量的向量,但结果不会是随机的:
import numpy as np
rng = np.random.default_rng(seed=0)
d = 8 # number of dimensions
vectors = [rng.normal(size=d)]
for _ in range(d-1):
vectors.append(np.linalg.lstsq(
np.stack(vectors, axis=0),
np.zeros(len(vectors)),
rcond=None,
)[0])
import itertools as it
for i, j in it.combinations(range(d), r=2):
assert abs(vectors[i] @ vectors[j]) < 1e-16
所以我认为我应该随机抽样 (d-m)
个组件,然后用 np.linalg.solve
确定其他组件。例如:
import numpy as np
rng = np.random.default_rng(seed=0)
d = 10 # number of dimensions
vectors = [rng.normal(size=d)]
for _ in range(d-1):
random_indices = rng.choice(np.arange(d), size=d-len(vectors), replace=False)
random_mask = np.zeros(d, dtype=bool)
random_mask[random_indices] = True
random_components = rng.normal(size=random_mask.sum())
matrix = np.stack(vectors, axis=0)
other_components = np.linalg.solve(
matrix[:, ~random_mask],
-(matrix[:, random_mask] @ random_components),
)
new = np.empty_like(vectors[-1])
new[random_mask] = random_components
new[~random_mask] = other_components
for v in vectors:
assert abs(v @ new) < 1e-12, f'dot product: {v @ new}'
vectors.append(new)
但是,为了使上述工作正常,我必须根据维数放宽垂直条件 v @ new == 0
。例如,对于 d = 10
,我只能要求 v @ new < 1e-12
,而对于 d = 20
,阈值是 1e-10
。对于最小二乘解,使用与 d
无关的相同阈值就足够了(事实上,所有点积都为零)。
此外,我不确定上述算法(即首先随机抽取待随机化组件的索引,然后确定其他组件)是否会从该子空间中产生真正随机的向量。我该如何验证?
您可以使用 Gram-Schmidt process
迭代地执行此操作n = 100;
d = 20;
v = np.random.rand(n);
v = v / np.sqrt(np.sum(np.abs(v)**2));
V = [v]
for i in range(d):
v = np.random.rand(n);
# orthogonalize
v = v - sum(vi * np.sum(vi.conj() * v) for vi in V);
# normalize
v = v / np.sqrt(np.sum(np.abs(v)**2))
V.append(v);
A = np.array(V)
np.allclose(A @ A.T, np.eye(d+1)) # check the result
在这个例子中,我从一个空基开始。
如果您已经有了基础并且想要一个向量,您只需重复我在上一次迭代中所做的即可。