我有一个整数数组,如果我按大小排序,我想改变它们的位置值,目前我这样做:
var result = { 5,4,6,12,1,0 };
var order = new int[result.Length];
for (int i = 0; i < order.Length; i++)
{
order[i] = i;
}
Array.Sort(result,order);
for (int i = 0; i < result.Length; i++)
{
result[i] = i;
}
Array.Sort(order, result);
//output: result = { 3,2,4,5,1,0 }
但是,根据Visual Studio的分析,不仅我觉得这是非常低效的,而且那些类型占用了我大约80%的CPU时间。 我怎样才能让它更快?
答案 0 :(得分:1)
这个怎么样:
var order = Enumerable.Range( 0, results.Length ).OrderBy( i => results[i] ).ToArray();
那应该给你一个数组,这样就可以按顺序打印results
数组而不修改结果数组:
foreach( var index in order )
{
Console.WriteLine( results[index] );
}
编辑: 如上所述,上述内容未提供所需的结果。但是,它确实提供了结果的“反转”,所以很容易拍摄并正确映射:
var order = Enumerable.Range( 0, results.Length ).OrderBy( i => results[i] ).ToArray();
var indices = new int[results.Length];
for( int i = 0; i < results.Length; ++i )
{
indices[order[i]] = i;
}
// The indices array now holds the index that each item in the results array
// would end up at if sorted
答案 1 :(得分:1)
嗯,对于初学者来说,根据输入大小选择正确的算法,总会带来好处。我不确定Array.Sort
是否会这样做,很可能不是。
例如:如果处理3-4个元素,则更快可以使用您自己的insertion sort / hardcoded if statements
实现。
其次,如果处理多个元素(~10000),如果并行化QuickSort,则可以轻松地将Array.Sort
的性能提高50%。不过,请参阅ParallelExtensions
,但是您不能同时处理这么多元素。
问题最终归结为排序。使用LINQ / OrderBy的任何解决方案都不会超过整数数组上的本地Array.Sort
,.NET框架几乎没有巧妙的优化,可以将速度降低2倍。
为了使算法改进2倍,您只需要排序一次。
以下假设密钥的范围为0..1000:
int[] _indexArray = new int[1001];
public void AbitBetterImplementation(int[] array)
{
int[] copy = array.ToArray();
for(var i = 0; i < array.Length; i++){
var elem = array[i];
_indexArray[elem] = i;
}
Array.Sort(copy);
for(var i = 0; i < copy.Length; i++){
var oldIndex = _indexArray[copy[i]];
array[oldIndex] = i;
}
}
快速基准:
Basic implementation Time Elapsed 183,2125 ms
A bit better implementation Time Elapsed 99,4912 ms
答案 2 :(得分:0)
您可以计算每个项目存在多少个较低值:
int[] r = result.Select(n => result.Count(x => x < n)).ToArray();
或:
int[] r = new int[result.Length];
for (var i = 0; i < result.Length; i++) {
int n = result[i];
for (var j = 0; j < result.Length; j++) {
if (result[j] < n) r[i]++;
}
}
这实际上是效率还是效率取决于数据量。这是一个O(n * n)解决方案,因此如果数组很长,它就不会那么有效。
答案 3 :(得分:0)
这是一个用LINQ编写的简单解决方案:
var input = new int[] { 5, 4, 6, 12, 1, 0 };
var result = from n in input
let order = input.OrderBy(k => k).ToList()
select order.IndexOf(n);
请注意,实际上,重复项将具有相同的结果索引。我不知道它是否符合您的需求(但我怀疑在输入序列中不可能重复)。
答案 4 :(得分:0)
这个只对数组进行一次排序 +它在集合中循环(仅)3次。如果集合很大,这很重要。如果多个值具有相同的值,它知道原始位置。
var items = new[] { 5, 4, 6, 12, 1, 0 };
var combinations = items.Select((value, originalindex) => new { value, originalindex });
var sortedCombination = combinations.OrderBy(c => c.value);
var sortedCombinationWithIndex =
sortedCombination.Select((combination, sortedIndex) => new {combination, sortedIndex});
var result = new int[items.Length];
foreach (var item in sortedCombinationWithIndex)
{
result[item.combination.originalindex] = item.sortedIndex;
}
重写方法,您只需致电:
public int[] SortIndexes<T>(T[] source) where T : IComparable<T>
{
var combinations = source.Select((value, originalindex) => new { value, originalindex });
var sortedCombination = combinations.OrderBy(c => c.value);
var sortedCombinationWithIndex =
sortedCombination.Select((combination, sortedIndex) => new { combination, sortedIndex });
var result = new int[source.Length];
foreach (var item in sortedCombinationWithIndex)
{
result[item.combination.originalindex] = item.sortedIndex;
}
return result;
}