关于我的问题:我在学校有一个作业,但我失败了,因为我的代码运行得太慢了。现在我必须修理和研究它,因为我将不得不解释它如何修复它以及它是如何工作的。
我的问题是:我可以修复我的代码的哪些部分来归档平均性能O(n ^ 2)?我怎样才能让它跑得更快?
需要什么:
Sort()
array
方法,System.Collections.Generic
或其他库不允许(仅允许使用系统库),这是我到目前为止所尝试的,但它的工作方式太慢......我必须排序为200000个数字:
编辑:此代码无法正常运行。我在下面发布了更新版本。
static public int[] StrandSortAscending(int[] p1)
{
int[] p2,
p3;
int p1s1,
p2s1,
p2s2,
p3s1,
p3s2,
n;
p2 = new int[p1.Length];
p3 = new int[p1.Length];
Reset(ref p2);
Reset(ref p3);
p1s1 = p1.Length;
p3s1 = 0;
while (p1s1 != 0)
{
n = int.MinValue;
p2s1 = 0;
for (int i8 = 0; i8 < p1.Length; i8++)
{
if (p1[i8] != int.MaxValue)
{
if (p1[i8] >= n)
{
p2[p2s1] = p1[i8];
n = p1[i8];
p1[i8] = int.MaxValue;
p1s1--;
p2s1++;
}
}
}
int p3p,
zs;
bool pn = false;
for (int i5 = 0; i5 < p2.Length; i5++)
{
p3p = int.MinValue;
for (int i6 = 0; i6 < p3.Length; i6++)
{
if (pn)
{
zs = p3[i6];
p3[i6] = p3p;
p3p = zs;
}
else
{
if (p2[i5] >= p3p && p2[i5] <= p3[i6])
{
p3p = p3[i6];
p3[i6] = p2[i5];
}
}
}
}
Reset(ref p2);
}
return p3;
}
static void Reset(ref int[] a)
{
for (int i = 0; i < a.Length; i++)
a[i] = int.MaxValue;
}
维基百科链接到链排序算法:http://en.wikipedia.org/wiki/Strand_sort
注意:这个算法最大的错误是,我忘了数组是参考数据类型。并且2个数组在内存中指向同一个数组。我花了大部分时间来解决这个问题。我就像......这里到底出了什么问题......然后它终于点击了。 :)
更新(我做了一些升级):
我在这里得到了合并算法的想法:How to merge two sorted arrays into a sorted array?
static public int[] StrandSortAscending2(int[] np)
{
int[] up = new int[np.Length]; // sorted array
int[] zp = new int[np.Length]; // temporary array
int[] nnp = new int[np.Length]; // new unsorted array
int[] sp = new int[np.Length]; // merged sorted array
int dvup = 0; // the length of non-empty elements of the sorted array
int dvnp = np.Length; // the length of non-empty elements of the unsorted array
//0. repeat until the unsorted array isn't empty
while (dvnp > 0)
{
//NOTE: reference data type. 2 arrays point to the same array/table in memoty (RAM),
//from the previous cycle, that's why the values in unsorted array (and temp and merged array) were wrong...
zp = new int[np.Length];
nnp = new int[np.Length];
sp = new int[np.Length];
//these counters are needed for knowing till which index of an array are the elements not-empty
//so that i don't need to always increase and decrease the size of an array, but just overwrite the old values
//the algorithm must not be slow, the higher memory usage is not a problem
int dvzp = 0; // the length of non-empty elements of the temporary array
int dvnnp = 0; // the length of non-empty elements of the new unsorted array
//1.1 fill the temporary and the new unsorted array with values
//1.2 the unsorted array should point then to the new unsorted array
int nszp = int.MinValue; // biggest number of the temporary array
for (int inp = 0; inp < dvnp; inp++) // index of unsorted array
{
if (inp == 0 || np[inp] > nszp)
{
nszp = np[inp];
zp[dvzp++] = np[inp];
}
else
{
nnp[dvnnp++] = np[inp];
}
}
np = nnp;
dvnp = dvnnp;
//2. merge temporary and sorted arrays
int izp = 0; // index/counter of temporary array
int iup = 0; // index/counter of sorted array
int isp = 0; // index/counter of merged array
if (dvup > 0)
{
while (izp < dvzp && iup < dvup)
{
if (zp[izp] < up[iup])
sp[isp++] = zp[izp++];
else
sp[isp++] = up[iup++];
}
//if there are still numbers left in the temporary array
//then add then all to the merged array
//they are all bigger then the ones already in the merged array
while (izp < dvzp)
sp[isp++] = zp[izp++];
//do the same for the sorted array
while (iup < dvup)
sp[isp++] = up[iup++];
// dfdfgdgd
up = sp;
dvup = isp;
}
else
{
up = zp;
dvup = dvzp;
}
}
return up;
}
我可以进一步改善表现吗?这样它会跑得更快吗?
答案 0 :(得分:2)
好吧,我感到无聊并且想要帮助(同样,如果没有答案,就不能留下大量评论的问题:D),所以拿了Wikipedia PHP implementation并将其转换为C#,但稍后用户添加他不能使用泛型...
所以,我发布了一个完整的实现,一个使用泛型,另一个使用它自己的LinkedList,它有点长但是它有效(采用维基百科示例,一步一步,它的行为与示例表完全相同)和它也被评论,所以它很容易理解(链表代码没有被评论)。
它来了!
public class StrandSort
{
public static int[] SortWithGenerics(int[] Values)
{
List<int> Original = new List<int>();
List<int> Result = new List<int>();
List<int> Sublist = new List<int>();
Original.AddRange(Values);
//While we still have numbers to sort
while (Original.Count > 0)
{
//Clear sublist and take first available number from original to the new sublist
Sublist.Clear();
Sublist.Add(Original[0]);
Original.RemoveAt(0);
//Iterate through original numbers
for (int x = 0; x < Original.Count; x++)
{
//If the number is bigger than the last item in the sublist
if (Original[x] > Sublist[Sublist.Count - 1])
{
//Add it to the sublist and remove it from original
Sublist.Add(Original[x]);
Original.RemoveAt(x);
//Roll back one position to compensate the removed item
x--;
}
}
//If this is the first sublist
if (Result.Count == 0)
Result.AddRange(Sublist); //Add all the numbers to the result
else
{
//Iterate through the sublist
for (int x = 0; x < Sublist.Count; x++)
{
bool inserted = false;
//Iterate through the current result
for (int y = 0; y < Result.Count; y++)
{
//Is the sublist number lower than the current item from result?
if (Sublist[x] < Result[y])
{
//Yes, insert it at the current Result position
Result.Insert(y, Sublist[x]);
inserted = true;
break;
}
}
//Did we inserted the item because found it was lower than one of the result's number?
if (!inserted)
Result.Add(Sublist[x]);//No, we add it to the end of the results
}
}
}
//Return the results
return Result.ToArray();
}
public static int[] SortWithoutGenerics(int[] Values)
{
IntLinkedList Original = new IntLinkedList();
IntLinkedList Result = new IntLinkedList();
IntLinkedList Sublist = new IntLinkedList();
Original.AddRange(Values);
//While we still have numbers to sort
while (Original.Count > 0)
{
//Clear sublist and take first available number from original to the new sublist
Sublist.Clear();
Sublist.Add(Original.FirstItem.Value);
Original.Remove(Original.FirstItem);
IntLinkedItem currentOriginalItem = Original.FirstItem;
//Iterate through original numbers
while (currentOriginalItem != null)
{
//If the number is bigger than the last item in the sublist
if (currentOriginalItem.Value > Sublist.LastItem.Value)
{
//Add it to the sublist and remove it from original
Sublist.Add(currentOriginalItem.Value);
//Store the next item
IntLinkedItem nextItem = currentOriginalItem.NextItem;
//Remove current item from original
Original.Remove(currentOriginalItem);
//Set next item as current item
currentOriginalItem = nextItem;
}
else
currentOriginalItem = currentOriginalItem.NextItem;
}
//If this is the first sublist
if (Result.Count == 0)
Result.AddRange(Sublist); //Add all the numbers to the result
else
{
IntLinkedItem currentSublistItem = Sublist.FirstItem;
//Iterate through the sublist
while (currentSublistItem != null)
{
bool inserted = false;
IntLinkedItem currentResultItem = Result.FirstItem;
//Iterate through the current result
while (currentResultItem != null)
{
//Is the sublist number lower than the current item from result?
if (currentSublistItem.Value < currentResultItem.Value)
{
//Yes, insert it at the current Result position
Result.InsertBefore(currentResultItem, currentSublistItem.Value);
inserted = true;
break;
}
currentResultItem = currentResultItem.NextItem;
}
//Did we inserted the item because found it was lower than one of the result's number?
if (!inserted)
Result.Add(currentSublistItem.Value);//No, we add it to the end of the results
currentSublistItem = currentSublistItem.NextItem;
}
}
}
//Return the results
return Result.ToArray();
}
public class IntLinkedList
{
public int count = 0;
IntLinkedItem firstItem = null;
IntLinkedItem lastItem = null;
public IntLinkedItem FirstItem { get { return firstItem; } }
public IntLinkedItem LastItem { get { return lastItem; } }
public int Count { get { return count; } }
public void Add(int Value)
{
if (firstItem == null)
firstItem = lastItem = new IntLinkedItem { Value = Value };
else
{
IntLinkedItem item = new IntLinkedItem{ PreviousItem = lastItem, Value = Value };
lastItem.NextItem = item;
lastItem = item;
}
count++;
}
public void AddRange(int[] Values)
{
for (int buc = 0; buc < Values.Length; buc++)
Add(Values[buc]);
}
public void AddRange(IntLinkedList Values)
{
IntLinkedItem item = Values.firstItem;
while (item != null)
{
Add(item.Value);
item = item.NextItem;
}
}
public void Remove(IntLinkedItem Item)
{
if (Item == firstItem)
firstItem = Item.NextItem;
if (Item == lastItem)
lastItem = Item.PreviousItem;
if(Item.PreviousItem != null)
Item.PreviousItem.NextItem = Item.NextItem;
if (Item.NextItem != null)
Item.NextItem.PreviousItem = Item.PreviousItem;
count--;
}
public void InsertBefore(IntLinkedItem Item, int Value)
{
IntLinkedItem newItem = new IntLinkedItem { PreviousItem = Item.PreviousItem, NextItem = Item, Value = Value };
if (Item.PreviousItem != null)
Item.PreviousItem.NextItem = newItem;
Item.PreviousItem = newItem;
if (Item == firstItem)
firstItem = newItem;
count++;
}
public void Clear()
{
count = 0;
firstItem = lastItem = null;
}
public int[] ToArray()
{
int[] results = new int[Count];
int pos = 0;
IntLinkedItem item = firstItem;
while (item != null)
{
results[pos++] = item.Value;
item = item.NextItem;
}
return results;
}
}
public class IntLinkedItem
{
public int Value;
internal IntLinkedItem PreviousItem;
internal IntLinkedItem NextItem;
}
}
编辑:它实际上不是链接列表而是双链表;)
编辑:更正了非通用实现中缺少的“其他”
编辑:我真的无聊,并创建了用户代码的重构和更正版本,注释了更改,以便他可以从中学习;)
用户注意:我建议您为变量使用更有意义的名称,在阅读时更容易理解代码。
static public int[] SortCorrectedUserCode(int[] Source)
{
int[] Sublist,
Results;
int ItemsLeft,
SublistPos,
ResultPos;//new variable to store current pos in results
//n; n was useless
Sublist = new int[Source.Length];
Results = new int[Source.Length];
//Avoid resets just using an integer to track array lengths
//Reset(ref Sublist);
//Reset(ref Results);
ItemsLeft = Source.Length;
ResultPos = 0;
while (ItemsLeft != 0)
{
//n = int.MinValue;
SublistPos = 0;
for (int currentSourcePos = 0; currentSourcePos < Source.Length; currentSourcePos++)
{
if (Source[currentSourcePos] != int.MaxValue)
{
//Added special treatment for first item in sublist (copy it yes or yes ;D)
if (SublistPos == 0 || Source[currentSourcePos] > Sublist[SublistPos])
{
Sublist[SublistPos] = Source[currentSourcePos];
//n = Source[currentSourcePos]; useless
Source[currentSourcePos] = int.MaxValue;
ItemsLeft--;
SublistPos++;
}
}
}
//int p3p, zs;
//pn is never true...
//bool pn = false;
//Sublist was being iterated for all it's length, not only for the current items
//for (int currentSublistPos = 0; currentSublistPos < Sublist.Length; currentSublistPos++)
for (int currentSublistPos = 0; currentSublistPos < SublistPos; currentSublistPos++)
{
//p3p = int.MinValue;
bool inserted = false;
//Results was being iterated for all it's length, not only for current items
//for (int currentResultPos = 0; currentResultPos < Results.Length; currentResultPos++)
for (int currentResultPos = 0; currentResultPos < ResultPos; currentResultPos++)
{
//This part was never used...
//if (pn)
//{
// zs = Results[currentResultPos];
// Results[currentResultPos] = p3p;
// p3p = zs;
//}
//else
//{
//This IF was wrong, when the code entered this piece of code it started
//for subsequent iterations in the current loop to copy data from sublist to list, which is not correct ... I think, not sure
//because it's really confusing
//if (Sublist[currentSublistPos] >= p3p && Sublist[currentSublistPos] <= Results[currentResultPos])
//{
//p3p = Results[currentResultPos];
//Results[currentResultPos] = Sublist[currentSublistPos];
//}
//}
//New code, if the item at sublist is lower than the one at results then insert item in current position
if (Sublist[currentSublistPos] < Results[currentResultPos])
{
InsertInArray(currentResultPos, Sublist[currentSublistPos], Results);
inserted = true;
break;
}
}
//Did we inserted the item?
if (!inserted)
Results[ResultPos] = Sublist[currentSublistPos]; //If no then just add it to the end of the results
ResultPos++;
}
//Reset(ref Sublist);
}
return Results;
}
//Helper function to insert a value in an array and displace values to the right
static void InsertInArray(int Index, int Value, int[] Target)
{
//Create temp array of right items
int[] tempArray = new int[(Target.Length - Index) - 1];
//Copy items to temp array
Array.Copy(Target, Index, tempArray, 0, tempArray.Length);
//Set value at index
Target[Index] = Value;
//Copy values from temp array to correct position
Array.Copy(tempArray, 0, Target, Index + 1, tempArray.Length);
}
还对功能进行了一些基准测试(因为用户关心速度),这些是我在调试模式下运行的结果(对于5000个项目,没有耐心测试更长的数组,因为旧代码非常慢):
答案 1 :(得分:2)
可能最重要的是,the Wikipedia article说关于链排序:
使用其他数据结构(如数组)会因漫长的插入和删除而大大增加算法的运行时间和复杂性。
快速浏览一下代码表明它可能确实是O(n ^ 3),因为每次通过主p2
循环都会清除while
数组。
我必须遗漏一些东西,因为我不知道你的算法是如何终止的。你在主循环的开头有这个:
while (p1s1 != 0)
{
n = int.MinValue;
p2s1 = 0;
for (int i8 = 0; i8 < p1.Length; i8++)
{
if (p1[i8] != int.MaxValue)
{
if (p1[i8] >= n)
{
p2[p2s1] = p1[i8];
n = p1[i8];
p1[i8] = int.MaxValue;
p1s1--;
p2s1++;
}
}
}
让我们看一下这两个if
语句:
if (p1[i8] != int.MaxValue)
{
if (p1[i8] >= n)
您在循环开始时将n
设置为int.MaxValue
。这里的第二个条件是检查p1[i8]
是否大于或等于n
。但它不能超过int.MaxValue
,并且它不能等于int.MaxValue
,因为如果它超过它将不会超过第一个条件。
我没有看到n
分配了值的其他任何地方。因为p1s1
唯一被改变的地方是,如果第二个条件评估为true
(并且我们知道它不能),那么while
循环将永远存在,因为p1s1
永远不等于0。