【问题标题】:Last digit of a huge number大数的最后一位
【发布时间】:2020-11-12 14:54:01
【问题描述】:

我正在 Codewars 中解决 Last digit of a huge number

我设法找到了计算odd number ^ odd numberodd number ^ even number 的方法。但是我被困在even ^ even/odd,因为数字和它的力量模型之间的关系并不明显。

这是我设法得到的:

lastDigit :: [Integer] -> Integer
lastDigit = (`rem` 10) . go
  where
    go [] = 1
    go [x] = x
    go (x : y : r)
      | odd x && odd y = x ^ (go (y : r) `rem` (x + 1))
      | odd x && even y = x ^ (foldMod y (go r) (x + 1))
      | otherwise = -- any hint?

foldMod :: Integer -> Integer -> Integer -> Integer
foldMod _ 0 _ = 1
foldMod base 1 modulo = base `rem` modulo
foldMod base power modulo = (base * foldMod base (power -1) modulo) `rem` modulo

任何人都可以提供一些关于如何处理偶数情况的提示吗?

【问题讨论】:

  • 提示:(a * b) mod c = (a mod c) * (b mod c) mod c
  • 您实际上可以计算 O(1) 中的最后一位数字(假设您可以在 O(1) 中进行取模,通过分析如何反复相乘最终会产生一个“循环”。
  • 你能用文字说出你打算go 计算什么函数吗?我已经盯着它看了一段时间,但还没有说服自己当前的方程对于任何明显的解释都是正确的。
  • @DanielWagner 是的,在重新考虑并尝试其他一些方法后,我也发现我的方法有问题 T_T
  • @WillemVanOnsem 我不相信这可以在 O(1) 中完成。考虑 lastDigit (replicate n 0) 的奇数与偶数 n

标签: haskell modulo


【解决方案1】:

我建议重新考虑您的方法,并从更通用的功能开始。具体来说,你能计算一下吗

powMod :: Integer -> [Integer] -> Integer

powMod base exponents 在哪里计算问题 mod base 中描述的指数塔?请注意,当您递归时,您将使用不同的base 进行递归——因为各个第一指数的循环长度不一定都是base 的除数。例如,以 10 为底,最后一位为 7 个周期的任何第一个指数每 4 个循环,而不是每 10 个循环;幂的最后一位是这样的:

x               0 1 2 3 4 5 6 7
7^x `mod` 10    1 7 9 3 1 7 9 3

您还需要注意第一个指数本身不在到达的最终循环中的情况;例如,在 base 4 中,我们有:

x              0 1 2 3 4 5 6 7
2^x `mod` 4    1 2 0 0 0 0 0 0

所以你不能仅仅查看x `mod` 1 并从中推断出2^x `mod` 4 是什么,即使循环长度是1。 (还有其他例子,比如2^x `mod` 12,其中的循环长于1,但里面仍然没有原来的2。)

【讨论】:

    【解决方案2】:

    您无需计算整个数字即可知道最后一位数字,有一些快速和较慢的方法可以计算最后一位数字,而无需担心所有其他数字。

    一个可以在 O(log b) 时间内计算 ab 的简单方法是利用以下等式:

    (a×b) mod m = ((a mod m) × (b mod m)) mod m.

    这意味着我们每次都可以计算最后一个数字并使用它。因此这意味着只要我们可以表示到 81 的所有数字,我们就可以计算出 ab mod m powMod m a b:

    powMod :: Int -> Int -> Int -> Int
    powMod m = go
        where go _ 0 = 1
              go a b | even n = r
                     | otherwise = (x * r) `mod` m
                  where r = go ((a*a) `mod` m) (div b 2) `mod` m

    如果我们假设我们可以在恒定时间内计算模数、除以二、检查值是否为偶数等,这将在 O(log b) 中运行。

    但我们甚至可以通过寻找周期来更快地做到这一点。假设我们要计算 7 的幂,那么 7^0 是 1,7^1 是 7,7^2 mod 10 = 9,等等。我们可以做一个乘法表:

    × │ 0 1 2 3 4 5 6 7 8 9
    ──┼──────────────────────
    0 │ 0 0 0 0 0 0 0 0 0 0
    1 │ 1 2 3 4 5 6 7 8 9
    2 │ 4 6 8 0 2 4 8 6
    3 │ 9 2 5 8 1 4 7
    4 │ 6 0 4 8 2 6
    5 │ 5 0 5 0 5
    6 │ 6 2 8 4
    7 │ 9 6 3
    8 | 4 2
    9 | 1

    如果我们因此查看最后一位数字的 7 次幂,我们会看到:

    幂 │ │ 最后一位
    ──────────────────────────
     0 │ │ 1
     1 │ 7*1 │ 7
     2 │ 7*7 │ 9
     3 │ 7*9 │ 3
     4 │ 7*3 │ 1

    因此这意味着有一个循环,实际上,每次乘以 7 之后,我们移动到下一个状态,从而得到下图:

    1  →  7  →  9  →  3
     ↖_______________/

    因此,这意味着循环的长度为四。因此,这意味着如果我们必须计算 7 的例如 374 的幂,那么我们知道这些是长度为 4 的 93 个循环,因此没有影响,再加上两个额外的移动,我们因此知道 7393 是 9,无需计算这个数字。由于此类循环的最大长度为 10,因此可以在恒定时间内确定十进制数的最后一位。

    【讨论】:

    • 7^0 = 1。不知道这对你的答案有多大的影响。
    • @dfeuer:答案的其余部分假设 7^0=1,我认为这是一个错字:s。
    • @dfeuer:但这些不是幂,而是乘法。然后将它们用于第二个表。
    • 我认为这与 kata 的有趣部分无关。具体来说:即使首先计算传递给您的powMod 的指数也很昂贵(并且可能不适合Int),而且您没有解决降低成本的问题。 (您需要为此计算几个小于 10 的模数的周期,例如,并且您不谈论这个。我认为您还需要实现一个操作来有效地检查指数塔是否至少与给定数字,以处理某些循环不会立即开始的问题。)
    • @DanielWagner:但您不需要计算整个指数,因为您只需要找出“循环”的长度。例如,如果我们必须计算 3^(4^5),那么我们知道 3 的循环为 4。所以我们只对 (4^5) 的模感兴趣(如果你愿意,“基数为 4 的数字中的最后一位数字”。然后我们计算 (4^5 mod 4)。因此,我们不仅减少了(3^...),但也属于 (4^5) 本身。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-20
    • 2019-06-26
    • 1970-01-01
    • 2020-10-21
    • 2014-06-10
    • 2017-03-08
    • 2013-06-13
    相关资源
    最近更新 更多