C# 从阵列中获得n个最低值的最快方法
我需要从double数组(让我们调用数组示例)中找到n个最低值(不是0)。我需要在循环中多次这样做,因此执行速度至关重要。我尝试先对数组进行排序,然后取前10个值(不是0),但是,虽然array.Sort据说速度很快,但它成为了瓶颈: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
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];
}
}