以O(logN)时间复杂度计算2的N次方的算法

题目来源于ACM竞赛。

要求,输入任意一个正整数,计算得到2^N%N,需要考虑溢出,并且有时间限制。

按照正常的循环求指数的运算,基本上在N比较大的时候一定会超时,因此为了实现这个要求只能寻求时间复杂度小于O(N)的算法。

相应的算法及测试代码如下:

 1
《以O(logN)时间复杂度计算2的N次方的算法》
    
class
 Program

 2
《以O(logN)时间复杂度计算2的N次方的算法》《以O(logN)时间复杂度计算2的N次方的算法》    
《以O(logN)时间复杂度计算2的N次方的算法》
{
 3《以O(logN)时间复杂度计算2的N次方的算法》        static void Main(string[] args)
 4《以O(logN)时间复杂度计算2的N次方的算法》《以O(logN)时间复杂度计算2的N次方的算法》        《以O(logN)时间复杂度计算2的N次方的算法》{
 5《以O(logN)时间复杂度计算2的N次方的算法》            Console.WriteLine(请输入一个正整数:);
 6《以O(logN)时间复杂度计算2的N次方的算法》            bool flag = false;
 7《以O(logN)时间复杂度计算2的N次方的算法》            int test = 0;
 8《以O(logN)时间复杂度计算2的N次方的算法》            while (!flag)
 9《以O(logN)时间复杂度计算2的N次方的算法》《以O(logN)时间复杂度计算2的N次方的算法》            《以O(logN)时间复杂度计算2的N次方的算法》{
10《以O(logN)时间复杂度计算2的N次方的算法》                string s = Console.ReadLine();
11《以O(logN)时间复杂度计算2的N次方的算法》                flag = int.TryParse(s, out test);
12《以O(logN)时间复杂度计算2的N次方的算法》                flag = flag && (test > 0);
13《以O(logN)时间复杂度计算2的N次方的算法》                if (!flag)
14《以O(logN)时间复杂度计算2的N次方的算法》                    Console.WriteLine(格式不正确,请重新输入:);
15《以O(logN)时间复杂度计算2的N次方的算法》                else
16《以O(logN)时间复杂度计算2的N次方的算法》                    Console.WriteLine(您输入的正整数为:{0}, test);
17《以O(logN)时间复杂度计算2的N次方的算法》            }

18《以O(logN)时间复杂度计算2的N次方的算法》
19《以O(logN)时间复杂度计算2的N次方的算法》            Stopwatch sw = new Stopwatch();
20《以O(logN)时间复杂度计算2的N次方的算法》            sw.Start();
21《以O(logN)时间复杂度计算2的N次方的算法》            int fast = computeFast(test);
22《以O(logN)时间复杂度计算2的N次方的算法》            sw.Stop();
23《以O(logN)时间复杂度计算2的N次方的算法》            Console.WriteLine(O(logN)计算结果为{0}, fast);
24《以O(logN)时间复杂度计算2的N次方的算法》            Console.WriteLine(O(logN)算法耗时{0}ms, sw.ElapsedMilliseconds);
25《以O(logN)时间复杂度计算2的N次方的算法》            sw.Reset();
26《以O(logN)时间复杂度计算2的N次方的算法》            sw.Start();
27《以O(logN)时间复杂度计算2的N次方的算法》            int slow = computeSlow(test);
28《以O(logN)时间复杂度计算2的N次方的算法》            sw.Stop();
29《以O(logN)时间复杂度计算2的N次方的算法》            Console.WriteLine(O(N)计算结果为{0}, slow);
30《以O(logN)时间复杂度计算2的N次方的算法》            Console.WriteLine(O(N)算法耗时{0}ms, sw.ElapsedMilliseconds);
31《以O(logN)时间复杂度计算2的N次方的算法》            Console.Read();
32《以O(logN)时间复杂度计算2的N次方的算法》        }

33《以O(logN)时间复杂度计算2的N次方的算法》
34《以O(logN)时间复杂度计算2的N次方的算法》        static int computeFast(int n)
35《以O(logN)时间复杂度计算2的N次方的算法》《以O(logN)时间复杂度计算2的N次方的算法》        《以O(logN)时间复杂度计算2的N次方的算法》{
36《以O(logN)时间复杂度计算2的N次方的算法》            if (n <= 0)
37《以O(logN)时间复杂度计算2的N次方的算法》                throw new ArgumentException();
38《以O(logN)时间复杂度计算2的N次方的算法》            long result = 1;
39《以O(logN)时间复杂度计算2的N次方的算法》            long helper = 2;
40《以O(logN)时间复杂度计算2的N次方的算法》            int m = n;
41《以O(logN)时间复杂度计算2的N次方的算法》            while (m > 0)
42《以O(logN)时间复杂度计算2的N次方的算法》《以O(logN)时间复杂度计算2的N次方的算法》            《以O(logN)时间复杂度计算2的N次方的算法》{
43《以O(logN)时间复杂度计算2的N次方的算法》                if ((m & 1== 1)
44《以O(logN)时间复杂度计算2的N次方的算法》                    result = (result * helper) % n;
45《以O(logN)时间复杂度计算2的N次方的算法》                helper = ((helper % n) * (helper % n)) % n;
46《以O(logN)时间复杂度计算2的N次方的算法》                m = m >> 1;
47《以O(logN)时间复杂度计算2的N次方的算法》            }

48《以O(logN)时间复杂度计算2的N次方的算法》            return (int)result;
49《以O(logN)时间复杂度计算2的N次方的算法》        }

50《以O(logN)时间复杂度计算2的N次方的算法》
51《以O(logN)时间复杂度计算2的N次方的算法》
52《以O(logN)时间复杂度计算2的N次方的算法》        static int computeSlow(int n)
53《以O(logN)时间复杂度计算2的N次方的算法》《以O(logN)时间复杂度计算2的N次方的算法》        《以O(logN)时间复杂度计算2的N次方的算法》{
54《以O(logN)时间复杂度计算2的N次方的算法》            if (n <= 0)
55《以O(logN)时间复杂度计算2的N次方的算法》                throw new ArgumentException();
56《以O(logN)时间复杂度计算2的N次方的算法》            long result = 1;
57《以O(logN)时间复杂度计算2的N次方的算法》            for (int i = 0; i < n; i++)
58《以O(logN)时间复杂度计算2的N次方的算法》《以O(logN)时间复杂度计算2的N次方的算法》            《以O(logN)时间复杂度计算2的N次方的算法》{
59《以O(logN)时间复杂度计算2的N次方的算法》                result = (result << 1% n;
60《以O(logN)时间复杂度计算2的N次方的算法》            }

61《以O(logN)时间复杂度计算2的N次方的算法》            return (int)result;
62《以O(logN)时间复杂度计算2的N次方的算法》        }

63《以O(logN)时间复杂度计算2的N次方的算法》    }
对于两个算法中用long去申请局部变量的原因是为了防止溢出,尽管N是一个int变量,但是由于int集合在乘法运算中并不满足闭包性质,所以只有利用long才能解决问题。至于结果由于是已经%N,因此它一定小于N,仍然是一个int变量。

主要来说,这里面运用的数学定理不多,主要就是(a*b)%n=(a%n)*(b%n)%n。如果没有这个公式,可以说由于指数运算,long也必然无法满足要求而导致溢出。

至于第一种算法,主要就是将指数N分解成为二进制的形式,利用一个helper变量来维护当前需要乘的值。利用的是公式a^(m+n)=a^m*a^n。

经过测试,当N较大时,差距将比较明显——时间复杂度的区别还是非常值得注意的。

    原文作者:算法小白
    原文地址: https://www.cnblogs.com/wodehuajianrui/articles/1548765.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞