(结贴)给1~N个数选一

大家讨论了好几天,主流有几个方法,到底哪个是性能最好的呢?我自己花了点时间,写个测试程序,大家参考。估计这个问题可以结贴了。

程序先声明10000个数组,然后初始化为1~9999,然后将顺序打乱,将其中一个为0的值置为随机的重复值。
算法有三个:
1.求和相减(大众解法)
2.异或(来自http://www.cnblogs.com/Ivony/)
3.我自己的方法
每个方法执行N遍,并计算其执行的tick数 (数组规模不大,大了计算容易溢出)。
下面是程序:
class Program
    {
        
static void Main(string[] args)
        {
            
int N =10000;
            
int COUNT = N;
            
long magicNumber=0;
            
long [] array=NewArray(N,ref magicNumber);
            
            List
<long[]> arrayList = new List<long[]>();
            
for (int i = 0; i < COUNT; i++)
            {
                
long [] a=new long [N];
                arrayList.Add(a);
            }

            Stopwatch watch 
= new Stopwatch();
            
for (int i = 0; i < COUNT; i++)
            {
                array.CopyTo(arrayList[i], 
0);
            }
            watch.Start();
            
for (int i = 0; i < COUNT; i++)
            {
                Debug.Assert(magicNumber 
== Method1(arrayList[i]));
            }
            watch.Stop();
            Console.WriteLine(
"Method1 average:{0} ticks", watch.ElapsedTicks / COUNT);
            watch.Reset();

            
for (int i = 0; i < COUNT; i++)
            {
                array.CopyTo(arrayList[i], 
0);
            }
            watch.Start();
            
for (int i = 0; i < COUNT; i++)
            {
                Debug.Assert(magicNumber 
== Method2(arrayList[i]));
            }
            watch.Stop();
            Console.WriteLine(
"Method2 average:{0} ticks", watch.ElapsedTicks / COUNT);

            watch.Reset();
            
for (int i = 0; i < COUNT; i++)
            {
                array.CopyTo(arrayList[i], 
0);
            }
            watch.Start();
            
for (int i = 0; i < COUNT; i++)
            {
                Debug.Assert(magicNumber 
== Method3(arrayList[i]));
            }
            watch.Stop();
            Console.WriteLine(
"Method3 average:{0} ticks", watch.ElapsedTicks / COUNT);
        }
        
//求和
        static long Method1(long[] a)

        {
            
long sum = 0;
            
long sumN=(a.Length * (a.Length - 1)) / 2;
           
                
for (int i = 0; i < a.Length; i++)
                    sum 
+= a[i];
                
return sum - sumN;
            
        }
        
//异或
        static long Method2(long[] a)
        {
            
long temp1=0, temp2=0;

            
for (int i = 0; i < a.Length; i++)
                temp1 
^= a[i];
            
//for (int i = 1; i < a.Length; i++)
            
//    temp2 ^= i;

            
return temp1 ^( a.Length % 2 == 0 ? 0 : a.Length);
        }
        
//??
        static unsafe long Method3(long[] a)
        {
            
fixed (long* p = a)
            {
                
long* p1 = p;
                
//if a[0]==a[1]
                if (*p1 == *(p1 + 1))
                {
                    
//Find!
                    return *p1;
                }
                
//loop array
                while (p1 < p + a.Length)
                {
                    
int* pLittleEndian = (int*)p1;
                    
int* pBigEndian = (int*)(p + *pLittleEndian) + 1;
                    
*pBigEndian = *pLittleEndian;
                    p1
++;
                    pLittleEndian 
= (int*)p1;
                    pBigEndian 
= (int*)(p + *pLittleEndian) + 1;
                    
if (*pBigEndian == *pLittleEndian)
                    {
                        
//Find!
                        break;
                    }
                }

                
return *((int*)p1);
            }
        }

        
static long[] NewArray(int N, ref long n)
        {
            
long[] a = new long[N];
            Random rand 
= new Random();
            
//赋值
            for (long i = 1; i < a.Length; i++)
            {
                a[i] 
= i;
            }

            
int idx1 = 0;
            
int idx2 = 0;
            
//打乱顺序
            for (int i = 0; i < a.Length * 10; i++)
            {
                idx1 
= rand.Next(N);
                idx2 
= rand.Next(N);
                
if (idx1 == idx2)
                {
                    i
--;
                    
continue;
                }
                a[idx1] 
= a[idx1] ^ a[idx2];
                a[idx2] 
= a[idx1] ^ a[idx2];
                a[idx1] 
= a[idx1] ^ a[idx2];
            }
            
//创建重复元素一个
            idx1 = rand.Next(N);
            
while (a[idx1] == 0)
                idx1 
= rand.Next(N);
           
            
for (int i = 0; i < a.Length; i++)
            {
                
if (a[i] == 0)
                {
                    a[i] 
= a[idx1];
                    n 
= a[i];
                    Console.WriteLine(
"重复元素是:a[{0}],a[{1}]:{2}",i,idx1,a[i]);
                    
break;
                }
            }

            
return a;
        }
    }
多次运行,基本上ticks保持在1:1:3.5。其中运行顺序还有些关系,大家可以自己测试一下。
结论:2方法最好,不会溢出;在不考虑溢出的情况下,1和2相当;3方法大家可以无视的,来凑数的。
posted @ 2009-07-23 22:52 DiggingDeeply 阅读(...) 评论(...) 编辑 收藏