仔细阅读上一个问题 Random numbers that add to 100: Matlab
我正在努力解决类似但稍微复杂的问题。
我想创建一个由n个元素组成的数组,总和为1,但我想要一个附加约束,即每个元素的最小增量(或者如果你喜欢有效数字的数量)是固定的。
例如,如果我希望10个数字总和为1而没有任何约束,则以下工作完美:
num_stocks=10;
num_simulations=100000;
temp = [zeros(num_simulations,1),sort(rand(num_simulations,num_stocks-1),2),ones(num_simulations,1)];
weights = diff(temp,[],2);
我愚蠢地认为通过缩放这个我可以添加约束如下
num_stocks=10;
min_increment=0.001;
num_simulations=100000;
scaling=1/min_increment;
temp2 = [zeros(num_simulations,1),sort(round(rand(num_simulations,num_stocks-1)*scaling)/scaling,2),ones(num_simulations,1)];
weights2 = diff(temp2,[],2);
然而,尽管这适用于n&的小值。小的增量值,例如n = 1,000&增量为0.1%,然后在大量试验中,第一个和最后一个数字的平均值始终低于0.1%。
我确信这有一个合乎逻辑的解释/解决方案,但我一直在撕扯我的头发尝试&找到它&我想知道任何人都会如此友善地指出我正确的方向。将问题置于上下文中创建随机股票投资组合(因此总和为1)。
提前致谢
感谢您到目前为止的回复,只是为了澄清(因为我认为我的初步问题可能是非常严厉的措辞),它的权重固定增量为0.1%,因此0%,0.1%,0.2%等。
我最初尝试使用整数
num_stocks=1000;
min_increment=0.001;
num_simulations=100000;
scaling=1/min_increment;
temp = [zeros(num_simulations,1),sort(randi([0 scaling],num_simulations,num_stocks-1),2),ones(num_simulations,1)*scaling];
weights = (diff(temp,[],2)/scaling);
test=mean(weights);
但是这更糟糕,第一和第二的平均值最后的权重远低于0.1%.....
编辑以反映Floris&澄清
我用来解决这个问题的原始代码(在找到这个论坛之前)是
function x = monkey_weights_original(simulations,stocks)
stockmatrix=1:stocks;
base_weight=1/stocks;
r=randi(stocks,stocks,simulations);
x=histc(r,stockmatrix)*base_weight;
end
这个速度非常快,考虑到我想要运行总计10,000,000次模拟,1000次模拟的10,000次模拟仅需2秒多一次,单核心和我使用并行工具箱在8核机器上运行整个代码。
它还提供了我在平均值方面所寻求的分布,而且我认为同样可以获得100%在1股票中的投资组合,因为对于geta投资组合来说,每个投资组合都是0.1%股票(虽然我很乐意得到纠正)。
我的问题是,尽管它适用于1,000种股票和增量为0.1%,我猜它适用于100种股票和增加1%,随着股票数量减少,然后每个股票变成一个非常大的百分比(在极端情况下,有两只股票你将总是获得50/50的投资组合)。
实际上我觉得这个解决方案就像Floris建议的二项式解决方案(但更有限)
但是我的问题很复杂,因为我想让我的方法变得更加灵活。有可能说3股票&我当前代码无法正确处理的增量为1%,因此我如何在stackoverflow上遇到原始问题偶然发现
Floris的递归方法将得到正确答案,但考虑到问题的严重程度,速度将是一个主要问题。
原始研究的一个例子就在这里
http://www.huffingtonpost.com/2013/04/05/monkeys-stocks-study_n_3021285.html
我目前正在努力扩展它,在投资组合权重方面具有更大的灵活性。索引中的股票数量,但它似乎是我的编程&概率论能力是一个限制因素.......
答案 0 :(得分:2)
我可以看到的一个问题是你的公式允许数字为零 - 当舍入操作导致两个连续数字在排序后相同时。不确定你是否认为这是一个问题 - 但我建议你考虑一下(这意味着你的模型投资组合中存在的股票数量少于N股,因为其中一只股票的贡献为零)。
另外需要注意的是,在您的发行版中获得极值的概率是您希望它们的一半:如果您有均匀分布的数字,从0到1000,并且round
,舍入0
的数字位于[0 0.5>
区间;那些围绕1
的人来自[0.5 1.5>
- 两倍大。最后一个数字(舍入到1000
)再次来自较小的区间:[999.5 1000]
。因此,您不会像您想象的那样经常获得第一个和最后一个数字。如果您使用round
代替floor
,我认为您会得到您期望的答案。
修改强>
我更多地考虑了这一点,并提出了一个缓慢但(我认为)准确的方法来做到这一点。基本思路是:
(N/m)
的二项分布变量的算法 - 调用第一个值v1
;然后将剩余的时间间隔N - v1
分成m-1
个步骤;我们可以递归地这样做。以下代码实现了这一点:
% random integers adding up to a definite sum
function r = randomInt(n, limit)
% returns an array of n random integers
% whose sum is limit
% calls itself recursively; slow but accurate
if n>1
v = binomialRandom(limit, 1 / n);
r = [v randomInt(n-1, limit - v)];
else
r = limit;
end
function b = binomialRandom(N, p)
b = sum(rand(1,N)<p); % slow but direct
要获得10000个实例,请按以下步骤运行:
tic
portfolio = zeros(10000, 10);
for ii = 1:10000
portfolio(ii,:) = randomInt(10, 1000);
end
toc
这在一台适度的机器上运行3.8秒(单线程) - 当然,获得二项分布随机变量的方法是减缓它的速度;有统计工具箱具有更高效的功能,但我没有。如果您增加粒度(例如,通过设置limit=10000
),它将减慢更多,因为您增加了生成的随机数样本的数量;使用limit = 10000
上述循环需要13.3秒才能完成。
作为测试,我发现mean(portfolio)'
和std(portfolio)'
如下(使用limit=1000
):
100.20 9.446
99.90 9.547
100.09 9.456
100.00 9.548
100.01 9.356
100.00 9.484
99.69 9.639
100.06 9.493
99.94 9.599
100.11 9.453
这看起来像是一个非常有说服力的“扁平”分发给我。我们希望数字以二进制方式分布,平均值为100,标准偏差为sqrt(p*(1-p)*n)
。在这种情况下,p=0.1
我们期待s = 9.4868
。我实际得到的价值再次非常接近。
我意识到这对limit
的大值是低效的,我没有尝试提高效率。我发现当你开发新东西时,清晰度胜过速度。但是,例如,您可以预先计算p=1./(1:10)
的累积二项分布,然后进行随机查找;但如果你打算这样做一次,对于10万个实例,它会在不到一分钟内运行;除非你打算多次这样做,否则我不会打扰。但如果有人想要改进这些代码,我很乐意听取他们的意见。
答案 1 :(得分:2)
最终我解决了这个问题!
我在约翰霍普金斯大学找到了一篇由两位学者撰写的论文“统一单元采样” http://www.cs.cmu.edu/~nasmith/papers/smith+tromble.tr04.pdf
在论文中,他们概述了天真算法是如何不起作用的,其方式与木片非常相似,回答了随机数增加到100个问题。然后他们继续表明David Schwartz建议的方法也可能略有偏差,并提出一种似乎有效的修改算法。
如果你想要x和y的总和
我花了一段时间才意识到为什么当最初的方法没有时它会起作用并且归结为获得零重量的概率(正如Floris在他的回答中所强调的那样)。为了获得原始版本的零重量,除了第一个或最后一个权重之外,你的随机数必须具有相同的2个值,但是对于第一个和第一个权重。最后的那些随机数为零或最大数量将导致零权重,这更可能。 在修改后的算法中,零&amp;最大数量不在随机选择集中。只有当您选择两个连续的数字时,才会出现零权重,这对于每个职位都是一样的。
我在Matlab中将其编码如下
function weights = unbiased_monkey_weights(num_simulations,num_stocks,min_increment)
scaling=1/min_increment;
sample=NaN(num_simulations,num_stocks-1);
for i=1:num_simulations
allcomb=randperm(scaling+num_stocks-1);
sample(i,:)=allcomb(1:num_stocks-1);
end
temp = [zeros(num_simulations,1),sort(sample,2),ones(num_simulations,1)*(scaling+num_stocks)];
weights = (diff(temp,[],2)-1)/scaling;
end
显然循环有点笨重,因为我正在使用2009版本,randperm函数只允许你生成整个集合的排列,但是尽管如此,我可以在5秒内在我的笨重上运行10,000个模拟1000个数字笔记本电脑足够快。
平均重量现在是正确的&amp;作为一个快速测试我复制木片产生3个数字,总和为1,最小增量为0.01%&amp;它也看起来正确
谢谢大家的帮助,我希望这个解决方案对未来的其他人有用
答案 2 :(得分:1)
简单的答案是使用没有最小增量的方案,然后转换问题。一如既往,要小心。有些方法不会产生统一的数字集。
因此,假设我想要总数为100的11个数字,最小增量为5的约束。我首先会找到总数为45的11个数字,样本上没有下限(除了零。)I可以使用文件交换中的工具。最简单的是在区间[0,45]中简单地采样10个数字。对它们进行排序,然后找出差异。
X = diff([0,sort(rand(1,10)),1]*45);
向量X是总和为45的数字样本。但向量Y总和为100,最小值为5.
Y = X + 5;
当然,如果您希望找到具有给定约束的多组数字,这可以简单地进行矢量化。