C# 从阵列中获得n个最低值的最快方法

C# 从阵列中获得n个最低值的最快方法,c#,arrays,C#,Arrays,我需要从double数组(让我们调用数组示例)中找到n个最低值(不是0)。我需要在循环中多次这样做,因此执行速度至关重要。我尝试先对数组进行排序,然后取前10个值(不是0),但是,虽然array.Sort据说速度很快,但它成为了瓶颈: const int numLowestSamples = 10; double[] samples; double[] lowestSamples = new double[numLowestSamples]; for (int count = 0; cou

我需要从double数组(让我们调用数组示例)中找到n个最低值(不是0)。我需要在循环中多次这样做,因此执行速度至关重要。我尝试先对数组进行排序,然后取前10个值(不是0),但是,虽然array.Sort据说速度很快,但它成为了瓶颈:

const int numLowestSamples = 10;

double[] samples;

double[] lowestSamples = new double[numLowestSamples];

for (int count = 0; count < iterations; count++) // iterations typically around 2600000
{
    samples = whatever;
    Array.Sort(samples);
    lowestSamples = samples.SkipWhile(x => x == 0).Take(numLowestSamples).ToArray();
}
const int numLowestSamples=10;
双[]样本;
double[]lowestSamples=新的double[numLowestSamples];
对于(int count=0;countx==0);
}
因此,我尝试了一种不同但不太干净的解决方案,首先读取前n个值,对它们进行排序,然后循环遍历样本中的所有其他值,检查该值是否小于排序后的最低样本数组中的最后一个值。如果该值较低,则将其替换为数组中的值,然后再次对数组进行排序。结果证明,这大约快了5倍:

const int numLowestSamples = 10;

double[] samples;

List<double> lowestSamples = new List<double>();

for (int count = 0; count < iterations; count++) // iterations typically around 2600000
{
    samples = whatever;

    lowestSamples.Clear();

    // Read first n values
    int i = 0;
    do
    {
        if (samples[i] > 0)
            lowestSamples.Add(samples[i]);

        i++;
    } while (lowestSamples.Count < numLowestSamples)

    // Sort the array
    lowestSamples.Sort();

    for (int j = numLowestSamples; j < samples.Count; j++) // samples.Count is typically 3600
    {
        // if value is larger than 0, but lower than last/highest value in lowestSamples
        // write value to array (replacing the last/highest value), then sort array so
        // last value in array still is the highest
        if (samples[j] > 0 && samples[j] < lowestSamples[numLowestSamples - 1])
        {
            lowestSamples[numLowestSamples - 1] = samples[j];
            lowestSamples.Sort();
        }
    }
}
const int numLowestSamples=10;
双[]样本;
List lowestSamples=新列表();
对于(int count=0;count0)
最低样本。添加(样本[i]);
i++;
}while(lowerstsamples.Count0和样本[j]

虽然这工作相对较快,但我想向任何人提出一个更快更好的解决方案。

我认为您可能需要尝试维护最小堆,并测量性能差异。这是我一直在研究的一种称为斐波那契堆的数据结构。这可能需要一些工作,但你至少可以验证我的假设

public sealed class FibonacciHeap<TKey, TValue>
{
    readonly List<Node> _root = new List<Node>();
    int _count;
    Node _min;

    public void Push(TKey key, TValue value)
    {
        Insert(new Node {
            Key = key,
            Value = value
        });
    }       

    public KeyValuePair<TKey, TValue> Peek()
    {
        if (_min == null)
            throw new InvalidOperationException();
        return new KeyValuePair<TKey,TValue>(_min.Key, _min.Value);
    }       

    public KeyValuePair<TKey, TValue> Pop()
    {
        if (_min == null)
            throw new InvalidOperationException();
        var min = ExtractMin();
        return new KeyValuePair<TKey,TValue>(min.Key, min.Value);
    }

    void Insert(Node node)
    {
        _count++;
        _root.Add(node);
        if (_min == null)
        {
            _min = node;
        }
        else if (Comparer<TKey>.Default.Compare(node.Key, _min.Key) < 0)
        {
            _min = node;
        }
    }

    Node ExtractMin()
    {
        var result = _min;
        if (result == null)
            return null;
        foreach (var child in result.Children)
        {
            child.Parent = null;
            _root.Add(child);
        }
        _root.Remove(result);
        if (_root.Count == 0)
        {
            _min = null;
        }
        else
        {
            _min = _root[0];
            Consolidate();
        }
        _count--;
        return result;
    }

    void Consolidate()
    {
        var a = new Node[UpperBound()];
        for (int i = 0; i < _root.Count; i++)
        {
            var x = _root[i];
            var d = x.Children.Count;
            while (true)
            {   
                var y = a[d];
                if (y == null)
                    break;                  
                if (Comparer<TKey>.Default.Compare(x.Key, y.Key) > 0)
                {
                    var t = x;
                    x = y;
                    y = t;
                }
                _root.Remove(y);
                i--;
                x.AddChild(y);
                y.Mark = false;
                a[d] = null;
                d++;
            }
            a[d] = x;
        }
        _min = null;
        for (int i = 0; i < a.Length; i++)
        {
            var n = a[i];
            if (n == null)
                continue;
            if (_min == null)
            {
                _root.Clear();
                _min = n;
            }
            else
            {
                if (Comparer<TKey>.Default.Compare(n.Key, _min.Key) < 0)
                {
                    _min = n;
                }
            }
            _root.Add(n);
        }
    }

    int UpperBound()
    {
        return (int)Math.Floor(Math.Log(_count, (1.0 + Math.Sqrt(5)) / 2.0)) + 1;
    }

    class Node
    {
        public TKey Key;
        public TValue Value;
        public Node Parent;
        public List<Node> Children = new List<Node>();
        public bool Mark;

        public void AddChild(Node child)
        {
            child.Parent = this;
            Children.Add(child);
        }

        public override string ToString()
        {
            return string.Format("({0},{1})", Key, Value);
        }
    }
}
公共密封类FibonacciHeap
{
只读列表_root=新列表();
整数计数;
节点_min;
公共无效推送(TKey键,TValue值)
{
插入(新节点){
键=键,
价值=价值
});
}       
public KeyValuePair Peek()
{
如果(_min==null)
抛出新的InvalidOperationException();
返回新的KeyValuePair(_min.Key,_min.Value);
}       
公钥值对Pop()
{
如果(_min==null)
抛出新的InvalidOperationException();
var min=ExtractMin();
返回新的KeyValuePair(最小键,最小值);
}
空心插入(节点)
{
_计数++;
_root.Add(节点);
如果(_min==null)
{
_min=节点;
}
else if(Comparer.Default.Compare(node.Key,_min.Key)<0)
{
_min=节点;
}
}
节点ExtractMin()
{
var结果=_min;
如果(结果==null)
返回null;
foreach(result.Children中的变量child)
{
child.Parent=null;
_root.Add(子级);
}
_根。删除(结果);
如果(_root.Count==0)
{
_最小值=零;
}
其他的
{
_最小值=_根[0];
巩固();
}
_计数--;
返回结果;
}
无效合并()
{
var a=新节点[UpperBound()];
对于(int i=0;i<\u root.Count;i++)
{
var x=_根[i];
var d=x.Children.Count;
while(true)
{   
变量y=a[d];
如果(y==null)
打破
if(Comparer.Default.Compare(x.Key,y.Key)>0)
{
var t=x;
x=y;
y=t;
}
_去根(y);
我--;
x、 AddChild(y);
y、 标记=假;
a[d]=null;
d++;
}
a[d]=x;
}
_最小值=零;
for(int i=0;i
理想情况下,您只需对集合进行一次遍历,因此您的解决方案非常灵活。但是,每次插入时,您都会使用整个子列表,而您只需要提升前面的数字。然而,对10个元素进行排序几乎可以忽略不计,增强这一点并不会给您带来太多好处。
int samplesCount = samples.Count;

for (int j = numLowestSamples; j < samplesCount; j++)
{
    double sample = samples[j];

    if (sample > 0 && sample < currentMax)
    {
        int k;

        for (k = 0; k < numLowestSamples; k++)
        {
           if (sample < lowestSamples[k])
           {
              Array.Copy(lowestSamples, k, lowestSamples, k + 1, numLowestSamples - k - 1);
              lowestSamples[k] = sample;

              break;
           }
        }

        if (k == numLowestSamples)
        {
           lowestSamples[numLowestSamples - 1] = sample;
        }

        currentMax = lowestSamples[numLowestSamples - 1];
    }
}