(结贴)给1~N个数选一
大家讨论了好几天,主流有几个方法,到底哪个是性能最好的呢?我自己花了点时间,写个测试程序,大家参考。估计这个问题可以结贴了。
程序先声明10000个数组,然后初始化为1~9999,然后将顺序打乱,将其中一个为0的值置为随机的重复值。
算法有三个:
1.求和相减(大众解法)
2.异或(来自http://www.cnblogs.com/Ivony/)
3.我自己的方法
每个方法执行N遍,并计算其执行的tick数 (数组规模不大,大了计算容易溢出)。
下面是程序:
结论:2方法最好,不会溢出;在不考虑溢出的情况下,1和2相当;3方法大家可以无视的,来凑数的。
程序先声明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方法大家可以无视的,来凑数的。