Find value of n^k modulo 10^7 + 7.

huangapple go评论89阅读模式
英文:

Find value of n^k modulo 10^7 + 7

问题

给定整数 n 和 k,请找出 n^k 对 10^7+7 取模的值。我一直在尝试以不同的方式解决这个问题,利用取模运算的性质,即 (a * b) % c = ((a % c) * (b % c)) % c。以下是我的代码。另外,我是一个新用户,如果我漏掉了什么,请告诉我。

  1. int res = 1;
  2. int modulo = (int) Math.pow(10, 7) + 7;
  3. //(a * b) % c = ((a % c) * (b % c)) % c
  4. System.out.println(modulo);
  5. for (int i = 1; i <= k; i++){
  6. res = ((res % modulo) * (n % modulo)) % modulo;
  7. }
  8. return res;
英文:

Giving int n and k. Find the value of n^k modulo 10^7+7.
I 've been trying to do this problem in different ways using the properties of modulo as ( a * b) % c = ( ( a % c ) * ( b % c ) ) % c . Below is my code. Also, I am a new user, if I missed anything please let me know.

  1. int res = 1;
  2. int modulo = (int) Math.pow(10,7)+7;
  3. //( a * b) % c = ( ( a % c ) * ( b % c ) ) % c
  4. System.out.println(modulo);
  5. for (int i = 1 ; i &lt;= k; i++){
  6. res = ((res%modulo)*(n%modulo))%modulo;
  7. }
  8. return res;

答案1

得分: 1

下面两种解决方案的时间复杂度为_O(log k)_,与问题中的代码(O(k))和WJS的答案使用BigInteger(大整数)不同。当k为6位数时,WJS的代码开始变慢。下面两种解决方案即使在kLong.MAX_VALUE时也会立即返回。


在本答案的描述中,a^b表示指数运算,而不是按位异或。

你已经在正确的路径上,使用了以下等式:

  1. (a * b) % c = ((a % c) * (b % c)) % c

你所忽略的是:

  1. a ^ (b + c) = (a ^ b) * (a ^ c)

递归解法

由于目标是计算(n^k) % (10^7 + 7),你可以通过递归地分割k来在_O(log k)_时间内计算这个值。

例如,如果k = 15,那么可以这样分割:

  1. n^15 = n^8 * n^7
  2. = (n^4 * n^4) * (n^4 * n^3)
  3. = ...

用模运算表示:

  1. (n^15) % m = ((n^8) % m * (n^7) % m) % m
  2. = ...

如果你创建一个用于计算(n^k) % m的方法:

  1. int calc(int n, int k, int m)

那么当k = 15时,calc(n, 15, m)会递归地调用calc(n, 8, m)calc(n, 7, m)。使用记忆化(memoization),结果的时间复杂度为_O(log k)_。

  1. public static int calc(int n, long k) {
  2. if (n < 0 || k < 0)
  3. throw new IllegalArgumentException();
  4. if (n == 0)
  5. return 0;
  6. if (k == 0)
  7. return 1;
  8. return calc0(n, k, new HashMap<>());
  9. }
  10. private static int calc0(int n, long k, Map<Integer, Integer> cache) {
  11. Integer cachedValue = cache.get(k);
  12. if (cachedValue != null)
  13. return cachedValue;
  14. int result;
  15. if (k == 1)
  16. result = n % MODULO;
  17. else
  18. result = (int) ((long) calc0(n, k / 2, cache) * calc0(n, k - k / 2, cache) % MODULO);
  19. cache.put(k, result);
  20. return result;
  21. }
  22. private static final int MODULO = 10000007;

位运算解法

如果你不喜欢递归和记忆化,你可以使用位操作。实际上,这比递归方法稍微更有效率,有时使用更少的乘法和求余操作。

我不会对此进行解释。看看你是否能够弄清楚它是如何工作的。

  1. public static int calc(int n, long k) {
  2. if (n < 0 || k < 0)
  3. throw new IllegalArgumentException();
  4. int e = n % MODULO;
  5. int r = ((k & 1) != 0 ? e : 1);
  6. for (long i = k >>> 1; i != 0; i >>>= 1) {
  7. e = (int) ((long) e * e % MODULO);
  8. if ((i & 1) != 0)
  9. r = (int) ((long) r * e % MODULO);
  10. }
  11. return r;
  12. }
  13. private static final int MODULO = 10000007;

测试

  1. System.out.println(calc(282828,292929));
  2. System.out.println(calc(123456789,987654321));
  3. System.out.println(calc(Integer.MAX_VALUE, Long.MAX_VALUE));

输出

  1. 6154601
  2. 5111924
  3. 5910478
英文:

Both solutions below run in O(log k), unlike e.g. the code in the question (O(k)), and the answer by WJS using BigInteger. WJS's code starts getting slow when k is 6 digits. Both solutions below returns immediately even when k is Long.MAX_VALUE.


In the description in this answer, a^b means exponentiation, not bitwise xor.

You're on the right path using

  1. (a * b) % c = ((a % c) * (b % c)) % c

What you're missing is

  1. a ^ (b + c) = (a ^ b) * (a ^ c)

<h3>Recursive solution</h3>

Since the goal is to calculate (n^k) % (10^7 + 7), you can calculate this in O(log k) time by recursively splitting k.

E.g. if k = 15 then split like this

  1. n^15 = n^8 * n^7
  2. = (n^4 * n^4) * (n^4 * n^3)
  3. = ...

With modulo that means

  1. (n^15) % m = ((n^8) % m * (n^7) % m) % m
  2. = ...

If you create a method for calculating (n^k) % m

  1. int calc(int n, int k, int m)

then with k = 15, calc(n, 15, m) calls calc(n, 8, m) and calc(n, 7, m) recursively. With memoization, the result has O(log k) time complexity.

  1. public static int calc(int n, long k) {
  2. if (n &lt; 0 || k &lt; 0)
  3. throw new IllegalArgumentException();
  4. if (n == 0)
  5. return 0;
  6. if (k == 0)
  7. return 1;
  8. return calc0(n, k, new HashMap&lt;&gt;());
  9. }
  10. private static int calc0(int n, long k, Map&lt;Integer, Integer&gt; cache) {
  11. Integer cachedValue = cache.get(k);
  12. if (cachedValue != null)
  13. return cachedValue;
  14. int result;
  15. if (k == 1)
  16. result = n % MODULO;
  17. else
  18. result = (int) ((long) calc0(n, k / 2, cache) * calc0(n, k - k / 2, cache) % MODULO);
  19. cache.put(k, result);
  20. return result;
  21. }
  22. private static final int MODULO = 10000007;

<h3>Bitwise solution</h3>

If you don't like recursion and memoization, you can use bit manipulation. This is actually slightly more efficient than the recursive approach, sometimes using fewer multiplication and remainder operations.

I won't explain it. See if you can figure out how that works.

  1. public static int calc(int n, long k) {
  2. if (n &lt; 0 || k &lt; 0)
  3. throw new IllegalArgumentException();
  4. int e = n % MODULO;
  5. int r = ((k &amp; 1) != 0 ? e : 1);
  6. for (long i = k &gt;&gt;&gt; 1; i != 0; i &gt;&gt;&gt;= 1) {
  7. e = (int) ((long) e * e % MODULO);
  8. if ((i &amp; 1) != 0)
  9. r = (int) ((long) r * e % MODULO);
  10. }
  11. return r;
  12. }
  13. private static final int MODULO = 10000007;

<h3>Test</h3>

  1. System.out.println(calc(282828,292929));
  2. System.out.println(calc(123456789,987654321));
  3. System.out.println(calc(Integer.MAX_VALUE, Long.MAX_VALUE));

Output

  1. 6154601
  2. 5111924
  3. 5910478

答案2

得分: 0

这是最简单的。您可以使用它来检查您的其他答案。

  1. System.out.println(fnc(282828,292929));
  2. System.out.println(yours(282828,292929));
  3. public static BigInteger fnc(int n, int k) {
  4. int modulo = (int) Math.pow(10, 7) + 7;
  5. BigInteger res = BigInteger.valueOf(n).pow(k)
  6. .mod(BigInteger.valueOf(modulo));
  7. return res;
  8. }

这是您的解决方案。您只需要使用long类型:

  1. public static long yours(long n, long k) {
  2. long res = 1;
  3. int modulo = (int) Math.pow(10, 7) + 7;
  4. // ( a * b) % c = ( ( a % c ) * ( b % c ) ) % c
  5. // 将 n % modulo 单独提取出来,因为 n 永远不会变化。
  6. long nmod = n % modulo;
  7. res = nmod % modulo;
  8. for (int i = 1; i < k; i++) {
  9. res = ((res % modulo) * (nmod)) % modulo;
  10. }
  11. return res;
  12. }

它们都打印出:

  1. 6154601
  2. 6154601

轻微的优化。

  • n % modulo提取为nmod。只需计算一次。
  • 由于第一次1 % modulo1,只需将res初始化为nmod % modulo。然后只需迭代i < k次。
英文:

This is easiest. You can use it to check your other answers.

  1. System.out.println(fnc(282828,292929));
  2. System.out.println(yours(282828,292929));
  3. public static BigInteger fnc(int n, int k) {
  4. int modulo = (int) Math.pow(10, 7) + 7;
  5. BigInteger res = BigInteger.valueOf(n).pow(k)
  6. .mod(BigInteger.valueOf(modulo));
  7. return res;
  8. }

And here is your solution. You only needed to use long&#39;s

  1. public static long yours(long n, long k) {
  2. long res = 1;
  3. int modulo = (int) Math.pow(10, 7) + 7;
  4. // ( a * b) % c = ( ( a % c ) * ( b % c ) ) % c
  5. // pull out n % modulo since n never changes.
  6. long nmod = n% modulo;
  7. long res = nmod%modulo;
  8. for (int i = 1; i &lt; k; i++) {
  9. res = ((res% modulo)*(nmod))%modulo;
  10. }
  11. return res;
  12. }

They both print

  1. 6154601
  2. 6154601

Slight optimizations.

  • pull out n % modulo as nmod. Just compute once.
  • since 1 % modulo is 1 first time thru, just initialize res to nmod % modulo. Then just iterate to i &lt; k times.

huangapple
  • 本文由 发表于 2020年4月4日 07:16:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/61021793.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定