程序先声明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。其中运行顺序还有些关系,大家可以自己测试一下。
{
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;
}
}
结论:2方法最好,不会溢出;在不考虑溢出的情况下,1和2相当;3方法大家可以无视的,来凑数的。