三个相互不相等的随机数?

时间:2014-02-16 15:58:58

标签: c++ c random logic

我能够在0到25的范围内生成三个随机整数。但是我必须确定这三个整数应该总是具有不同的值,即i != jj != kk != i。我已经制作了以下代码,但我担心它不会满足条件,也可能导致无限循环条件。

int i = ts_rint (25); int j = ts_rint (25); int k = ts_rint (25);

    while ( k == i)
    {
        k = ts_rint (25);

        while ( i == j )
        {
            i = ts_rint (25);

            while ( j == k )
            {
                j = ts_rint (25);
            }
        }
    }

我正在使用TScope这是一个C库来创建认知实验。您可以查看here以查看ts_rint(int nMax)

我的代码在此条件下失败了:

原文:i = 9 j = 9 k = 11

While While循环i:9 j:9 k:11

所以,这个while循环系统非常容易失败。

8 个答案:

答案 0 :(得分:3)

这称为拒绝抽样。对于以下情况,这是一个很好的解决方案:

  • 拒绝是罕见的,不太可能花费太多时间。
  • 不需要计算时间的确定性限制。
  • 难以直接计算样本而不会被拒绝。

这个循环在理论上确实是无界限的,但是,在大多数情况下实际上,这不会是一个问题。

没有必要重新取样所有三个数字。你可以用这个:

int i = ts_rint(25), j, k;
do j = ts_rint(25); while (i == j);
do k = ts_rint(25); while (k ==i || k == j);

在这种情况下,我们只拒绝部分样本。 (样本是数字的三重组合。我们一次只拒绝一个。)在这种情况下有效。然而,随着更复杂的概率分布,有必要确保算法的结构产生所需的分布。

编辑:正如其他答案所指出的那样,有一些方法可以生成所需的三元组而不会出现循环。 (因此,违反了上述第三个标准;拒绝采样通常不应用于此问题。)但是,正如其他答案和众多编辑所证明的那样,它们似乎容易出错。所以仔细检查一下代码。

现在其他答案已经有所缓和,我决定玩得开心。让我们直接按升序生成三个数字(不带任何分支)。我们可以使用以下代码执行此操作:

int p = ts_rint(25*24*23/6);
complex double t =
    cpow(372600 - 324*p + 3*csqrt(11664.*p*p-26827200.*p-80590467), 1/3.);
int j = (50 - t/3 - 1729/t - csqrt(-3)*(t/3-1729/t))/4 + 0x1p-20;
int r = p - (69*j - 79 - 2*j*j) * j / 6;
int i = r%j;
int k = r/j;

不幸的是,我现在没时间了,所以详细解释将等待另一天。简而言之,我们将样本编号从0到2299(2300个样本,25•24•23/6)。我们知道 j •( n - j -1)样本有 j 作为它们的中间值(因为有 j 可用于第一个值的较低数字和 n - j -1可用于第三个值的较高数字)。然后我们求和,以确定有多少样本的中间值小于或等于特定的 j 。然后我们将该总和设置为等于样本数,并将 j 求解为样本数的函数。这允许我们从ts_rint(25*24*23/6)的结果直接确定 j 。找到 j 后, i k 就会轻松跟进。有趣的部分是sum是一个三次多项式,因此它的解决方案使用复杂的算法。

代替进一步推导,这是一个证明正确性的程序:

#include <complex.h>
#include <math.h>
#include <stdio.h>


#define N   25


int main(void)
{
    int A[N][N][N] = {{{0}}};

    //  First, mark each of the 2300 results we ought to find.
    for (int i = 0;   i < N; ++i)
    for (int j = i+1; j < N; ++j)
    for (int k = j+1; k < N; ++k)
        ++A[i][j][k];

    /*  Next, iterate through each value that ts_rint(25*24*23/6) might return,
        generate a triple for each value, and test for whether we generate each
        required triple exactly once.
    */
    for (int p = 0; p < 25*24*23/6; ++p)
    {
        complex double t =
            cpow(372600 - 324*p + 3*csqrt(11664.*p*p-26827200.*p-80590467), 1/3.);
        int j = (50 - t/3 - 1729/t - csqrt(-3)*(t/3-1729/t))/4 + 0x1p-20;
        int r = p - (69*j - 79 - 2*j*j) * j / 6;
        int i = r%j;
        int k = r/j;

        /*  Given triple (i, j, k), is it one we are supposed to generate, and
            have we not seen it before?
        */
        if (A[i][j][k] != 1)
        {
            printf("Error, A[%d][%d][%d] = %d, p = %d.\n",
                i, j, k, A[i][j][k], p);
            return 1;
        }
        A[i][j][k] = 0;
    }

    return 0;
}

答案 1 :(得分:3)

您的代码有效。

作为替代方案,您可以使用:

int i = ts_rint(25);
int j = ts_rint(24);
int k = ts_rint(23);
j += j >= i;
k += k >= std::min(i, j);
k += k >= std::max(i, j);

答案 2 :(得分:1)

如果你真的只需要3个随机整数,你可以这样做:

    k = rint (25);

    // only 24 values remaining for i
    i = rint (24);
    // remap k to 24
    if (i == k)
        i = 24;

    // only 23 values remaining for j
    j = rint (23);
    // remap k to 25 (or 24 if it's taken)
    if (j == k)
        j = (i == 24) ? 23 : 24;
    // remap i to 24
    else if (j == i)
        j = (k == 24) ? 23 : 24;

如果您需要将其缩放到更多值,最好创建一个包含所有可能值的向量,并在随机选择时从中删除值。

答案 3 :(得分:1)

我想解释如何有三个随机数而没有它们可以相等的可能性,我不会给出任何代码; 你需要从没有前一个数字的0到25区间生成随机数,例如:当你生成i时你需要从0到25的间隔中提取k (抱歉英语不好)

答案 4 :(得分:1)

int k = rint(25 * 24 * 23);
int i = k / (24 * 23);
k %= 24 * 23;
int j = k / 23;
k %= 23;

if (j >= i)
{
    ++j;
    if (k >= i)
        ++k;
    if (k >= j)
        ++k;
}
else
{
    if (k >= j)
        ++k;
    if (k >= i)
        ++k;
}

答案 5 :(得分:1)

修改:此解决方案有误,需要进一步改进,请参阅下面的评论。

没有循环的简单解决方案:

srand(time(0));

int i = rand() % 25;
int j = rand() % 25;
int k = rand() % 25;

if (i == j) j++;
if (i == k) k++;
if (j == k) k++;

(对不起rand(),这只是为了简洁起见)

答案 6 :(得分:1)

您是否考虑过将您的解决方案基于Fisher-Yates shuffle?以下在O(k)时间内生成k元组,并保证具有相同概率被选择的所有元素的不同结果。如果你需要生成多个k元组,则不需要重新初始化数组 - 改组不依赖于初始排序来产生均匀分布的结果。

#include <stdlib.h>
#include <stdio.h>

#define NUM_VALUES 26

static int values[NUM_VALUES] =
    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25};

void generate(int n) {
    for(int i = NUM_VALUES - 1; i > NUM_VALUES - (n + 1); --i) {
        int j = arc4random_uniform(i+1);
        if (i != j) {
            int tmp = values[i];
            values[i] = values[j];
            values[j] = tmp;
        }
        printf("%d ", values[i]);
    }
    printf("\n");
}

int main(void) {
    for(int i = 0; i < 100000; ++i) {
        generate(3);
    }
}

这可能会变得更干净,我的C生锈了。我的编译器似乎没有ts_rint()所以我使用arc4random_uniform()代替,但我认为这是替换它的简单替代。

答案 7 :(得分:0)

编辑:这是一个错误的答案,在运气不好的情况下无法产生正确的结果

int i, j, k;

i = ts_rint( 25 );     /*  casually get a random number with 1/26 probability  */
                    /*  you're done with the i                              */

j = ts_rint( 24 );     /*  get a random number with 1/25 probability      */
                    /*  now, this number has the right probability,    */
                    /*  but it was not to be picked from [0, 24]       */
                    /*  rather from [0, 25] \ { i } or in other words  */
                    /*  [0, i - 1] union [i + 1, 25], so...            */
if ( j >= i ) j++;  /*  <<< this will achieve that                     */


k = ts_rint( 23 );     /*  same story, two fold  */
if ( k >= i ) k++;
if ( k >= j ) k++;

然后,所有的解释都作为评论给出。

编辑:k >= ik >= j 的最后一行不正确