Google Code Jam 2008:第1A轮问题3

时间:2009-09-29 12:42:22

标签: algorithm

在Google Code Jam 2008第1A轮,there is problem

  

计算前三位数   数字的小数点   (3 + SQRT(5))^ n的

n可以是1000000的大数字 例如:如果n = 2则(3 + sqrt(5))^ 2 = 27 .4164079 ...答案为027.
对于n = 3:(3 + sqrt(5))^ 3 = 3 935 .73982 ...答案是935.

其中一个解决方案是创建矩阵M 2x2:[[0,1],[ - 4,6]],而不是计算矩阵P = M ^ n,其中计算由模1000执行。 结果是(6*P[0,0] + 28*P[0,1] - 1) mod 1000.

谁能解释我这个解决方案?

3 个答案:

答案 0 :(得分:7)

我将提出一种解决这个问题的方法,甚至不用理解解决方案。

假设您熟悉斐波那契数字:

ghci> let fib = 0 : 1 : zipWith (+) fib (tail fib)
ghci> take 16 fib
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610]

并熟悉其封闭的表格形式:

ghci> let calcFib i = round (((1 + sqrt 5) / 2) ^ i / sqrt 5)
ghci> map calcFib [0..15]
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610]

你注意到((1 + sqrt 5)/ 2) n 和(3 + sqrt 5) n 的相似性。

从这里可以猜测,可能有一系列类似于斐波那契来计算这个。

但是什么系列?所以你计算了前几个项目:

ghci> let calcThing i = floor ((3 + sqrt 5) ^ i)
ghci> map calcThing [0..5]
[1,5,27,143,751,3935]

猜测公式的形式如下:

东西 n = a * thing n-1 + b * thing n-2

我们有:

27 = a * 5 + b * 1

143 = a * 27 + b * 5

我们求解线性方程组得到:

东西 n = 4 * thing n-1 + 7 * thing n-2 (a = 4,b = 7)< / p>

我们检查:

ghci> let thing = 1 : 5 : zipWith (+) (map (* 4) (tail thing)) (map (* 7) thing)
ghci> take 10 thing
[1,5,27,143,761,4045,21507,114343,607921,3232085]
ghci> map calcThing [0..9]
[1,5,27,143,751,3935,20607,107903,564991,2958335]

然后我们发现遗憾的是,这并不能计算我们的功能。但是,我们对它获得最正确的数字这一事实感到欢欣鼓舞。不理解为什么,但在这个事实的鼓舞下,我们尝试类似的东西。要查找修改后的公式的参数:

东西 n = a * thing n-1 + b * thing n-2 + c

然后我们到达:

东西 n = 6 * thing n-1 - 4 * thing n-2 + 1

我们检查一下:

ghci> let thing =
        1 : 5 : map (+1) (zipWith (+)
          (map (*6) (tail thing))
          (map (* negate 4) thing))
ghci> take 16 thing == map calcThing [0..15]
True

答案 1 :(得分:3)

我不知道如何解释,但问题的作者已撰写this analysis

答案 2 :(得分:3)

只是回答一个非常古老的问题:

感谢yairchu我有想法在维基百科页面上重新阅读Binet's formula的证明。它不是那么清楚,但我们可以使用它。

我们在维基百科页面上看到一个封闭的表格,其中“通过舍入计算”:F n =⌊φ/√5⌋ n 。 如果我们可以用3 +√5代替φ/√5(称为后者x)。我们可以很容易地计算x n 的最低值,特别是mod 1000,通过在我们新构造的序列中找到第n个项(这是F的类比(后面我们称之为模拟U))。

我们要找的是什么序列?好吧,我们将尝试遵循Binet公式的证明。我们需要一个以x为根的二次方程。假设x 2 = 6 x-4,这个根有x和y:= 3 - √5。方便的部分现在是:

定义U n (对于每个a和b),例如:

  

U n = a x n + b y n

根据x和y的定义,你可以看到

  

U n = 6 U n-1 - 4 U n-2

现在我们可以自由选择a和b。我们需要U n 为整数,所以我建议选择a = b = 1。现在是U 0 = 2,U 1 = 6,U 2 = 28 ...

我们仍然需要通过四舍五入来进行计算。你可以看到y n &lt;每n为1(因为y≅0.76<1)因此U n = x n + y n =⌈x n ⌉。

如果我们可以计算U n ,我们可以找到⌊x n ⌋,只需减去1。

我们可以通过它的递归公式计算U n ,但这需要O(n)计算时间。我们可以做得更好!

为了计算这样的递归公式,我们可以使用矩阵:

⌈ 0 1⌉ ⌈ U(n-1) ⌉     ⌈ U(n) ⌉
⌊-4 6⌋ ⌊  U(n)  ⌋  =  ⌊U(n+1)⌋

调用此矩阵M.现在M *(U(1),U(2))计算(U(2),U(3))。 现在我们可以计算P = M n-1 (注意我使用的是一个小于n,你可以看到这是正确的,如果你测试小的情况:n = 0,n = 1,n = 2)P *(6,28)现在给出了序列的第n和第(n + 1)项:

(P *(6,28)) 0 - 1 =⌊x n

现在我们可以把所有mod 1000(这简化了计算(很多))并且我们在计算时间O(log(n))中得到了期望的结果(或者甚至更好地利用矩阵幂的计算奇迹(在循环有限域上))。这就解释了这个奇怪的解决方案,我想。