费马的小定理方法在数学上是一种合理的方法,只是反复乘以 7 mod 10^6 是最简单的代码,但是您可以采用另一种计算效率高的方法(但需要更复杂的代码)。首先,请注意,当乘以 7 时,最后一位仅取决于之前的最后一位(即我们所做的一切都是 mod 10)。我们反复乘以 7 得到
7 (4)9 (6)3 (2)1 (0)7 ...
好的,太好了,所以如果我们想要一个 3,我们从 7^3 开始,然后每 7^4 上升一次。现在,我们注意到,当乘以 7^4 时,最后两位数字仅取决于 7^4 的最后两位数字和上一个答案的最后两位数字。7^4 是 2401。所以事实上,当上升 7^4 时,最后两位数总是相同的。
最后三个呢?嗯,7^3 = 343 和 7^4 以 401 结尾,所以我们得到 mod 1000
343 543 743 943 143 343
我们在第 2 列 (543) 中得到了前三位数字,我们看到该序列重复了 5 次,所以我们应该从那里上升 7^20。
我们可以一遍又一遍地玩这个把戏:找出下一个数字块重复的频率,在该块中找到正确的子序列,然后乘以 7^n 而不是 7。
我们真正要做的是在第 m 个数字上找到一个(乘法)环,然后将所有环的大小相乘以获得具有相同 N 位的连续幂之间的跨度,如果我们遵循这种方法。这里有一些 Scala 代码(2.8.0 Beta1)就是这样做的:
def powRing(bigmod: BigInt, checkmod: BigInt, mul: BigInt) = {
val powers = Stream.iterate(1:BigInt)(i => (i*mul)%bigmod)
powers.take( 2+powers.tail.indexWhere(_ % checkmod == 1) ).toList
}
def ringSeq(digits: Int, mod: BigInt, mul: BigInt): List[(BigInt,List[BigInt])] = {
if (digits<=1) List( (10:BigInt , powRing(mod,10,mul)) )
else {
val prevSeq = ringSeq(digits-1, mod, mul)
val prevRing = prevSeq.head
val nextRing = powRing(mod,prevRing._1*10,prevRing._2.last)
(prevRing._1*10 , nextRing) :: prevSeq
}
}
def interval(digits: Int, mul: Int) = {
val ring = ringSeq(digits, List.fill(digits)(10:BigInt).reduceLeft(_*_), mul)
(1L /: ring)((p,r) => p * (r._2.length-1))
}
所以,如果我们找到了我们想要的数字的一种情况,我们现在可以通过找到合适的环的大小来找到所有的数字。在我们的例子中,使用 6 位数字(即 mod 10^6)和以 7 为底,我们发现重复大小为:
scala> interval(6,7)
res0: Long = 5000
所以,我们得到了答案!7^7 是第一个,7^5007 是第二个,7^10007 是第三个,等等。
由于这是通用的,我们可以尝试其他答案...11^11 = 285311670611(8 位数字)。我们来看看区间:
scala> interval(12,11)
res1: Long = 50000000000
因此,这告诉我们 11^50000000007 是 11^11 之后的下一个数字,具有相同的 12 位初始集合。如果您好奇,请手动检查!
让我们也检查一下 3^3——小数扩展以 27 结尾的 3 的下一个幂是多少?
scala> interval(2,3)
res2: Long = 20
应该是 3^23。检查:
scala> List.fill(23)(3L).reduceLeft((l,r) => {println(l*r) ; l*r})
9
27
81
243
729
2187
6561
19683
59049
177147
531441
1594323
4782969
14348907
43046721
129140163
387420489
1162261467
3486784401
10460353203
31381059609
94143178827
是的!
(在编辑中切换代码以使用 BigInt,因此它可以处理任意数量的数字。但是,该代码不会检测退化情况,因此请确保使用素数作为幂....)