Algorithm 随机排列

Algorithm 随机排列,algorithm,random,permutation,Algorithm,Random,Permutation,我想尽快生成一个随机排列。 问题是:knuth洗牌是O(n),它涉及生成n个随机数。 因为生成随机数非常昂贵。 我想找到一个O(n)函数,它包含固定的O(1)个随机数 我知道以前有人问过这个问题,但我没有看到任何相关的答案 我要强调一点:我不是在寻找小于O(n)的任何东西,只是一种涉及更少随机数生成的算法 谢谢为每个排列创建一个1-1映射,映射到从1到n的数字!(n阶乘)。在1到n中生成一个随机数!,使用映射,获得排列 对于映射,这可能很有用: 当然,这会很快失控,因为n!很快就会变大。生成一个

我想尽快生成一个随机排列。 问题是:knuth洗牌是O(n),它涉及生成n个随机数。 因为生成随机数非常昂贵。 我想找到一个O(n)函数,它包含固定的O(1)个随机数

我知道以前有人问过这个问题,但我没有看到任何相关的答案

我要强调一点:我不是在寻找小于O(n)的任何东西,只是一种涉及更少随机数生成的算法


谢谢

为每个排列创建一个1-1映射,映射到从1到n的数字!(n阶乘)。在1到n中生成一个随机数!,使用映射,获得排列

对于映射,这可能很有用:


当然,这会很快失控,因为n!很快就会变大。

生成一个随机数需要很长时间,你说呢?Javas Random.nextInt的实现大致如下

oldseed = seed;
nextseed = (oldseed * multiplier + addend) & mask;
return (int)(nextseed >>> (48 - bits));

每个元素的工作量是否太大?

生成一个
32
位整数。对于每个索引
i
(可能最多只有数组中元素数的一半),如果位
i%32
1
,则使用
n-i-1
交换
i


当然,对于您的目的来说,这可能不够随机。您可能可以通过不使用
n-i-1
进行交换来改进这一点,而是通过另一个应用于
n
i
的函数来实现更好的分布。您甚至可以使用两个函数:一个用于位为
0
时,另一个用于位为
1
时。这不是您确切要求的,但如果提供的随机数生成器不能满足您的要求,您可能应该尝试其他方法。通常,伪随机数的生成非常简单

可能是最著名的算法

更多

正如其他答案所建议的那样,您可以创建一个0到N范围内的随机整数!并用它来产生一个洗牌。虽然理论上是正确的,但一般来说,这不会更快,因为N!增长很快,你会花所有的时间做bigint算术


如果您想要速度,并且不介意牺牲一些随机性,那么最好使用性能较差的随机数生成器。线性同余生成器(请参阅)将在几个周期内为您提供一个随机数。

通常不需要在下一个随机值的整个范围内,因此要使用与下一个方法完全相同的随机数,您可以使用下一个方法(我想这几乎类似于随机数(0,N!):

/。。。
m=1;//随机缓冲区范围(单变量)
r=0;//随机缓冲区(零号)
// ... 
对于(/*…*/){
而(m

另外,当然会有一些与2^n不同的值除法相关的错误,但它们会分布在结果样本中。

在进行计算之前生成n个数字(n<您所需的随机数),或者将它们作为数据存储在数组中,使用速度慢但性能好的随机生成器;然后选择一个数字,简单地将索引增加到计算循环中的数组中;如果您需要不同的种子,请创建多个表。

您确定您的数学和算法方法正确吗

我遇到了和费舍尔完全相同的问题——耶茨洗牌将成为角落案例的瓶颈。但对我来说,真正的问题是蛮力算法,它不能很好地适应所有问题。下面的故事解释了我到目前为止提出的问题和优化

为4名玩家发牌

可能的交易数量为96位。这给随机数生成器带来了相当大的压力,以避免在从生成的交易样本集中选择游戏计划时出现静态异常。我选择使用/dev/random中的2xmt19937_64种子,因为它的周期长,而且在web上有大量的广告,这有利于科学模拟

简单的方法是使用Fisher–Yates shuffle生成交易,并过滤掉与已收集信息不匹配的交易。Knuth shuffle每笔交易需要约1400个CPU周期,主要是因为我必须生成51个随机数并交换表中的51个项

对于我只需要在7分钟内完成10000-100000笔交易的正常情况来说,这并不重要。但在极端情况下,过滤器可能只选择需要生成大量交易的手的非常小的子集

对多张卡使用单个数字

当调用CalgRink(ValgRink)的轮廓时,我注意到主减速是C++随机数生成器(在切换STD之后::MulnsixIn分布是第一个瓶颈)。 然后我想出了一个主意,我可以用一个随机数来处理多张牌。其思想是首先使用数字中最不重要的信息,然后删除该信息

int number = uniform_rng(0, 52*51*50*49);
int card1 = number % 52;
number /= 52;
int cards2 = number % 51;
number /= 51;
......
当然,这只是一个小优化,因为生成仍然是O(N)

使用位置换生成

下一个想法正是在这里提出的解决方案,但我最终还是得到了O(N),但比最初的洗牌成本更高。但让我们看看解决方案,以及为什么它会失败得如此惨烈

我决定使用这个想法

到目前为止还不错,外观也不错,但select_deal实现是PITA

void select_deal(Id &new_id, uint64_t partner, uint64_t hands)
{
    unsigned idx;
    unsigned e, n, ns = 26;

    e = n = 13;

    // Figure out partnership who owns which card
    for (idx = CARDS_IN_SUIT*NUM_SUITS; idx > 0; ) {
        uint64_t cut = ncr(idx - 1, ns);

        if (partner >= cut) {
            partner -= cut;
            // Figure out if N or S holds the card
            ns--;
            cut = ncr(ns, n) * 10400600LU;

            if (hands > cut) {
                hands -= cut;
                n--;
            } else
                new_id[idx%NUM_SUITS] |= 1 << (idx/NUM_SUITS);

        } else
            new_id[idx%NUM_SUITS + NUM_SUITS] |= 1 << (idx/NUM_SUITS);

        idx--;
    }

    unsigned ew = 26;
    // Figure out if E or W holds a card
    for (idx = CARDS_IN_SUIT*NUM_SUITS; idx-- > 0; ) {
        if (new_id[idx%NUM_SUITS + NUM_SUITS] & (1 << (idx/NUM_SUITS))) {
            uint64_t cut = ncr(--ew, e);

            if (hands >= cut) {
                hands -= cut;
                e--;
            } else
                new_id[idx%NUM_SUITS] |= 1 << (idx/NUM_SUITS);

        }
    }
}
void选择交易(Id和新Id、uint64合作伙伴、uint64手)
{
无符号idx;
无符号e,n,ns=26;
e=n=13;
//找出谁拥有哪张卡
对于(idx=卡片套装*NUM套装;idx>0;){
uint64
void Deal::generate()
{
    // 52:26 split, 52!/(26!)**2 = 495,918,532,948,1041
    max = 495918532948104LU;
    partner = uniform_rng(eng1, max);

    // 2x 26:13 splits, (26!)**2/(13!)**2 = 10,400,600**2
    max = 10400600LU*10400600LU;
    hands = uniform_rng(eng2, max);

    // Create 104 bit presentation of deal (2 bits per card)
    select_deal(id, partner, hands);
}
void select_deal(Id &new_id, uint64_t partner, uint64_t hands)
{
    unsigned idx;
    unsigned e, n, ns = 26;

    e = n = 13;

    // Figure out partnership who owns which card
    for (idx = CARDS_IN_SUIT*NUM_SUITS; idx > 0; ) {
        uint64_t cut = ncr(idx - 1, ns);

        if (partner >= cut) {
            partner -= cut;
            // Figure out if N or S holds the card
            ns--;
            cut = ncr(ns, n) * 10400600LU;

            if (hands > cut) {
                hands -= cut;
                n--;
            } else
                new_id[idx%NUM_SUITS] |= 1 << (idx/NUM_SUITS);

        } else
            new_id[idx%NUM_SUITS + NUM_SUITS] |= 1 << (idx/NUM_SUITS);

        idx--;
    }

    unsigned ew = 26;
    // Figure out if E or W holds a card
    for (idx = CARDS_IN_SUIT*NUM_SUITS; idx-- > 0; ) {
        if (new_id[idx%NUM_SUITS + NUM_SUITS] & (1 << (idx/NUM_SUITS))) {
            uint64_t cut = ncr(--ew, e);

            if (hands >= cut) {
                hands -= cut;
                e--;
            } else
                new_id[idx%NUM_SUITS] |= 1 << (idx/NUM_SUITS);

        }
    }
}