wodehuajianrui

博客园 首页 新随笔 联系 订阅 管理
题目来源于ACM竞赛。
要求,输入任意一个正整数,计算得到2^N%N,需要考虑溢出,并且有时间限制。
按照正常的循环求指数的运算,基本上在N比较大的时候一定会超时,因此为了实现这个要求只能寻求时间复杂度小于O(N)的算法。
相应的算法及测试代码如下:
 1    class Program
 2    {
 3        static void Main(string[] args)
 4        {
 5            Console.WriteLine("请输入一个正整数:");
 6            bool flag = false;
 7            int test = 0;
 8            while (!flag)
 9            {
10                string s = Console.ReadLine();
11                flag = int.TryParse(s, out test);
12                flag = flag && (test > 0);
13                if (!flag)
14                    Console.WriteLine("格式不正确,请重新输入:");
15                else
16                    Console.WriteLine("您输入的正整数为:{0}", test);
17            }

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

33
34        static int computeFast(int n)
35        {
36            if (n <= 0)
37                throw new ArgumentException();
38            long result = 1;
39            long helper = 2;
40            int m = n;
41            while (m > 0)
42            {
43                if ((m & 1== 1)
44                    result = (result * helper) % n;
45                helper = ((helper % n) * (helper % n)) % n;
46                m = m >> 1;
47            }

48            return (int)result;
49        }

50
51
52        static int computeSlow(int n)
53        {
54            if (n <= 0)
55                throw new ArgumentException();
56            long result = 1;
57            for (int i = 0; i < n; i++)
58            {
59                result = (result << 1% n;
60            }

61            return (int)result;
62        }

63    }
对于两个算法中用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较大时,差距将比较明显——时间复杂度的区别还是非常值得注意的。
posted on 2009-08-18 12:45  花间蕊  阅读(4820)  评论(0编辑  收藏  举报