C - 生成无重复的随机序列而不需要改组

时间:2016-09-23 15:02:47

标签: c algorithm random

我希望以随机顺序生成序列[0...1'000'000]的数组而不会改组

这意味着我不想这样做:

int arr[1000000];

for (int i = 0; i < 1000000; i++)
{
    arr[i] = i; 
}

shuffle(arr);
shuffle(arr);

我想弄清楚如何在没有“黑盒子”shuffle功能的情况下做到这一点。我也不想在11'000'000之间随机选择索引,因为在999'999号时,只有1/1'000'000个机会继续。

我一直在想一个解决方案,我认为关键是并行数组并向后循环然后使用模数来限制你尚未去过的索引,但是我不能保证我得到的价值是独一无二的。

我也不想使用HashSet或TreeSet实现。

2 个答案:

答案 0 :(得分:2)

这可以在O(n)时间内完成,有两个列表,一个按顺序编号(initialy),另一个按结果顺序。

您可以在源列表中按顺序开始使用n元素。然后选择一个随机数mod n。这将为您提供下一个元素,您将其置于目标列表中。

现在是关键部分。如果您每次选择0n-1之间的随机数,就像您认为随机播放一样,您选择之前选择的数字的机会就越大。那你怎么处理这个?通过减少可供选择的号码列表。

在源列表中,选择一个数字后,将列表的最后一个元素移动到刚刚使用的索引。您现在有一个可供选择的n-1个数字列表。因此,在下一次迭代中,您将采用随机数mod n-1。继续,直到你的源列表只有一个元素。

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

#define LEN 10

int main()
{
    int a[LEN], b[LEN];
    int i, val;
    int count = LEN;

    srand(time(NULL));

    for (i=0;i<LEN;i++) {
        a[i]=i+1;
    }
    for (i=0;i<LEN;i++) {
        val = rand() % count;
        b[i] = a[val];
        a[val] = a[count-1];
        count--;
    }
    for (i=0;i<LEN;i++) {
        printf("%d ", b[i]);
    }
    printf("\n");

    return 0;
}

编辑:

这是一个稍微高效的版本,不使用两个数组,因此O(1)空间:

int a[LEN];
int i, val, tmp;

srand(time(NULL));

for (i=0;i<LEN;i++) {
    a[i]=i+1;
}
for (i=0;i<LEN-1;i++) {
    val = (rand() % (LEN - 1 - i)) + i + 1;
    tmp = a[i];
    a[i] = a[val];
    a[val] = tmp;
}
for (i=0;i<LEN;i++) {
    printf("%d ", a[i]);
}
printf("\n");

答案 1 :(得分:1)

O(N)答案很好但是这里是使用二进制搜索和binary indexed tree在O(NlogN)中执行此操作的替代方法。

arr = []
N = 1000,000
for i from 0 to N-1
     low = 0
     high = N-1
     mid = (low+high)/2
     while low < high
         if full(low,mid)
             low = mid+1
         else if full(mid+1,high)
             high = mid
         else
             if rand() < 0.5
                  low = mid+1
             else
                  high = mid
     mark(low) // marking the element in binary indexed tree
     arr[i] = low

使用二进制索引树实现函数full,并检查给定范围内的所有元素是否都已标记。 markfull都具有O(logN)复杂度。