【发布时间】:2012-08-20 22:31:48
【问题描述】:
以前有人问过这个问题,但是没有一个得到明确的回答,我尝试编译在这里找到的所有信息。如有必要,请随意合并/移动到另一个 stackexchange 站点。
以下是我发现的与此相关的问题:
该问题最初是作为 Interviewstreet Code Sprint 发布的,但现在它被列为 a practice problem。也是ported to SPOJ。
这是问题陈述:
这是洗 N 张牌的算法:
1) 将卡片分成 K 个相等的堆。
2) 底部的 N / K 张牌 属于第1堆的顺序相同(所以最初的底牌 桩是桩 1) 的底牌。
3) 接下来的 N / K 张卡片来自 底部属于第 2 堆,以此类推。
4) 现在洗好的牌堆的顶牌就是牌堆 1 的顶牌。 下一张牌是第 2 堆的最上面的牌,...,洗好的牌堆的第 K 张牌是第 K 堆的最上面的牌。那么第(K + 1)张牌就是现在在牌堆顶部的牌 堆 1,(K + 2)nd 是现在在堆 2 顶部的卡 等等。
例如,如果 N = 6 且 K = 3,则一副纸牌的顺序为“ABCDEF” (从上到下)洗牌一次时将变为“ECAFDB”。
给定 N 和 K,之后最少需要多少次随机播放 堆恢复到原来的顺序了吗?
输入:第一行包含测试用例的数量T。下一个T 行包含两个整数,每个整数 N 和 K。
输出:输出 T 行,每个测试用例一个包含最小值 需要的洗牌次数。如果甲板永远不会回到它的 原始顺序,输出-1。
约束:
- K 将是 N 的一个因子。
- T
- 2
剧透警告 - 如果您想自己解决,请不要阅读下文。
问题可以翻译为:
求K-way(完美)in-shuffle需要执行的次数 将一副 N 牌恢复到初始顺序。
我采取了两种方法来解决这个问题。想到的第一个方法是:
- 找到一个公式,给定初始顺序中的位置将生成卡片的下一个位置
- 使用公式确定从第一堆中的每张牌(n / k 大小)返回其初始位置所需的洗牌次数
- 返回之前确定的洗牌次数的最小公倍数
这个解决方案的复杂度是 O(n / k + max_number_of_suhffles)。 Here's the actual implementation。这样做的问题是它超过了最长时间,所以我开始寻找一个可以让我在接近恒定的时间内得到数字的公式。
我在这里可以优化的最多(例如,使用一些地图来缓存相同排列循环中的计算值等)是让它通过 interviewstreet 上的 3/10 测试。
我找到this implementation,它假设返回初始状态所需的洗牌次数是K相对于N + 1的multiplicative order。来自wiki:
As a consequence of Lagrange's theorem, ordn(a) always divides φ(n).
φ(n) 是Euler totient function,ordn 是group order——我们正在寻找的。我发现this paper 使用 φ 来计算 shuffle 的数量,但它仅适用于 2-way in-shuffle,而不是 k-way。
以下是此实施的步骤:
- 预计算素数列表
- 根据其主要因数计算
φ(N+1)。 - 通过以所有可能的方式组合其素因子来确定
φ(N + 1)的所有因子。 - 依次尝试每个因素,得到最小的一个,
x,验证k ^ x % N + 1 = 1
这个实现也是posted on GitHub。
这运行得非常快,但自动评分器在 SPOJ 和 Interviewstreet 上的 10 次测试中有 9 次给了我“错误答案”分类。
我尝试比较两个实现的输出,但是对于我输入的测试用例(已知结果和随机),两个实现总是输出相同的东西。这很奇怪,因为我很确定第一个算法是正确的,我假设第二个算法也应该是正确的。
“错误答案”分类可能来自代码中的运行时错误,但没有任何可能的原因跳出。
我没有考虑到没有数字洗牌可以使牌组恢复到初始状态的情况——我的理解是这是不可能的。有限数量的完美洗牌最终将恢复初始排序,即使洗牌的数量可能非常高。
如果您花时间阅读本文,谢谢。 :) 我很好奇这个问题,我想解决它。
【问题讨论】:
-
Same Here.. 我用数学公式尝试过,7/10 案例显示超出时间限制。虽然我不确定测试用例。
-
请注意,在spoj.pl/problems/CODESPTC 中,我们有 2
-
在看了上面的内容,并摆弄了 3*2 和 3*3 的情况后,我猜想如果你将卡片标记为 1、2、3...N,那么洗牌会留下包x, 2x, 3x, ..Nx mod (N+1),您可以通过查看位置 1 中的内容来计算 x 是什么。如果是这样,您需要 x 在数字组中的顺序 mod N+1 与 N+1 互质——我认为这是您的第二种方法,乘数略有不同。您能否通过简单地检查 x、x^2、x^3... 来检查这个想法并简化您的代码,直到找到一个为 1 的结果?
-
感谢提示,明天试试。今天的工作妨碍了:)。