如何在满足以下约束的n个元素的数组中分配随机值?

时间:2015-11-05 06:48:28

标签: arrays algorithm math

假设a是我的n元素数组。

约束:

  1. alpha< = a [i]< = beta。
  2. 0< = alpha< = beta< = 1。
  3. a中所有元素的总和应该恰好为1。
  4. 假设对于给定的alpha和beta值,这样的数组总是可行的

    这就是我的想法:

    在alpha和beta之间随机分配值。找到数组元素的总和。 如果sum> 1,则用x减去所有元素,使得满足约束。如果和< 1,则用x添加所有元素,以满足约束。

    但我发现很难找到新映射的值。

3 个答案:

答案 0 :(得分:2)

如果fα×n≤1≤β×n,则可以满足。

你可以通过选择n个随机数来做到这一点。设r i 为i th 随机数,设R为随机数之和,S =(β-alpha)/(1-n×alpha)× R.设置[i] = alpha + r i / S *(beta - alpha)。只要S不小于最大随机数,则a的所有元素都在alpha和beta之间,并且它们的总和为1.

#include <iostream>
#include <cassert>
#include <random>
#include <vector>
#include <algorithm>
#include <iterator>

int main() {
    const double alpha = 0.05, beta = 0.15;
    const int n = 10;

    if (!(n * alpha <= 1.0 && 1.0 <= n * beta)) {
        std::cout << "unable to satisfy costraints.\n";
        return 0;
    }

    if (alpha == beta || std::fabs(1.0 - n * alpha) < 1e-6 || std::fabs(1.0 - n * beta) < 1e-6) {
        std::cout << "Solution for alpha = " << alpha << ", beta = " << beta << " n = " << n << ":\n";
        std::fill_n(std::ostream_iterator<double>(std::cout, " "), n, 1.0/n);
        std::cout << "\nSum: 1.0\n";
        return 0;
    }

    std::vector<int> r;
    double S;

    {
        std::random_device rd;
        std::seed_seq seed{rd(), rd(), rd(), rd(), rd(), rd(), rd(), rd()};
        std::mt19937 eng(seed);
        std::uniform_int_distribution<> dist(0, 1000);

        do {
            r.clear();
            std::generate_n(back_inserter(r), n, std::bind(dist, std::ref(eng)));

            int sum = std::accumulate(begin(r), end(r), 0);
            S = (beta - alpha) / (1 - n * alpha) * sum;
        } while (S < *std::max_element(begin(r), end(r)));

    }

    std::vector<double> a(n);
    std::transform(begin(r), end(r), begin(a), [&] (int ri) { return alpha + ri/S * (beta - alpha); });

    for (double ai : a) {
        assert(alpha <= ai && ai <= beta);
    }

    std::cout << "Solution for alpha = " << alpha << ", beta = " << beta << " n = " << n << ":\n";
    std::copy(begin(a), end(a), std::ostream_iterator<double>(std::cout, " "));
    std::cout << '\n';
    std::cout << "Sum: " << std::accumulate(begin(a), end(a), 0.0) << '\n';
}
  

α= 0.05的解,β= 0.15,n = 10:
  0.073923 0.117644 0.0834555 0.139368 0.101696 0.0846471 0.115261 0.0759395 0.0918882 0.116178
  总和:1

你没有指定你希望算法提供的特定分布,但我想我会指出这个算法不一定能以相同的概率产生所有可能的解决方案;我相信有些解决方案比其他解决方案更容易产生。

答案 1 :(得分:2)

(正如我在评论中提到的,这里有一个隐藏的要求。如果我们查看数字范围,至少应该有一个元素<= (1/n),否则总和将是> 1。所以我们必须强制alpha <= 1/n。同样我们需要强制beta >= 1/n。)

我们可以做到以下几点:

  1. x_i = 0允许i = 0
  2. [x_i, C]中选择一个随机数。设为x_(i+1)。我们将在下面描述这个常量C
  3. 执行上述n-1次以获得{x_1, x_2, ...., x_(n-1)}
  4. 现在,数字{(x_1 - x_0), ..., (1 - x_(n-1))}为正数,总和为C。这些是{y_1, y_2, ..., y_n}
  5. 现在考虑k_i = alpha + y_i * (beta - alpha)。由于y_i位于(0, C)k_i如果alphabeta也会介于C <= 1k_i之间。
  6. n * alpha + (beta - alpha) * (sum of y_i)的总和为n * alpha + (beta - alpha) * C。这与1相同。这应该等于C
  7. 解决C = (1 - n * alpha) / (beta - alpha),我们得到alpha = 0.4

    说明: 如果beta = 1n = 2C = (1 - 0.8) / 0.6 = 1/3,我们会: x_i

    {1/8}的随机值是:(n-1) = 1(因为我们只关心y_i = {1/8, (1/3 - 1/8)} = {1/8, 5/24}

    beta - alpha = 0.6

    k_i = {0.4 + 0.6/8, 0.4 + 3/24} = {0.475, 0.525}

    {0.475 and 0.525}

    因此,所需数组为1,其总和为C < 1

    这里的基本假设是C < 1。我认为我们可能能够证明(0, C)是对问题可行性的强烈要求,但现在为时已经来不及证明这一点。

    请注意,上面会产生一致的随机数,因为数字x_i是在{{1}}范围内随机选择的。

答案 2 :(得分:1)

我意识到问题本身并不是很简单,但我发现一个简单的算法,我认为(尚未证明)将在所有可能的值上生成均匀分布。我想这个算法必定已经被某些人考虑过了,因为它非常简单,但我还没有尝试过寻找这种算法的分析。算法如下:

  1. 从1到n
  2. 迭代
  3. 在每一步中,通过假设此点之后的值将具有最大值(针对下限)或最小值(针对上限)来找出真实的下限和上限。
  4. 在边界之间生成均匀分布的数字(如果没有满足边界的数字,则约束不可满足)
  5. 将生成的数字添加到数组
  6. 最后,将生成的数组洗牌
  7. 代码:

    # -*- coding: utf-8 -*-
    """
    To produce an array of random numbers between alpha and beta, which sums to 1
    Assumption: there is always a solution for the given constraint.
    """
    
    # Import statements
    import random
    import sys
    
    def shuffle(arr):
        result = []
        while len(arr) > 0:
            idx = random.randint(0, len(arr)-1)
            result.append(arr[idx])
            del(arr[idx])
        return result
    
    def main():
        if len(sys.argv) < 4:
            print('Usage: random_constrained.py <n> <alpha> <beta>\nWhere:\n\tn is an integer\n\t0 <= alpha <= beta <= 1')
            sys.exit(0)
        n = int(sys.argv[1])
        alpha = float(sys.argv[2])
        beta = float(sys.argv[3])
        result = []
        total = 0
        for i in range(n):
            low = max(alpha, 1-total-((n-i-1)*beta))
            high = min(beta, 1-total-((n-i-1)*alpha))
            if high < low - 1e-10:
                print('Error: constraint not satisfiable (high={:.3f}, low={:.3f})'.format(high, low))
                sys.exit(1)
            num = random.uniform(low, high)
            result.append(num)
            total += num
        result = shuffle(result)
        print(result)
    
    if __name__ == '__main__':
        main()
    

    示例输出:

    $ python random_constrained.py 4 0 0.5
    [0.06852504971359885, 0.39391285249108765, 0.24215492185626314, 0.2954071759390503]
    $ python random_constrained.py 4 0 0.5
    [0.2519926400714304, 0.4138640296394964, 0.27906367876610466, 0.055079651522968565]
    $ python random_constrained.py 4 0 0.5
    [0.11505150404455633, 0.16665881845206237, 0.45371668123772924, 0.264572996265652]
    $ python random_constrained.py 4 0 0.5
    [0.31689744182294444, 0.11233051635974067, 0.3599600067081529, 0.21081203510916197]
    $ python random_constrained.py 4 0 0.5
    [0.16158825078700828, 0.18989326608974527, 0.1782112102703714, 0.470307272852875]
    
    $ python random_constrained.py 5 0 0.2
    [0.19999999999999998, 0.2, 0.19999999999999996, 0.19999999999999996, 0.20000000000000004]
    $ python random_constrained.py 5 0 0.2
    [0.2, 0.2, 0.19999999999999998, 0.2, 0.19999999999999996]
    $ python random_constrained.py 5 0 0.2
    [0.20000000000000004, 0.19999999999999998, 0.19999999999999996, 0.19999999999999998, 0.2]
    $ python random_constrained.py 5 0 0.2
    [0.2, 0.20000000000000004, 0.19999999999999996, 0.19999999999999996, 0.19999999999999996]
    
    $ python random_constrained.py 2 0.4 1
    [0.5254259945319483, 0.47457400546805173]
    $ python random_constrained.py 2 0.4 1
    [0.5071103628251259, 0.4928896371748741]
    $ python random_constrained.py 2 0.4 1
    [0.4595236988530377, 0.5404763011469623]
    $ python random_constrained.py 2 0.4 1
    [0.44218002983240046, 0.5578199701675995]
    $ python random_constrained.py 2 0.4 1
    [0.4330169754142243, 0.5669830245857757]
    $ python random_constrained.py 2 0.4 1
    [0.543183373724851, 0.45681662627514896]
    

    我还不确定如何处理那里的精度错误。

    我运行了一些测试以检查n=2的情况下的一致性,生成100000个数组(使用alpha=0.4, beta=0.6)并将值从alpha到beta的10个相同大小的桶中获取,计算出现的次数:

    First number: [9998, 9966, 9938, 9952, 10038, 10161, 9899, 10007, 10054, 9987]
    Second number: [9987, 10054, 10007, 9899, 10161, 10038, 9952, 9938, 9966, 9998]
    

    对于n=4, alpha=0, beta=0.3,10000次尝试:

    [0, 0, 0, 304, 430, 569, 730, 1135, 1874, 4958]
    [0, 0, 0, 285, 492, 576, 805, 1113, 1775, 4954]
    [0, 0, 0, 248, 465, 578, 769, 1077, 1839, 5024]
    [0, 0, 0, 252, 474, 564, 800, 1100, 1808, 5002]
    

    我们可以看到每个数字的分布大致相同,因此对任何位置都没有偏见。