Algorithm 有效地解决了这种递推关系。[优于O(N^2)]

Algorithm 有效地解决了这种递推关系。[优于O(N^2)],algorithm,complexity-theory,recurrence,Algorithm,Complexity Theory,Recurrence,我有一个递归关系,它是这样的: E(i,j)=A[i]+A[j]+E(i+1,j-1)+E(i+2,j)+E(i,j-2);如果我j E(i,j)=A[i];如果(i==j) E(i,j)=0;如果i

我有一个递归关系,它是这样的:

E(i,j)=A[i]+A[j]+E(i+1,j-1)+E(i+2,j)+E(i,j-2);如果我j

E(i,j)=A[i];如果(i==j)

E(i,j)=0;如果i<0或j<0


A[i]1关于你的复发关系:

这似乎有点困难,因为总结表明,对于E(1,N),我们需要E(2,…)和E(3,…),每一个都需要其他具有更高“i”的条目,等等

现在,重新排列这些项,使E项与最高的“i”隔离(一般来说,使用递归关系的一个好主意是查看隔离特定索引的不同安排-最高、最低、中间一个…):

E(i+2,j)=E(i,j)-E(i,j-2)-E(i+1,j-1)-A(i)-A(j)

然后,将所有i向下移动2(只需重写公式)

E(i,j)=E(i-2,j)-E(i-2,j-2)-E(i-1,j-1)-A(i-2)-A(j)

然后,对于{i=1,j=N},这就是你要寻找的,我们得到:

E(1,N)=E(-1,N)-E(-1,N-2)-E(0,N-1)-A(-1)-A(N)=-A(N),因为所有其他项都是零

(当然,我假设对于I=0/j=0,E(I,j)和A(I)为零;当您仅为负指数指定零值,但不为零指数指定零值时,您的说明可能不完整)

然后,关于你对潜在案例的(编辑)描述——两名玩家轮流从A开始,任意从左或右,直到A耗尽:

从前面的结果来看,我不认为你的循环关系描述了潜在的游戏,因为结果是-A(N)。。。因此,将该递归关系放在一边(对于i=1,它无论如何都会得到解决)。看看游戏本身,我们可以得到什么样的关系

[编辑后继续]

到目前为止,我还没有找到一种可以在封闭形式下进行转换的方法,即比处理某种递归关系更快的方法。因此,现在,让我们写下我们如何用一种便于计算的方式来描述游戏

定义以下数量:

设X(i,k)为在回合#k(计算两名玩家的回合数)结束时,物品#i(在数组A中)仍然存在(即尚未被任何玩家拿走)的概率。当然,在我们的游戏中,k从1到N,我也是。出于验证目的,我们可能会注意到,对于所有i,X(i,N)必须为零:在游戏结束时,所有元素都已被取下

如果我们可能需要(在公式评估中)超出“正常”范围的任何X(i,k)值,我们评估:

X(i,k)=1 for {1<=i<=N; k=0}: initially all elements in A are still there.
X(i,k)=0 whenever i<1 or i>N: no elements ever exist outside the (original) range of A.
元素#i在回合#k后仍然存在的概率X(i,k)是它在上一回合结束时存在的概率,减去在回合#k时被取下的概率:

元素#i与玩家#1的概率是T(i,k)的所有回合k的总和,但只计算他的回合数。让我们用P(i,1)表示这个数量,其中1代表玩家#1

同样,对于玩家2:

玩家#1的预期分数为S(1)之和:

看一下上面的公式,我看不出有什么方法可以避免O(N2)方法。内存使用率可以是O(N),因为我们可能会继续运行总计并丢弃不再需要的旧“元素”数据。考虑到这一点——假设你不处理过长的数组——玩家将无法生存O(n2)时间性能在实践中不应该是这样的问题

int N  = sizeof(A);

// note on the code below: we'll use i as an integer running over the elements in A, and k running over the turns that players make.
// while in human parlance we would have them (both) run from 1 to N, the zero-based arrays of C++ make it more convenient to let them 
// run from 0 to N-1.

// initialize the running totals P(i), the accumulated (over turns) probabilities that element #i winds up with player #1.
// if we ever need the same for player #2, it's values are of course 1-P (that is: at the end of the game).
double* P = new double[N];
for (int i=0; i<N; i++)
{
  P[i] = 0; // before we start, there's little chance that player #1 has already taken any element.
}
// initialize the existence-array for the elements.
int* X = new int[N];
for (int i=0; i<N; i++)
{
  X[i] = 1; // initially all elements exist, i.e. have not yet been taken by any player.
}
// declare an array for processing the probabilities that elements are taken at a particular turn.
double* T = new double[N];

// iterate over the turns.
for (int k=0; k<N; k++)
{
  // for each element, calculate the probability that it is taken NOW.
  // the current values of X - the existence array - refer to the (end of the) previous turn.
  for (int i=0; i<N; i++)
  {
    // note: take care of the boundaries i=0 and i=N-1.
    if (i == 0)
    {
      T[i] = X[i] * ( 2 - X[i+1] ) / 2;
    }
    else if (i == N-1)
    {
      T[i] = X[i] * ( 2 - X[i-1] ) / 2;
    }
    else
    {
      T[i] = X[i] * ( 2 - X[i-1] - X[i+1] ) / 2;
    }
  } // element i
  // with the take-probabilities for this turn in place, update P - only at odd turns (i.e. k=even): P is for player #1 only.
  if (k % 2 == 0)
  {
    for (int i=0; i<N; i++)
    {
      P[i] += T[i];
    }
  }
  // finally - in this turn - update the existence array.
  for (int i=0; i<N; i++)
  {
    X[i] -= T[i];
  }

} // turn k

// result: calculate the expected score for player #1.
double score = 0;
for (int i=0; i<N; i++)
{
  score += P[i] * A[i];
}
int N=sizeof(A);
//注意下面的代码:我们将使用i作为一个整数,在A中的元素上运行,在玩家的回合上运行k。
//在人类语言中,我们将使它们(两者)都从1到n运行,基于C++的零数组使它们更方便。
//从0运行到N-1。
//初始化运行总数P(i),即元素i与玩家1的累积(翻转)概率。
//如果玩家2需要相同的值,那么它的值当然是1-P(即:在游戏结束时)。
double*P=新的double[N];

对于(inti=0;i,由于概率相等,这只是一个计数问题


你能计算出选择[j]的几率吗在第k个回合?

我对此表示怀疑。不过,你可能会找到一种不同的方法来解决根本问题,所以我认为你应该发布。好的,我会在一分钟后发布问题,但是假设我们有一种有效的替代方法来解决根本问题,那么这种方法不应该在这里反映为解决这个重复问题吗效率更高?或者可能不一定!这个问题看起来像是一个复制品。纳布:是的,它“同样的问题,但显然有”一个比O(N^2)更好的方法。我已经尝试过使用memorization进行DP。@srbh.kmr什么让你认为它可以在不到O(N^2)的时间内完成?我看不到任何证据表明这是一个答案。@david:也许如果你再次尝试阅读这个问题?它清楚地说,欢迎任何提示……即使如此。你给出了什么提示?@david:我所暗示的正是我所写的:试着计算A[j]在第k个回合中被选中。我相信我们甚至可以给出一个封闭式公式。事实上,这似乎是另一个答案中缺少的一步。这个评论听起来比你的答案好。我只是想帮助你。接受与否取决于你。伯特:感谢你的透彻分析。我认为是转变打破了这种重复“i向下2”的定义,因为复发是以自下而上的方式定义的,而将i移到i-2是试图用已经未知的值来定义一些值。我认为你的其他分析结合起来,有点像我的复发:
E(i,j)=0.5*a[i]+0.5*a[j]+0.5*E(i+1,j-1)+0.25*E(i+2,j)+0.25*E(i,j-2)。
其中,
E(i,j)
是player1通过子阵列
A[i,j]
可以获得的预期分数,可以
X(i,k) = X(i,k-1) - T(i,k)
P(i,1) = sum{ k=1,3,...: T(i,k) }
P(i,2) = sum{ k=2,4,...: T(i,k) }
S(1) = sum( i=1..N: P(i,1)*A(i) }, and likewise for player #2: S(2) = sum( i=1..N: P(i,2)*A(i) }
int N  = sizeof(A);

// note on the code below: we'll use i as an integer running over the elements in A, and k running over the turns that players make.
// while in human parlance we would have them (both) run from 1 to N, the zero-based arrays of C++ make it more convenient to let them 
// run from 0 to N-1.

// initialize the running totals P(i), the accumulated (over turns) probabilities that element #i winds up with player #1.
// if we ever need the same for player #2, it's values are of course 1-P (that is: at the end of the game).
double* P = new double[N];
for (int i=0; i<N; i++)
{
  P[i] = 0; // before we start, there's little chance that player #1 has already taken any element.
}
// initialize the existence-array for the elements.
int* X = new int[N];
for (int i=0; i<N; i++)
{
  X[i] = 1; // initially all elements exist, i.e. have not yet been taken by any player.
}
// declare an array for processing the probabilities that elements are taken at a particular turn.
double* T = new double[N];

// iterate over the turns.
for (int k=0; k<N; k++)
{
  // for each element, calculate the probability that it is taken NOW.
  // the current values of X - the existence array - refer to the (end of the) previous turn.
  for (int i=0; i<N; i++)
  {
    // note: take care of the boundaries i=0 and i=N-1.
    if (i == 0)
    {
      T[i] = X[i] * ( 2 - X[i+1] ) / 2;
    }
    else if (i == N-1)
    {
      T[i] = X[i] * ( 2 - X[i-1] ) / 2;
    }
    else
    {
      T[i] = X[i] * ( 2 - X[i-1] - X[i+1] ) / 2;
    }
  } // element i
  // with the take-probabilities for this turn in place, update P - only at odd turns (i.e. k=even): P is for player #1 only.
  if (k % 2 == 0)
  {
    for (int i=0; i<N; i++)
    {
      P[i] += T[i];
    }
  }
  // finally - in this turn - update the existence array.
  for (int i=0; i<N; i++)
  {
    X[i] -= T[i];
  }

} // turn k

// result: calculate the expected score for player #1.
double score = 0;
for (int i=0; i<N; i++)
{
  score += P[i] * A[i];
}