2/9游戏(Facebook访谈)

时间:2012-11-13 16:40:16

标签: algorithm math

我被问到这个问题:谷歌similar question。在Facebook采访中被问到类似的问题。

  

确定2/9数字游戏的获胜者

     

两名玩家玩下面的游戏:他们选择一个随机数N(小于20亿),然后从1开始,轮流将前一轮的数字乘以2或9(他们的选择)。   到达N的人首先获胜。

     

候选人应该写一个给定N谁决定胜利的函数(第一个或第二个玩家?)

对于乘法工作,基本随机选择2/9还是他们希望我们在移动中添加智能。例如:从乘以2开始,只有当你看到另一个人不能比你更快达到N时才乘以9?

解决这类问题的最佳方法是什么?

9 个答案:

答案 0 :(得分:9)

解决此类问题的最佳方法。

首先,你需要对游戏理论有基本的理解。真的很基本。那就是你意识到这样一个事实,即对于一个给定的数字N,对于开始的球员或者对于他的对手的获胜策略,要么存在获胜策略。所以你必须假设他们都知道策略并且尽可能地发挥最佳动作。

然后你开始熟悉游戏。你练习水平较低。你很快就注意到,对于2-9,首发球员是赢球,而对于10-18,他必须输球。因此,您的函数已准备好处理N<=18

的情况

然后你开始思考一般的制胜战略。知道策略会给你算法。 5分钟后(越快越好)你就会明白你不适合找到获胜策略,因为在这种情况下并不明显。因此,您决定为计算机提供一些基本原则,让它为您解决难题。你知道你会使用递归。

您尝试查找递归规则。您可能希望从结束或从头开始。我将从最后描述这种方法。

游戏的目标是将对手推向区域,在那里他必须给你胜利。而不是自己被推到那个区域。从N / 9到N,有一个获胜区。如果一个人被推到N / 9/2和N / 9之间,他必须输掉。 (因为他的两个动作都会将对手推向胜利区。)所以你写下你的功能:

wins(n) {
  // returns 1, if starting player is able to reach
  // the range [n, 2*n) with his move
  if (n<=18) {
    if (n<=9)
      return 1;
    else
      return 0;
  } else {
    // I must push him to the zone between N/9/2 and N/9
    return wins(n/18);
  }

如果你达到了这一点,你就过去了。还有一些细节,比如是否使用float或int,是否使用int向上或向下舍入。但总的来说,你展示了正确的思维方式,并且你已经准备好面对面试官了。)

编辑实际上上面的代码中有错误。 “获胜”与“能够达到范围(n,2n)”不同。这里可能需要2个函数:wins(n)reaches_slightly_above(n)。后者将以递归方式调用,返回到18以下的值应该不同,类似于Peter de Rivaz解决方案中的值。但是,下面的解决方案和一般方法应该没问题。

替代方法,从下到上,将使用该功能:

wins(a,n) {
  if (9*a >= n)
    // I win easily
    return 1;
  else
    // if one of my moves pushes the opponent to the zone
    // where he's not able to win, then I win!
    return !wins(2*a, n) || !wins(9*a, n);
}

如果他们要求n,则返回win(1,n)的值。这种算法的复杂性并不明显,但我认为它是对数的。

答案 1 :(得分:6)

由于它们必须准确到达N,因此只有在N的格式为2^a * 9^ba, b中的一个允许为0时才有可能。{ / p>

在上面找到ab:如果a + b = even,则第二位玩家将获胜,否则第一位将获胜。

这是因为,在每一步中,玩家都会更接近ab一个,因此更接近a + b一个。所以问题减少到:给定k,如果每一步玩家必须从k中减去1,哪个玩家将首先达到0?

答案 2 :(得分:5)

最佳比赛通常是与对手的移动相反,除了在开始和结束时。

通过与递归解决方案进行比较,结果可以根据数字-1的基数18表示中的最高有效数字来计算答案,如下所示:

def win29(n):
    if n<=9: return True
    n-=1
    while n>=18:
        n = n//18
    return n==1 or 4<=n<=8

答案 3 :(得分:3)

无论是试图满足还是达到或超过N,都可以通过确定在各种情况下总能获胜的策略来解决。我将介绍5个案例(或2个案例,其中第二个案例有4个子案例),涵盖所有N并为每个案例提供获胜策略。

考虑T = ceil( log(N)/log(18) ),即T是最小的权力,18^T达到或超过N

如果18^(T-1) * 9 < N那么第一个玩家总是输给理想的对手。每当第一个玩家选择2时,第二个玩家选择9。每当第一个选择9时,第二个选择一个2.这样,第二个玩家的回合总是以18的幂结束。T之后轮次,第二名球员获胜。第一个玩家无法在前一轮获胜,因为乘以9不足以超过N(因此也不能乘以2)。

所以,现在让我们考虑18^(T-1) * 9 >= N并选择最小k18^(T-1) * 2^k > N。有四种可能性k = 1, 2, 3, or 4

  • (k = 1)第一位玩家获胜。第一个玩家可以以2开始,然后像第二个玩家一样在上面玩,在每个后续转弯中与另一个玩家玩相反的数字直到下一轮。第二个玩家将始终面临初始2的18倍的力量。在18 ^(T-2)* 2时,玩家2最多可以乘以9到18^(T-1),这不足以获胜,并且至少可以返回18^(T-2)*4哪个玩家可以乘以9来赢得18^(T-1)*2

  • (k = 3)第一位玩家也获胜。这次播放器以9开头并像以前一样播放。第二个玩家将始终面对初始9的18倍的力量。在18 ^(T-2)* 9,玩家2最多可以达到18^(T-2) * 9 * 9 < 18^(T-2) * 18 * 8 = 18^(T-1) * 2^3,因此不足以赢得,并且可以至少通过乘以2返回18 ^(T-1),玩家一乘以9并获胜。

  • (k = 2 or 4)第二位玩家获胜。在这里,第二个玩家应该像以前一样玩相反的数字,直到接近结束,这样每个回合玩家一个以18的力量开始。在18^(T-2),玩家一个最多可以到达18^(T-2)* 9 < 18^(T-1),所以不够胜利。如果他返回18 ^(T-2)* 9,则玩家2获胜18^(T-2)*9*9 > 18^(T-2)*18*4 = 18^(T-1)*2^2如果玩家1返回18^(T-2)*2,则玩家2返回18^(T-2)*4。然后玩家最多可以制作18^(T-2)*4*9 = 18^(T-1)*2,这仍然不够。现在玩家1至少可以返回18^(T-2)*8,这足以让玩家2在18^(T-2)*8*9 = 18^(T-1)*4之后达到目标。

答案 4 :(得分:2)

是的,你应该考虑两位球员的最佳表现并决定谁将获胜。

在这里,一个简单的递归思维可以引导你找到解决方案。

如果玩家的号码为nn*9 >= N,则当前玩家将赢得游戏。
否则,他会将2*n9*n传递给第二名玩家。 现在,只有当他(2*n9*n)向第二名球员提供的两个选项导致第二名球员的中奖号码时,1st-Player才会输掉比赛,否则他将有机会再次挑选中奖号码。

因此,我们可以编写一个递归方法如下:
因为,游戏中的所有数字都将是格式:2^i * 9^j我们可以写:

 F(i, j) = true; if (2^i * 9^j * 9) >= N
           !(F(i+1, j) && F(i, j+1)); otherwise

解决方案将在F(0, 0),无论1st-Player是否获胜。

答案 5 :(得分:1)

如果N可以除以2和9,并且有一些好的博弈论方法,那么有很好的答案。这是Javascript中一个简单的动态编程方法,可以为任何可能的N提供答案。

function getWhoWins(n) {
    if(getWhichPlayerWins(0, 1, n) === 0) {
        console.log("First player wins for " + n);
    } else {
        console.log("Second player wins for " + n);
    }
}

// Returns 0 if first, 1 if 2nd player would win
function getWhichPlayerWins(currentPlayer, currentNumber, targetNumber) {
    if(currentNumber * 9 >= targetNumber) {
        return currentPlayer;
    }
    var nextPlayer = (currentPlayer + 1) % 2;
    if(getWhichPlayerWins(nextPlayer, currentNumber *2, targetNumber) === currentPlayer || getWhichPlayerWins(nextPlayer, currentNumber *9, targetNumber) === currentPlayer) {
        return currentPlayer;
    }
    return nextPlayer;
}

此解决方案的时间复杂度为O(2 * logN)= O(logN)。

答案 6 :(得分:0)

答案(我不是100%肯定):

r = N mod 18

if r belongs to (1,9]  player 1 wins
if r belongs to (9,18) or is =1  then player 2 wins.

我没有完整的数学演示,所以我错了。
如果两个玩家(或者至少其中一个)知道如何玩它,这应该是正确的答案。

我能得到这份工作吗? :)

答案 7 :(得分:0)

通过组合博弈论来研究像这样的双人,确定性游戏。这种无聊的博弈论与经济学中流行的Neumann和Nash更有用的博弈论无关。这篇开创性的作品是一本令人愉快的书,名为Conning Ways by Conway,Berlekamp&amp;盖

。对于任何游戏,要么:

  1. 有一种策略,即第二位玩家总是赢,但是第一位玩家会玩。
  2. 有一种策略,即第一个玩家总是赢,但第二个玩家玩。
  3. 你的游戏是一个特殊情况,一个公正的游戏,其中游戏的状态看起来与两个玩家相同。最好的例子是一个名为Nim的简单火柴游戏。碰巧所有公正的游戏都等同于Nim(这是Sprague Grundy定理),所以所有公正的游戏都可以完全解决。


    让我们解决你的游戏。游戏的可能状态是正整数。我们将每个州分类为第二个玩家的胜利(我们将这些游戏标记为零'0'游戏),或者为第一个玩家赢得胜利(我们将这些游戏标记为明星'*'游戏)。

    大于或等于N的整数都是零游戏,因为此时游戏结束了。谁移动它已经失去了。

    轮到它的玩家可以移动到上面的零位置的状态是明星游戏。因此,整数n,N/9 <= n < N都是明星游戏 - 获胜的动作是乘以9。

    轮到它的玩家别无选择只能移动到星位的状态再次为零位。所以整数n,N/9/2 <= n < N/9是零位。我们的球员输了。

    等等。使用类似的参数,我们最终将所有整数归类为一个。


    对于N = 1000说,

    • 整数1000及以上为零游戏
    • 整数112到999是明星游戏(获胜,乘以9)
    • 整数56到111为零游戏(玩家无法获胜)
    • 整数7到55是星际游戏(相应地乘以9或2,以便移动到零游戏中的一个56到111)
    • 整数4到6是零游戏(玩家无法获胜)
    • 整数2到3是星际游戏(乘以2)
    • 整数1是零游戏(玩家无法获胜)

    概括我们得出彼得的结论https://stackoverflow.com/a/13367642/284795

答案 8 :(得分:0)

有趣的帖子和答案。 我很想提出一种理论强力函数,它使用2/9因子计算所有组合路径,其中N <= 2×10 ^ 12(或尽可能接近)。 我说“理论”因为我假设那种计算能力甚至超过了FB?