数组将保持不变的概率是多少?


74

在Microsoft采访中已经问过这个问题。非常好奇地知道为什么这些人对概率提出如此奇怪的问题?

给定rand(N),一个随机生成器,生成从0到N-1的随机数。

int A[N]; // An array of size N
for(i = 0; i < N; i++)
{
    int m = rand(N);
    int n = rand(N);
    swap(A[m],A[n]);
}

编辑:请注意种子是不固定的。

数组A保持不变的概率是多少?
假设数组包含唯一元素。


9
对于N固定种子和固定种子,概率为0或,1因为它根本不是随机的。
Mysticial

25
如果确实是C程序(如标签所示),则概率为1。数组元素按值传递(C中没有按引用传递),因此该swap函数不可能更改的内容A。\ edit:嗯,swap也可以是一个宏……让我们希望它不是:)
reima 2012年

13
@reima:它可能是一个宏。
叶夫根尼·克鲁维

4
考虑在math.stackexchange.com上提问。改写:给定随机a_i,b_i,在对称组S_n中置换(a_1 b_1)(a_2 b_2)...(a_n b_n)=身份的概率是多少?
sdcvvc 2012年

11
呃...没人会初始化A从未初始化过的事实吗?
肯·贝克特

Answers:


29

好吧,我对此玩得很开心。当我第一次阅读该问题时,我想到的第一件事就是群论(尤其是对称群S n)。for循环通过在每次迭代中组成换位(即交换)来简单地在S n中建立一个置换σ 。我的数学并不是那么出色,我有些生疏,所以如果我的记法不适合我。


总览

让我们假设A数组在置换后保持不变。我们最终要求找出事件的概率APr(A)

我的解决方案尝试遵循以下过程:

  1. 考虑所有可能的排列(即数组的重新排序)
  2. 根据它们包含的所谓身份置换的数量,将这些置换划分为不相交的集合。这有助于将问题减少到均匀排列。
  3. 给定置换是偶数(且具有特定的长度),请确定获得标识置换的概率。
  4. 对这些概率求和,以获得阵列未更改的整体概率。

1)可能的结果

请注意,for循环的每次迭代都会创建一个交换(或transposition),该交换将导致以下两种情况之一(但不会同时发生):

  1. 交换了两个元素。
  2. 元素与自身交换。就我们的意图和目的而言,数组是不变的。

我们标记第二种情况。让我们定义一个身份转置如下:

一个身份换位当一个号码与自己交换发生。也就是说,在上述for循环中,当n == m时。

对于所列代码的任何给定运行,我们都组成N换位。有可能0, 1, 2, ... , N出现在这个“产业链”的身份换位。


例如,考虑一种N = 3情况:

Given our input [0, 1, 2].
Swap (0 1) and get [1, 0, 2].
Swap (1 1) and get [1, 0, 2]. ** Here is an identity **
Swap (2 2) and get [1, 0, 2]. ** And another **

请注意,存在奇数个非同一性换位(1),并且更改了数组。


2)基于身份换位的数量进行分区

让我们K_ii身份置换出现在给定排列中的事件为例。请注意,这形成了所有可能结果的详尽划分:

  • 排列不能同时具有两个不同数量的身份换位,并且
  • 所有可能的排列都必须在0N换位之间。

因此我们可以应用总概率定律

                      

现在,我们终于可以利用分区了。请注意,当非同一性换位的数目为奇数时,数组将无法保持不变*。从而:

                        

*从群体理论来看,排列是偶数或奇数,但不会两者都。因此,奇数置换不能是身份置换(因为身份置换是偶数)。

3)确定概率

因此,我们现在必须确定两个概率N-i

  1. Pr(K_i)
  2. Pr(A | K_i)

第一学期

第一项,,Pr(K_i)表示获得具有i身份置换的置换的概率。事实证明这是二项式的,因为对于for循环的每次迭代:

  • 结果与之前的结果无关,并且
  • 创建身份置换的可能性相同1/N

因此,对于N试验而言,获得i身份易位的可能性为:

                      

第二学期

所以,如果你走到今天这一步,我们已经将问题归结为寻找Pr(A | K_i)N - i偶数。这代表在给定i换位为身份的情况下获得身份置换的可能性。我使用一种幼稚的计数方法来确定实现身份置换的方法数量超过可能的置换数量。

首先考虑排列(n, m)(m, n)等效项。然后,将M可能的非身份置换数目设为。我们将经常使用此数量。

                              

这里的目标是确定可以组合换位的集合以形成标识置换的方式的数量。我将尝试在的示例旁边构造一般的解决方案N = 4


让我们考虑N = 4所有身份换位( i = N = 4)的情况。让X代表身份转置。对于每种X,都有N可能(它们是:)n = m = 0, 1, 2, ... , N - 1。因此,存在N^i = 4^4实现身份置换的可能性。为了完整起见,我们添加了二项式系数C(N, i)来考虑身份换位的顺序(此处等于1)。我试图用上面元素的物理布局和下面可能性的数量来描述这个问题:

I  =  _X_   _X_   _X_   _X_
       N  *  N  *  N  *  N  * C(4, 4) => N^N * C(N, N) possibilities

现在,无需显式替换N = 4and i = 4,我们可以查看一般情况。将以上内容与先前找到的分母结合起来,我们会发现:

                          

这很直观。实际上,除此以外的任何其他值1都可能会警告您。想想看:我们被认为所有的N换位都是身份。在这种情况下,数组可能保持不变?显然,1


现在,再次针对N = 4,让我们考虑2个身份换位( i = N - 2 = 2)。按照惯例,我们将两个身份放在最后(并在以后订购)。现在我们知道,我们需要选择两个换位,当组成时,它们将成为身份置换。让我们将任何元素放在第一个位置,称为t1。如上所述,有M可能假设t1不是身份(不可能,因为我们已经放置了两个身份)。

I  =  _t1_   ___   _X_   _X_
       M   *  ?  *  N  *  N

剩下的唯一可能出现在第二个位置的元素是的逆t1,这实际上是t1(并且这是唯一的具有逆唯一性的元素)。我们再次包含二项式系数:在这种情况下,我们有4个开放位置,并且我们打算放置2个身份置换。我们有多少种方法可以做到这一点?4选择2。

I  =  _t1_   _t1_   _X_   _X_ 
       M   *  1   *  N  *  N  * C(4, 2) => C(N, N-2) * M * N^(N-2) possibilities

再次查看一般情况,这全部对应于:

                      

最后,我们在N = 4没有身份换位的情况下( i = N - 4 = 0)进行处理。由于存在很多可能性,因此开始变得棘手,我们必须注意不要重复计算。类似地,我们从将单个元素放在第一位开始并找出可能的组合。首先采取最简单的方法:相同的换位4次。

I  =  _t1_   _t1_   _t1_   _t1_ 
       M   *  1   *  1   *  1   => M possibilities

现在让我们考虑两个独特的元素t1t2。(因为不能等于),所以存在M可能性t1,只有M-1可能性。如果我们用尽所有安排,我们将获得以下模式:t2t2t1

I  =  _t1_   _t1_   _t2_   _t2_ 
       M   *  1   *  M-1 *  1   => M * (M - 1) possibilities   (1)st

   =  _t1_   _t2_   _t1_   _t2_
       M   *  M-1 *  1   *  1   => M * (M - 1) possibilities   (2)nd

   =  _t1_   _t2_   _t2_   _t1_
       M   *  M-1 *  1   *  1   => M * (M - 1) possibilities   (3)rd

现在,让我们考虑三个独特的元素,t1t2t3t1首先放置,然后放置t2。和往常一样,我们有:

I  =  _t1_   _t2_   ___   ___ 
       M   *  ?   *  ?  *  ?  

我们还不能说可能t2有多少个,我们将在一分钟内看到原因。

现在t1,我们排名第三。注意,t1必须走到那里,因为如果要走到最后一个位置,我们将只是重新创建(3)rd上面的安排。重复计算是不好的!这将第三个唯一元素保留t3到最终位置。

I  =  _t1_   _t2_   _t1_   _t3_ 
       M   *  ?   *  1  *   ?  

那么,为什么我们需要花一点时间t2仔细考虑s的数量呢?该换位t1t2 不能是不相交的置换(他们必须分享他们的一个(且只有一个,因为他们也不能等于)nm)。这样做的原因是因为如果它们不相交,我们可以交换排列顺序。这意味着我们将重复计算该(1)st安排。

t1 = (n, m)t2必须是以下形式的(n, x)(y, m)为一些xy以是非不相交的。请注意,x可能不是n或,my很多不是nm。因此,t2实际上可能存在的可能排列数2 * (N - 2)

因此,回到我们的布局:

I  =  _t1_    _t2_    _t1_   _t3_ 
       M   * 2(N-2) *  1   *  ?  

现在t3必须是的反函数t1 t2 t1。让我们手动完成:

(n, m)(n, x)(n, m) = (m, x) 

因此t3必须(m, x)。请注意,这并非不相交t1且不等于t1或不相等,t2因此在这种情况下没有重复计数。

I  =  _t1_    _t2_    _t1_   _t3_ 
       M   * 2(N-2) *  1  *   1    => M * 2(N - 2) possibilities   

最后,将所有这些放在一起:

        

4)放在一起

就是这样了。向后工作,将我们发现的结果代入步骤2中给出的原始求和中。我计算出N = 4以下情况的答案。它非常接近另一个答案中找到的经验值!

         N = 4
         M = 6 _________ ______________ _________
                  | Pr(K_i)| Pr(A | K_i)| 产品展示
         _________ | _________ | _____________ | _________ |
        | | | | |
        | i = 0 | 0.316 | 120/1296 | 0.029 |
        | _________ | _________ | _____________ | _________ |
        | | | | |
        | i = 2 | 0.211 | 6/36 | 0.035 |
        | _________ | _________ | _____________ | _________ |
        | | | | |
        | i = 4 | 0.004 | 1/1 | 0.004 |
        | _________ | _________ | _____________ | _________ |
                            | | |
                            | 总和:0.068 |
                            | _____________ | __________ |

正确性

如果在小组理论中有结果可以在这里应用,那就太酷了,也许还有!当然,这将使所有繁琐的计数工作完全消失(并将问题简化为更优雅的方法)。我停止在上班了N = 4。对于N > 5,给出的仅是一个近似值(我不确定该如何好)。很清楚为什么要考虑这一点:例如,给定N = 8换位,显然有一些方法可以用四个独特的元素来创建身份,而这在上面并没有说明。随着置换的时间变长(据我所知...),方法的数量似乎变得难以计数。

无论如何,在面试范围内,我绝对不能做这样的事情。如果幸运的话,我将达到分母步骤。除此之外,它看起来还很讨厌。


我认为您不对,因为事件之间存在依赖关系-Pr(A和K = 0)+ Pr(A和K = 1)+ ... + Pr(A和K = N)。根据您的分析,您认为不可以。
barak1412

嗯,谢谢你的来信。也许我没有正确地应用它,但我正在考虑结果:令K_1,K_2,...,K_N是空间S的一个分区。令A是S的子空间。然后(有时我称总概率): P(A)= Pr(A | K_i)的总和* P(K_i)。我用这种方式可以肯定将创建一个分区,以及:没有别的置换可以在两个不同的子集(因为它有一个单一的身份号码)和所有可能的排列都占了。
用户

afaict您所拥有的一切都很好(除了分子无处不在……)
andrew

在这方面,答案肯定是薄弱的。我试图通过示例给出一些见解,但是请记住,它不是很好。它主要是试图尽可能地有效地进行计数(通过使用可能发生的情况的模式)。我检查了几次,但显然不是全部。那里应该有一个星号。如果有一种很好的方法来计算该概率,我认为此解决方案将非常简洁。但这绝对使我望而却步!:)
用户

1
这是正确的答案。它会击中所有正确的流行语(“置换”,“换位”,“身份”),并且推理是正确的:1)考虑实际换位的数量,2)考虑您需要偶数个换位,3)计算全部组成身份置换的置换对的数量。但是,有人批评说:如果您以更简洁的方式写东西,那么可能您会发现正确的做法可能更加明显。
杰里米

20

非常好奇地知道为什么这些人对概率提出如此奇怪的问题?

之所以问这样的问题,是因为它们使访问者可以深入了解访问者的

  • 能够读取代码(非常简单的代码,但至少有一些东西)
  • 分析算法以识别执行路径的能力
  • 应用逻辑发现可能的结果和边缘案例的技能
  • 解决问题的推理能力和解决问题的能力
  • 沟通和工作技能-他们问问题还是根据手头的信息孤立地工作

... 等等。揭露被访者这些属性的问题的关键是拥有一段看似简单的代码。这摆脱了非编码器卡住的冒名顶替者;傲慢的人得出错误的结论;懒惰或低于标准的计算机科学家找到了一个简单的解决方案,并停止寻找。正如他们所说,通常,不是您是否获得正确答案,而是您是否对自己的思维过程印象深刻。


我也会尝试回答这个问题。在面试中,我会解释自己而不是提供单行书面答案-这是因为即使我的“答案”是错误的,我也能够证明逻辑思维。

A将保持相同-即元素位于相同位置-

  • m == n在每次迭代中(以便每个元素仅与自身交换);要么
  • 交换的任何元素都将交换回其原始位置

第一种情况是duedl0r给出的“简单”情况,即数组未更改的情况。这可能是答案,因为

数组A保持不变的概率是多少?

如果数组在处更改,i = 1然后又在处恢复i = 2,则该数组处于原始状态,但不是“保持不变”-已更改,然后又更改回。那可能是一个聪明的技术。

然后考虑元素被交换和交换回去的可能性-我认为在一次采访中,计算超出了我的能力。显而易见的考虑是,不必进行更改-换回交换,可以很容易地在三个元素之间进行交换,交换1和2,然后交换2和3、1和3,最后交换2和3。继续,可能会有4、5或更多这样的“循环”项目之间的交换。

事实上,而不是考虑的情况下,该阵列是不变的,它可能是简单考虑它的情况下改变。考虑是否可以将此问题映射到Pascal三角形等已知结构


这是一个难题。我同意,在面试中很难解决,但这并不意味着在面试中很难提出。较差的候选人将没有答案,普通候选人将猜出最明显的答案,而好的候选人将解释为什么问题很难回答。

我认为这是一个“不限成员名额”的问题,可让面试官深入了解候选人。因此,即使在面试中很难解决,在面试中也要问一个问题。问一个问题不仅仅是检查答案是对还是错。


那更长的周期呢?他们不存在吗?还是随着周期长度的增加它们越来越不可能?希望这两种情况之一是正确的,最好对它们的重要性进行某种定量的估计。
安德鲁·库克

2
不幸的是,情况并非如此...当您交换1和2,然后交换2和3,然后交换1和3时,它将不是第一个状态...后者需要再次交换...
Erdem E.

3
是的,但这不能证明没有超过2个交换的序列是不可能的。例如,在您的论点中,进行四次交换可以使一切正常。
安德鲁·库克

@andrewcooke可以存在更长的周期,但会假设它们的长度可能相对于其长度呈指数下降。大概会有某种限制。
柯克·布罗德赫斯特

哦,对不起胜利者,以为你在跟我说话!
安德鲁·库克

10

以下是C代码,用于计算rand可以产生的2N元组的值的数量并计算概率。从N = 0开始,它显示计数为1、1、8、135、4480、189125和12450816,概率为1,1,.5,.185185,.0683594,.0193664和.00571983。计数没有出现在“整数序列百科全书”中,因此我的程序有错误或者这是一个非常晦涩的问题。如果是这样,这个问题并不是求职者要解决的,而是要揭露他们的一些思维过程以及他们如何处理挫败感。我不会认为这是一个很好的面试问题。

#include <inttypes.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>


#define swap(a, b)  do { int t = (a); (a) = (b); (b) = t; } while (0)


static uint64_t count(int n)
{
    // Initialize count of how many times the original order is the result.
    uint64_t c = 0;

    // Allocate space for selectors and initialize them to zero.
    int *r = calloc(2*n, sizeof *r);

    // Allocate space for array to be swapped.
    int *A = malloc(n * sizeof *A);

    if (!A || !r)
    {
        fprintf(stderr, "Out of memory.\n");
        exit(EXIT_FAILURE);
    }

    // Iterate through all values of selectors.
    while (1)
    {
        // Initialize A to show original order.
        for (int i = 0; i < n; ++i)
            A[i] = i;

        // Test current selector values by executing the swap sequence.
        for (int i = 0; i < 2*n; i += 2)
        {
            int m = r[i+0];
            int n = r[i+1];
            swap(A[m], A[n]);
        }

        // If array is in original order, increment counter.
        ++c;    // Assume all elements are in place.
        for (int i = 0; i < n; ++i)
            if (A[i] != i)
            {
                // If any element is out of place, cancel assumption and exit.
                --c;
                break;
            }

        // Increment the selectors, odometer style.
        int i;
        for (i = 0; i < 2*n; ++i)
            // Stop when a selector increases without wrapping.
            if (++r[i] < n)
                break;
            else
                // Wrap this selector to zero and continue.
                r[i] = 0;

        // Exit the routine when the last selector wraps.
        if (2*n <= i)
        {
            free(A);
            free(r);
            return c;
        }
    }
}


int main(void)
{
    for (int n = 0; n < 7; ++n)
    {
        uint64_t c = count(n);
        printf("N = %d:  %" PRId64 " times, %g probabilty.\n",
            n, c, c/pow(n, 2*n));
    }

    return 0;
}

1
一个独立的Python程序给出的结果相同,直到N = 6,因此您的实现是正确的。每个计数都可以除以N ^ 3,但是在OEIS中也不会出现。
ecatmur

10

该算法的行为可以被建模为一个Markov链对称群 小号Ñ

基本

数组N个元素A可以按N个排列!不同的排列。让我们对这些排列从1到N!进行编号,例如按字典顺序。因此,A算法中任何时候的数组状态都可以通过排列数来充分表征。

例如,对于N = 3,所有3个可能的编号之一!= 6个排列可能是:

  1. abc
  2. ACB
  3. bac
  4. bca
  5. 出租车
  6. cba

状态转移概率

在算法的每个步骤中,状态A要么保持不变,要么从这些排列中的一个过渡到另一个。现在,我们对这些状态更改的可能性感兴趣。让我们称Pr(ij状态从排列i变为排列j的概率在单循环迭代中。

当我们从[0,N -1]范围内均匀且独立地选择mn时,有N ²可能的结果,其中的每一个是同样可能。

身分识别

对于这些结果中的N个m = n成立,因此排列没有变化。因此,

Pr(i→i)

换位

剩余Ñ ² - ñ例是换位,即两个元件交换它们的位置,因此,置换的变化。假设这些换位之一交换位置xy处的元素。有两种情况可以通过算法生成此转置:m = xn = ym = yn = x。因此,每个换位的概率为2 / N ²。

这与我们的排列有何关系?让我们把两个置换Ĵ 邻居当且仅当有哪些转变换位Ĵ(反之亦然)。然后我们可以得出结论:

Pr(i→j)

过渡矩阵

我们可以安排的概率PR(Ĵ在过渡矩阵)P ∈[0,1] Ñ!× Ñ。我们定义

p ij = Pr(ij),

其中p ijP的i行和第j列中的条目。注意

Pr(ij)= Pr(ji),

这意味着P是对称的。

现在的关键是观察将P自身相乘会发生什么。采取任何元素p (2)IJP ²:

p(2)ij

乘积Pr(ik)·Pr(kj)是这样的概率,即从排列i开始,我们一步就过渡到排列k,而在下一个后续步骤中过渡到排列j。因此,对所有介于中间的置换k求和,就得出了ij分两步过渡的总概率。

这个论点可以扩展到P的高次幂。特殊的后果如下:

假设我们从置换i开始,p Nii是在N步之后返回置换i的概率。

让我们通过N = 3进行测试。我们已经为排列编号了。相应的转换矩阵如下:

P

P与自身相乘得到:

P²

另一个乘法产生:

P³

主对角线中的任何元素给我们想要的概率,这是15 / 815 / 27

讨论区

尽管此方法在数学上是合理的,并且可以应用于N的任何值,但这种形式并不实用。转换矩阵PN!2项,这很快变得非常大。即使对于N = 10,矩阵的大小也已经超过13万亿个条目。因此,天真地实现该算法似乎是不可行的。

但是,与其他建议相比,此方法结构非常结构化,不需要弄清楚哪些排列是邻居,不需要复杂的推导。我希望可以利用这种结构性来找到更简单的计算。

例如,可以利用以下事实:P的任何幂的所有对角元素都是相等的。假设我们可以轻松地计算出P N的踪迹,则解就是tr(P N)/ N!。P N的迹线等于其特征值的N次方之和。因此,如果我们有一个有效的算法来计算P的特征值,就可以成立了。但是,除了计算最多N = 5的特征值之外,我没有对此进行进一步的探讨。


翻阅一遍,这似乎是整个主题的唯一声音/正确答案。您是否有运气来推广结果?今晚我去看看。
Gleno

@Gleno我没有超出我已经写的内容。我希望知道您的发现!
reima 2012年

1
好吧,我还没有时间解决这个问题。我验证了您的发现(对于n = 3,得出相同的矩阵,概率相同,等等),并计算了特征值和概率,直到N =7。显然存在一种模式,但是当查看特征值序列时,它并没有跳到我身上。我还尝试通过查看矩阵的对角元素至n次方来作弊,并检查它们是否遵循任何已知的顺序-但可惜它们没有。令您感到遗憾的是,您的方法在页面上的位置是如此之低,而此时的概率高达N = 7。
Gleno

4

很容易观察边界1 / n n <= p <= 1 / n。

这是显示反指数上限的不完整想法。

您正在从{1,2,..,n}绘制2n次数字。如果它们中的任何一个是唯一的(恰好发生一次),则该数组肯定会更改,因为该元素已消失并且无法返回其原始位置。

固定数唯一的概率为2n * 1 / n *(1-1 / n)^(2n-1)= 2 *(1-1 / n)^(2n-1),渐近地为2 / e 2,它从0出发。[2n因为您选择在哪个尝试上获得它,所以1 / n表示您尝试获得它,(1-1 / n)^(2n-1)您没有获得它。其他尝试]

如果事件是独立的,您将有机会所有数字都是非唯一的(2 / e 2)^ n,这意味着p <= O((2 / e 2)^ n)。不幸的是,他们不是独立的。我认为可以通过更复杂的分析来显示界限。关键字是“球和垃圾箱问题”。


3

一种简单的解决方案是

p> = 1 / N N

由于数组保持不变的一种可能方式是m = n每次迭代。并且m等于n可能性1 / N

肯定比那高。问题是多少。

再想一想:人们还可能争辩说,如果您随机地对数组进行混洗,则每个排列都有相等的概率。由于存在n!排列,仅得到一个(我们一开始就有一个)的可能性是

p = 1 / N!

这比以前的结果好一点。

如上所述,该算法是有偏见的。这意味着并非每个排列都有相同的概率。所以1 / N!不是很准确。您必须找出排列的分布方式。


1
数组是随机混排的,问题是它是否被均匀混排。这两个概念无关。
Dietrich Epp

1
@DietrichEpp:是的,那么您必须证明此算法是否有偏见。如果没有偏见,它是均匀分布的。
duedl0r 2012年

5
嗯,我只是想起了一个非常容易证明该算法有偏见的证据。该算法可以统一地以N ^ N种不同的方式执行,但是排列的数量为N!。几乎肯定不是N 1,N的约数,因此,该算法被偏置为N> = 3
迪特里希埃普

@DietrichEpp对于证明n!并不总是划分n^n,只考虑其中的情况n是素大于2
丹尼斯萌

1
@ duedl0r:这是一个数学证明,因此您可以通过检查逻辑步骤并确保正确遵循来进行验证。如果您已经知道答案是什么,则无需实际运行该程序。就像我说的那样,这不是一个有建设性的证明,因此该证明不会为您提供有关存在多少偏差的任何信息,它仅证明必须存在偏差,因为否则会存在逻辑矛盾。当您说它“取消”时,这实际上只是一个猜测,但是欢迎您尝试证明它确实取消了。
Dietrich Epp

3

仅供参考,不确定上面(1 / n ^ 2)的范围是否成立:

N=5 -> 0.019648 < 1/25
N=6 -> 0.005716 < 1/36

采样代码:

import random

def sample(times,n):
    count = 0;
    for i in range(times):
        count += p(n)
    return count*1.0/times;

def p(n):
    perm = range(n);
    for i in range(n):
        a = random.randrange(n)
        b = random.randrange(n)

        perm[a],perm[b]=perm[b],perm[a];


    return perm==range(n)

print sample(500000,5)

请注意,您可以通过转到perm [a],perm [b] = perm [b],perm [a]进行互换
罗伯特·金(Robert King)

3

每个人都假定A[i] == i没有明确声明。我也将做这个假设,但请注意,概率取决于内容。例如,如果A[i]=0,则所有N的概率= 1。

这是操作方法。假设P(n,i)结果数组与原始数组相差i个换位的概率。

我们想知道P(n,0)。确实是这样:

P(n,0) = 
1/n * P(n-1,0) + 1/n^2 * P(n-1,1) = 
1/n * P(n-1,0) + 1/n^2 * (1-1/(n-1)) * P(n-2,0)

说明:我们可以通过两种方式获得原始数组,要么在已经良好的数组中进行“中性”转置,要么通过还原唯一的“不良”转置。为了获得只有一个“坏”转置的数组,我们可以将一个带有0个坏转置的数组制成一个非中立的转置。

编辑:在P(n-1,0)中用-2代替-1


1
1)问题指出“假设数组包含唯一元素。” 因此,在不失一般性的前提下,您可以假设A[i] == i。2)我认为通常没有找到简单的方法P(n,i)。这是很容易的P(n,0),但是对于较大的i来说,尚不清楚有多少个换位是“好”和有多少个“坏”。
sdcvvc 2012年

@sdcvvc:您说的很容易找到,P(n,0)但这正是问题所在。如果您不同意这种推理,请举一个例子,说明失败的原因或原因。
伦哈德·欧拉

我不这么认为P(n-1,1) = (1-1/(n-1)) * P(n-2,0)。如果您处于中间位置,则可以添加另一个不良步骤,以后将撤消该步骤。例如,考虑(1234)=>(2134)=>(2143)=>(1243)=>(1234)。分母中的数字n-1也值得怀疑,因为随机函数允许重复。
sdcvvc 2012年

1

这不是一个完整的解决方案,但至少是这样。

进行一组无效的交换。我们知道,一定会发生这样的情况,即它的交换最终使用全部n交换形成了一系列大小不同的循环。(为此,无效的交换可以视为大小为1的循环)

也许我们可以

1)根据循环的大小将它们分成几组
。2)计算获取每个组的方法数量。

(主要问题是,有一吨的不同群体的,但我不知道你怎么会实际计算这个,如果你没有考虑到不同的分组。)


1

有趣的问题。

我认为答案是1 / N,但是我没有任何证据。找到证明后,我将编辑答案。

到现在为止我得到了什么:

如果m == n,则不会更改数组。m == n的概率为1 / N,因为有N ^ 2个选项,并且只有N个是适合的(每0 <= i <= N-1的((i,i))。

因此,我们得到N / N ^ 2 = 1 / N。

表示Pk,在进行k次交换互换之后,大小为N的数组将保持不变的概率。

P1 = 1 / N。(如下所示)

P2 =(1 / N)P1 +(N-1 / N)(2 / N ^ 2)= 1 / N ^ 2 + 2(N-1)/ N ^ 3。

Explanation for P2:
We want to calculate the probability that after 2 iterations, the array with 
N elements won't change. We have 2 options : 
- in the 2 iteration we got m == n (Probability of 1/N)
- in the 2 iteration we got m != n (Probability of N-1/N)

If m == n, we need that the array will remain after the 1 iteration = P1.
If m != n, we need that in the 1 iteration to choose the same n and m 
(order is not important). So we get 2/N^2.
Because those events are independent we get - P2 = (1/N)*P1 + (N-1/N)*(2/N^2).

Pk =(1 / N)* Pk-1 +(N-1 / N)* X。(第一个用于m == n,第二个用于m!= n)

我必须多考虑X等于什么。(X只是真实公式的替代,不是常数或其他任何值)

Example for N = 2.
All possible swaps:

(1 1 | 1 1),(1 1 | 1 2),(1 1 | 2 1),(1 1 | 2 2),(1 2 | 1 1),(1 2 | 1 2)
(1 2 | 2 1),(1 2 | 2 2),(2 1 | 1 1),(2 1 | 1 2),(2 1 | 2 1),(2 1 | 2 2)
(2 2 | 1 1),(2 2 | 1 2),(2 2 | 2 1),(2 1 | 1 1).

Total = 16. Exactly 8 of them remain the array the same.
Thus, for N = 2, the answer is 1/2.

编辑: 我想介绍另一种方法:

我们可以将掉期分为三类:建设性掉期,破坏性掉期和无害掉期。

构造性交换定义为导致至少一个元素移动到正确位置的交换。

破坏性交换定义为导致至少一个元素从其正确位置移动的交换。

无害交换定义为不属于其他组的交换。

显而易见,这是所有可能交换的分区。(交集=空集)。

现在我要证明的主张:

    The array will remain the same if and only if 
the number of Destructive swap == Constructive swap in the iterations.

如果有人有反例,请写下来作为评论。

如果这个主张是正确的,我们可以将所有组合求和-0个无害掉期,1个无害掉期,..,N个无害掉期。

对于每个可能的k次无害交换,我们检查Nk是否为偶数,如果不是,则跳过。如果是,我们将(Nk)/ 2设为破坏性,将(Nk)设为建设性。只看所有可能性。


1
您的推论是错误的,因为我们交换了N次,即概率为1 / N ^ 2。但是,由于多种因素的组合,这也是错误的,其中一些因素至少已经在其他答案中得到了解释。
康拉德·鲁道夫2012年

@KonradRudolph您在哪里看到我写的概率是1 / N ^ 2?我确定我写的是正确的。
barak1412

你能解释为什么吗?(什么地方错了)我很确定不是。
barak1412

我的第一句话不是在解释什么问题吗?对于一次交换,n = m的概率为1 / N。您执行了N次,则每次都必须相同,这将产生1 / N * 1 / N = 1 / N ^ 2。基本上,答案中P2的计算是错误的。只是直觉上应该是显而易见的,保留原始阵列的机会应该不多,在N.小于1
康拉德·鲁道夫

等等,我在上面的评论中的推理是错误的,您不能将概率相乘,因为它们是有条件的,但是您的P2公式仍然是错误的,并且直觉仍然成立。
康拉德·鲁道夫

1

我将问题建模为多图,其中节点是数组的元素,并且交换在它们之间添加了无向(!)连接。然后以某种方式查找循环(​​所有节点都是循环的一部分=>原始)

确实需要重新工作!:(


1

从数学的角度来看:

要使数组元素每次都在同一位置交换,则Rand(N)函数必须为int m和int n两次生成相同的数字。因此,Rand(N)函数两次生成相同数字的概率为1 / N。并且在for循环中有N次调用Rand(N),因此我们的概率为1 /(N ^ 2)


1

C#中的天真的实现。这个想法是创建初始数组的所有可能排列并枚举它们。然后,我们建立状态可能变化的矩阵。将矩阵本身乘以N倍,我们将得到矩阵,该矩阵显示存在从N步到置换#i到置换#j的几种方式。Elemet [0,0]将显示将导致相同初始状态的几种方法。第0行的元素总和将显示不同方式的总数。通过将前者除以后者,我们得到了概率。

实际上,排列的总数为N ^(2N)。

Output:
For N=1 probability is 1 (1 / 1)
For N=2 probability is 0.5 (8 / 16)
For N=3 probability is 0.1851851851851851851851851852 (135 / 729)
For N=4 probability is 0.068359375 (4480 / 65536)
For N=5 probability is 0.0193664 (189125 / 9765625)
For N=6 probability is 0.0057198259072973293366526105 (12450816 / 2176782336)

class Program
{
    static void Main(string[] args)
    {
        for (int i = 1; i < 7; i++)
        {
            MainClass mc = new MainClass(i);
            mc.Run();
        }
    }
}

class MainClass
{
    int N;
    int M;

    List<int> comb;
    List<int> lastItemIdx;
    public List<List<int>> combinations;
    int[,] matrix;

    public MainClass(int n)
    {
        N = n;

        comb = new List<int>();
        lastItemIdx = new List<int>();
        for (int i = 0; i < n; i++)
        {
            comb.Add(-1);
            lastItemIdx.Add(-1);
        }

        combinations = new List<List<int>>();
    }

    public void Run()
    {
        GenerateAllCombinations();
        GenerateMatrix();
        int[,] m2 = matrix;
        for (int i = 0; i < N - 1; i++)
        {
            m2 = Multiply(m2, matrix);
        }

        decimal same = m2[0, 0];
        decimal total = 0;
        for (int i = 0; i < M; i++)
        {
            total += m2[0, i];
        }

        Console.WriteLine("For N={0} probability is {1} ({2} / {3})", N, same / total, same, total);
    }

    private int[,] Multiply(int[,] m2, int[,] m1)
    {
        int[,] ret = new int[M, M];
        for (int ii = 0; ii < M; ii++)
        {
            for (int jj = 0; jj < M; jj++)
            {
                int sum = 0;

                for (int k = 0; k < M; k++)
                {
                    sum += m2[ii, k] * m1[k, jj];
                }

                ret[ii, jj] = sum;
            }
        }

        return ret;
    }

    private void GenerateMatrix()
    {
        M = combinations.Count;
        matrix = new int[M, M];

        for (int i = 0; i < M; i++)
        {
            matrix[i, i] = N;
            for (int j = i + 1; j < M; j++)
            {
                if (2 == Difference(i, j))
                {
                    matrix[i, j] = 2;
                    matrix[j, i] = 2;
                }
                else
                {
                    matrix[i, j] = 0;
                }
            }
        }
    }

    private int Difference(int x, int y)
    {
        int ret = 0;
        for (int i = 0; i < N; i++)
        {
            if (combinations[x][i] != combinations[y][i])
            {
                ret++;
            }

            if (ret > 2)
            {
                return int.MaxValue;
            }
        }

        return ret;
    }

    private void GenerateAllCombinations()
    {
        int placeAt = 0;
        bool doRun = true;
        while (doRun)
        {
            doRun = false;
            bool created = false;

            for (int i = placeAt; i < N; i++)
            {
                for (int j = lastItemIdx[i] + 1; j < N; j++)
                {
                    lastItemIdx[i] = j; // remember the test

                    if (comb.Contains(j))
                    {
                        continue; // tail items should be nulled && their lastItemIdx set to -1
                    }

                    // success
                    placeAt = i;
                    comb[i] = j;
                    created = true;
                    break;
                }

                if (comb[i] == -1)
                {
                    created = false;
                    break;
                }
            }

            if (created)
            {
                combinations.Add(new List<int>(comb));
            }

            // rollback 
            bool canGenerate = false;
            for (int k = placeAt + 1; k < N; k++)
            {
                lastItemIdx[k] = -1;
            }

            for (int k = placeAt; k >= 0; k--)
            {
                placeAt = k;
                comb[k] = -1;

                if (lastItemIdx[k] == N - 1)
                {
                    lastItemIdx[k] = -1;
                    continue;
                }

                canGenerate = true;
                break;
            }

            doRun = canGenerate;
        }
    }
}

0

每次迭代m == n的概率,然后重复N次。P(m == n)= 1 / N。所以我认为在这种情况下P = 1 /(n ^ 2)。但是随后您必须考虑将值交换回来。所以我认为答案是(文本编辑器让我知道)1 / N ^ N。


1
绝对高于(1/N)^N。事实是,阵列本身实际上从未改变的可能性,我们仍然没有计算出事物四处移动并恰好返回到原始状态的可能性。
丹尼斯·蒙

0

问题:数组A保持不变的概率是多少?条件:假设数组包含唯一元素。

尝试使用Java解决方案。

随机交换发生在原始int数组上。在java方法中,参数始终按值传递,因此在swap方法中发生的情况无关紧要,因为数组的a [m]和a [n]元素(从下面的代码swap(a [m],a [n]))是传递的数组不完整。

答案是数组将保持不变。尽管有上述条件。参见下面的Java代码示例:

import java.util.Random;

public class ArrayTrick {

    int a[] = new int[10];
    Random random = new Random();

    public void swap(int i, int j) {
        int temp = i;
        i = j;
        j = temp;
    }

    public void fillArray() {
        System.out.println("Filling array: ");
        for (int index = 0; index < a.length; index++) {
            a[index] = random.nextInt(a.length);
        }
    }

    public void swapArray() {
        System.out.println("Swapping array: ");
        for (int index = 0; index < a.length; index++) {
            int m = random.nextInt(a.length);
            int n = random.nextInt(a.length);
            swap(a[m], a[n]);
        }
    }

    public void printArray() {
        System.out.println("Printing array: ");
        for (int index = 0; index < a.length; index++) {
            System.out.print(" " + a[index]);
        }
        System.out.println();
    }

    public static void main(String[] args) {
        ArrayTrick at = new ArrayTrick();

        at.fillArray();
        at.printArray();
        at.swapArray();
        at.printArray();
    }
}

样本输出:

填充阵列:打印阵列:3 1 1 4 9 7 9 5 9 5交换阵列:打印阵列:3 1 1 4 9 7 9 5 9 5


1
swap(i,j)可能不会影响任何事情,因为整数是通过值传递的
法官

我正是这个意思。swap()方法不会产生任何影响。
rashid 2012年

仅由于您选择语言和实施swap
OrangeDog
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.