如何在C ++中使用固定总和生成随机值

时间:2016-11-28 05:54:08

标签: algorithm random

我想在一行0 to 9范围内生成3个随机数,这些数字应该总计给定的固定数。例如,对于给定的固定和15,一种可能的解决方案是(3, 8, 4)。我怎样才能做到这一点 ?感谢。

3 个答案:

答案 0 :(得分:3)

我们可以:

  1. 首先在a,b,c0
  2. 之间生成随机浮点数1
  3. 获取sum
  4. a,b,c
  5. a,b,c除以sum
  6. 多个a,b,c给定所需的求和整数,然后将a,b,c舍入为最接近的整数
  7. 查看是否sum(a, b, c) == given integer ? get result : try again
  8. 检查此演示:

    使用boost随机生成器:

    #include <iostream>
    #include <time.h>
    #include <iomanip>
    #include <boost/random.hpp>
    
    int main()
    {
        static time_t seed = time(0);
        boost::random::mt19937 RandomNumGen(seed++);
        boost::random::uniform_real_distribution<> Range(0, 1);
    
        int Desired_Integer = 15;
        int Rand_Max = 9;
        int Max_Itr = 100000000;
        int Count = 0;
        int SumABC[3][10] = { 0 };
        float bias = 0.5;
    
        float a, b, c;
        for (int Loop = 1; Loop <= Max_Itr; ++Loop)
        {
            a = Range(RandomNumGen);
            b = Range(RandomNumGen);
            c = Range(RandomNumGen);
    
            float Sum = a + b + c;
            a = a / Sum;
            b = b / Sum;
            c = c / Sum;
    
            //Round to the nearest integer;
            int aI = static_cast<int>(a * Desired_Integer + bias), bI = static_cast<int>(b * Desired_Integer + bias), cI = static_cast<int>(c * Desired_Integer + bias);
            if (aI <= Rand_Max && bI <= Rand_Max && cI <= Rand_Max && aI + bI + cI == Desired_Integer)
            {
                SumABC[0][aI]++;
                SumABC[1][bI]++;
                SumABC[2][cI]++;
    
                Count++;
            }
        }
    
        int PaddingWidth = 10;
        std::cout << "\n" << Count << " in " << Max_Itr << " loops get desired outcome. \nDistribution of a,b,c: \n";
        std::cout << "Number" << std::setw(PaddingWidth) << "a" << std::setw(PaddingWidth) << "b" << std::setw(PaddingWidth) << "c" << std::endl;
        for (int i = 0; i < 10; i++)
        {
            std::cout 
                << i << std::setw(PaddingWidth + 8) 
                << std::setprecision(4) << 100.0 * SumABC[0][i] / (float)Count << std::setw(PaddingWidth) 
                << std::setprecision(4) << 100.0 * SumABC[1][i] / (float)Count << std::setw(PaddingWidth)
                << std::setprecision(4) << 100.0 * SumABC[2][i] / (float)Count << std::endl;
        }
    
        std::cout << "\n\n";
    
        system("pause");
        return 0;
    }
    

    测试效率:

    Test distribution of numbers in 100000000 loops

答案 1 :(得分:2)

在处理随机变量时,检查工作是个不错的主意。

我模拟了两个答案。小涛不仅分布不同,而且分配频率也不同。 aI和bI具有相同的分布,但cI明显不同。这三个应该有相同的分布。

此外,Kay的解决方案具有正确的分布,因为P(a)== 1 s / b 1.25倍P(a)== 1.

这是一个确定性的解决方案,它与Kay的统计数据完全相同

此外,纯粹从0到9的概率POV看每个数字的出现频率是4 / 73,5 /73,6 / 73,7 / 73,8 / 73,9 / 73,10 / 73,9 / 73,8 / 73和7/73

创建总和为15的所有可能数字序列的向量。然后随机选择一个元素。每个数字集具有相同的被选择概率

#include <algorithm>
#include <array>
#include <iostream>
#include <numeric>
#include <random>

using namespace std;

// Your constants:
static constexpr unsigned DICE_COUNT = 3;
static constexpr unsigned DICE_SIDES = 10;
static constexpr unsigned DESIRED_NUMBER = 15;

int main() {
    // Initialize your PRNG:

    vector<array<int, 3>> allLegalNumbers;
    for (int i=0; i <= 9; i++)      // go through all possible sets of 3 numbers from 0 to 9
        for (int ii = 0; ii < DICE_SIDES; ii++)
            for (int iii = 0; iii < DICE_SIDES; iii++)
                if (i + ii + iii == DESIRED_NUMBER) // keep the ones that add up to 15
                    allLegalNumbers.push_back(array<int, 3> {i, ii, iii});

    random_device rd;
    mt19937 generator(rd());
    uniform_int_distribution<unsigned> distribution(0, allLegalNumbers.size() - 1);

    int sum[3][DICE_SIDES]{};
    int sum_count = 0;
    for (int Loop = 1; Loop < 100000000; ++Loop)
    {
        auto index = distribution(generator);
        sum[0][allLegalNumbers[index][0]]++;
        sum[1][allLegalNumbers[index][1]]++;
        sum[2][allLegalNumbers[index][2]]++;
        sum_count++;
    }
    for (int i = 0; i < DICE_SIDES; i++)
        printf("Percent of aI==%d:%5.2f   bI==%d:%5.2f   cI==%d:%5.2f\n",
            i, 100.0*sum[0][i] / sum_count,
            i, 100.0*sum[1][i] / sum_count,
            i, 100.0*sum[2][i] / sum_count);
    return 0;
}
/* Results:
Percent of aI==0: 5.48   bI==0: 5.48   cI==0: 5.48
Percent of aI==1: 6.85   bI==1: 6.85   cI==1: 6.85
Percent of aI==2: 8.22   bI==2: 8.22   cI==2: 8.22
Percent of aI==3: 9.59   bI==3: 9.59   cI==3: 9.59
Percent of aI==4:10.96   bI==4:10.96   cI==4:10.96
Percent of aI==5:12.33   bI==5:12.33   cI==5:12.34
Percent of aI==6:13.69   bI==6:13.70   cI==6:13.70
Percent of aI==7:12.34   bI==7:12.33   cI==7:12.33
Percent of aI==8:10.96   bI==8:10.96   cI==8:10.95
Percent of aI==9: 9.59   bI==9: 9.59   cI==9: 9.58
*/
小涛的回答模拟:注意cI v aI和bI的不同分布

#include <iostream>
int main()
{
    int SumI = 15;
    int Rand_Max = 9;
    float a, b, c;
    int sum[3][10]{};
    int sum_count = 0;

    for (int Loop = 1; Loop < 100000000; ++Loop)
    {

        a = static_cast<float>(rand() % Rand_Max) / static_cast<float>(Rand_Max);
        b = static_cast<float>(rand() % Rand_Max) / static_cast<float>(Rand_Max);
        c = static_cast<float>(rand() % Rand_Max) / static_cast<float>(Rand_Max);

        float Sum = a + b + c;
        a = a / Sum;
        b = b / Sum;
        c = c / Sum;

        //Round to the nearest integer;
        int aI = static_cast<int>(a * SumI + 0.5), bI = static_cast<int>(b * SumI + 0.5), cI = static_cast<int>(c * SumI + 0.5);
        if (aI <= Rand_Max && bI <= Rand_Max && cI <= Rand_Max && aI + bI + cI == SumI)
        {
            sum[0][aI]++;
            sum[1][bI]++;
            sum[2][cI]++;
            sum_count++;
        }
    }
    for (int i = 0; i < 10; i++)
        printf("Percent of aI==%d:%5.2f   bI==%d:%5.2f   cI==%d:%5.2f\n",
            i, 100.0*sum[0][i] / sum_count,
            i, 100.0*sum[1][i] / sum_count,
            i, 100.0*sum[2][i] / sum_count);
    return 0;
}
/* Results:
Percent of aI==0: 5.84   bI==0: 5.83   cI==0: 5.84
Percent of aI==1: 5.30   bI==1: 5.31   cI==1: 5.31
Percent of aI==2: 7.43   bI==2: 7.43   cI==2: 6.90
Percent of aI==3: 9.55   bI==3: 9.54   cI==3: 9.28
Percent of aI==4:10.61   bI==4:10.61   cI==4:10.60
Percent of aI==5:15.64   bI==5:15.66   cI==5:15.39
Percent of aI==6:16.18   bI==6:16.18   cI==6:17.51
Percent of aI==7:11.41   bI==7:11.40   cI==7:10.88
Percent of aI==8: 9.82   bI==8: 9.81   cI==8:10.08
Percent of aI==9: 8.22   bI==9: 8.22   cI==9: 8.22
*/

凯的回答没有出现这个错误。这是模拟:

#include <algorithm>
#include <array>
#include <iostream>
#include <numeric>
#include <random>

// Don't use "using namespace" in production.
// I only use it to avoid the horizontal scrollbar.
using namespace std;

// Your constants:
static constexpr unsigned DICE_COUNT = 3;
static constexpr unsigned DICE_SIDES = 10;
static constexpr unsigned DESIRED_NUMBER = 15;

int main() {
    // Initialize your PRNG:
    random_device rd;
    mt19937 generator(rd());
    uniform_int_distribution<unsigned> distribution(0, DICE_SIDES - 1);

    int sum[3][10]{};
    int sum_count = 0;
    for (int Loop = 1; Loop < 10000000; ++Loop)
    {

        // Fill the array with three random numbers until you have a match:
        array<unsigned, DICE_COUNT> values = { 0 };
        while (accumulate(begin(values), end(values), 0) != DESIRED_NUMBER) {
            for_each(begin(values), end(values), [&](unsigned &v) {
                v = distribution(generator);
                //v = rand() % DICE_SIDES;  // substitute this to use rand()
            });
        }
        sum[0][values[0]]++;
        sum[1][values[1]]++;
        sum[2][values[2]]++;
        sum_count++;
    }
    for (int i = 0; i < 10; i++)
        printf("Percent of aI==%d:%5.2f   bI==%d:%5.2f   cI==%d:%5.2f\n",
            i, 100.0*sum[0][i] / sum_count,
            i, 100.0*sum[1][i] / sum_count,
            i, 100.0*sum[2][i] / sum_count);
    return 0;
}
/* Results:
Percent of aI==0: 5.48   bI==0: 5.48   cI==0: 5.47
Percent of aI==1: 6.85   bI==1: 6.85   cI==1: 6.85
Percent of aI==2: 8.22   bI==2: 8.19   cI==2: 8.22
Percent of aI==3: 9.60   bI==3: 9.59   cI==3: 9.60
Percent of aI==4:10.97   bI==4:10.96   cI==4:10.99
Percent of aI==5:12.34   bI==5:12.32   cI==5:12.32
Percent of aI==6:13.69   bI==6:13.70   cI==6:13.71
Percent of aI==7:12.31   bI==7:12.34   cI==7:12.30
Percent of aI==8:10.95   bI==8:10.96   cI==8:10.95
Percent of aI==9: 9.60   bI==9: 9.60   cI==9: 9.59
*/

答案 2 :(得分:1)

这是一个如何在C ++ 11中生成随机数的教程:http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution

最简单的解决方案是尝试直到找到匹配项:

#include <algorithm>
#include <array>
#include <iostream>
#include <numeric>
#include <random>

// Don't use "using namespace" in production.
// I only use it to avoid the horizontal scrollbar.
using namespace std;

// Your constants:
static constexpr unsigned DICE_COUNT = 3;
static constexpr unsigned DICE_SIDES = 10;
static constexpr unsigned DESIRED_NUMBER = 15;

int main() {
    // Initialize your PRNG:
    random_device rd;
    mt19937 generator(rd());
    uniform_int_distribution<unsigned> distribution(0, DICE_SIDES - 1);

    // Fill the array with three random numbers until you have a match:
    array<unsigned, DICE_COUNT> values = { 0 };
    while (accumulate(begin(values), end(values), 0) != DESIRED_NUMBER) {
        for_each(begin(values), end(values), [&](unsigned &v) {
            v = distribution(generator);
        });
    }

    // Print the result:
    for_each(begin(values), end(values), [&](unsigned &v) {
        cout << v << ' ';
    });
    cout << endl;

    return 0;
}

你需要大约9次迭代才能获得50/50的机会,你将投掷15: