zoukankan      html  css  js  c++  java
  • (结贴)给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方法大家可以无视的,来凑数的。
  • 相关阅读:
    使用C#调用C++类库
    C# IntPtr类型
    C# 调用C++ dll string类型返回
    C# try、catch、finally语句
    C语言 char *、char []、const char *、string的区别与相互转换
    C# 字符串string与char数组互转!
    C#如何调用C++(进阶篇)
    Springboot通过过滤器实现对请求头的修改
    【spring事务】
    命令行参数库:McMaster.Extensions.CommandLineUtils【转】
  • 原文地址:https://www.cnblogs.com/diggingdeeply/p/1529761.html
Copyright © 2011-2022 走看看