数组按其大小重新缩放整数

时间:2014-11-09 19:58:26

标签: c# arrays

我有一个整数数组,如果我按大小排序,我想改变它们的位置值,目前我这样做:

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时间。 我怎样才能让它更快?

5 个答案:

答案 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;
}