C# 如何在DateTime值数组中查找平均日期/时间

C# 如何在DateTime值数组中查找平均日期/时间,c#,datetime,average,C#,Datetime,Average,如果我有一个日期时间值数组: List<DateTime> arrayDateTimes; 平均值应为: 2003-May-21 18:00:00 若你们有一个大的列表,你们可以使用下面的方法 var count = dates.Count; double temp = 0D; for (int i = 0; i < count; i++) { temp += dates[i].Ticks / (double)count; } var average = new D

如果我有一个日期时间值数组:

List<DateTime> arrayDateTimes;
平均值应为:

2003-May-21 18:00:00

若你们有一个大的列表,你们可以使用下面的方法

var count = dates.Count;
double temp = 0D;
for (int i = 0; i < count; i++)
{
    temp += dates[i].Ticks / (double)count;
}
var average = new DateTime((long)temp);
var count=dates.count;
双温=0D;
for(int i=0;i
课程计划
{
静态void Main(字符串[]参数)
{
列表日期=新列表(){
新日期时间(2003,5,21,16,0,0),新日期时间(2003,5,21,17,0,0),
新日期时间(2003,5,21,18,0,0),新日期时间(2003,5,21,19,0,0),
新日期时间(2003,5,21,20,0,0),新日期时间(2003,5,21,16,0,0),
新日期时间(2003,5,21,17,0,0),新日期时间(2003,5,21,18,0,0),
新日期时间(2003,5,21,19,0,0),新日期时间(2003,5,21,20,0,0),
新日期时间(2003,5,21,16,0,0),新日期时间(2003,5,21,17,0,0),
新日期时间(2003,5,21,18,0,0),新日期时间(2003,5,21,19,0,0),
新日期时间(2003,5,21,20,0,0),新日期时间(2003,5,21,16,0,0),
新日期时间(2003,5,21,18,0,0),新日期时间(2003,5,21,19,0,0),
新日期时间(2003,5,21,20,0,0),新日期时间(2003,5,21,16,0,0),
新日期时间(2003,5,21,18,0,0),新日期时间(2003,5,21,19,0,0),
新日期时间(2003,5,21,20,0,0),新日期时间(2003,5,21,16,0,0),
新日期时间(2003,5,21,18,0,0),新日期时间(2003,5,21,19,0,0),
新日期时间(2003,5,21,20,0,0),新日期时间(2003,5,21,16,0,0),
新日期时间(2003,5,21,18,0,0),新日期时间(2003,5,21,19,0,0),
新日期时间(2003,5,21,20,0,0),新日期时间(2003,5,21,16,0,0),
新日期时间(2003,5,21,18,0,0),新日期时间(2003,5,21,19,0,0),
新日期时间(2003,5,21,20,0,0),新日期时间(2003,5,21,16,0,0),
新日期时间(2003,5,21,18,0,0),新日期时间(2003,5,21,19,0,0),
新日期时间(2003,5,21,20,0,0),新日期时间(2003,5,21,16,0,0),
};
var averageDate=dates.Average();
控制台写入线(平均日期);
Console.ReadKey();
}
}
公共静态类扩展
{
公共静态长期平均值(此IEnumerable longs)
{
long count=longs.count();
长均值=0;
foreach(以长为单位的var val)
{
平均值+=val/计数;
}
回归均值;
}
公共静态日期时间平均值(此IEnumerable dates)
{
返回新的日期时间(dates.Select(x=>x.Ticks.Average());
}
}

这不应溢出,但它假定日期时间是按顺序排列的:

var first = dates.First().Ticks;
var average = new DateTime(first + (long) dates.Average(d => d.Ticks - first));
事实上,上面的内容确实充斥着更大的列表和更大的差距。我想你可以用几秒钟来扩大射程。(同样,先排序)此外,这可能不是最有效的方法,但对我来说,仍然相对较快地完成了1000万次约会。不确定它是否更容易阅读,YYMV

var first = dates.First();
var average = first.AddSeconds(dates.Average(d => (d - first).TotalSeconds));
来源:摘自并修改了一点

List<DateTime> dates = new List<DateTime>();
//Add dates
for (int i = 1; i <= 28; i++) //days
    for (int j = 1; j <= 12; j++) //month
        for (int k = 1900; k <= 2013; k++) //year
            dates.Add(new DateTime(k, j, i, 1, 2, 3)); //over 38000 dates
输出时间:
1956-Dec-29 06:09:25

最初,文章中的代码如下:

double totalSec = 0;
for (int i = 0; i < dates.Count; i++)
{
    TimeSpan ts = dates[i].Subtract(DateTime.MinValue);
    totalSec += ts.TotalSeconds;
}
double averageSec = totalSec / dates.Count;
DateTime averageDateTime = DateTime.MinValue.AddSeconds(averageSec);
double totalSec=0;
for(int i=0;i
代码:

var count = dates.Count;
double temp = 0D;
for (int i = 0; i < count; i++)
{
    temp += dates[i].Ticks / (double)count;
}
var average = new DateTime((long)temp);
答案是正确的。它通过进行增量平均来防止溢出

然而,这是错误的,因为它不能处理溢出和他对公式的误解

平均值=(x1+x2+…xN)/N非(x1/N+x2/N+…xN/N)

这是相同的公式。这是使用分配属性的简单数学:

2(x + y) = 2x + 2y
平均公式与总和乘以1/N或将每个X乘以1/N并相加相同

1/n (x1 + x2 + ... xn)
通过分配属性,它变成:

x1/n + x2/n + ... xn/n
这里有一些

他的答案也不好,因为它不能像公认的答案那样防止溢出


我可能会对他的回答发表评论,但我没有足够的声誉。

使用双秒而不是长刻度将避免在任何实际输入上溢出-这里的扩展方法

    public static DateTime Average(this IEnumerable<DateTime> elements)
    {
        if (elements == null)
        {
            throw new ArgumentNullException(nameof(elements));
        }
        var enumerated = elements.ToArray(); //so we don't iterate a potentially one-use stream multiple times.
        if (!enumerated.Any())
        {
            throw new ArgumentException("Average of 0 elements is undefined", nameof(elements));
        }

        var epoch = enumerated.Min();
        var secondsSinceEpoch = enumerated.Select(d => (d - epoch).TotalSeconds).ToArray();
        var n = secondsSinceEpoch.LongLength;
        double totalSecondsSinceEpoch = secondsSinceEpoch.Sum();
        return epoch.AddSeconds(totalSecondsSinceEpoch / n);
    }

    [TestMethod]
    public void HugeDateAverage_DoesntThrow()
    {
        var epoch = new DateTime(1900,1,1);
        try
        {
            var dates = Enumerable.Range(1, 1_000_000_000)
             .Select(i => epoch.AddSeconds(i));
            var result = dates.Average();
        }
        catch (Exception ex)
        {
            Assert.Fail();
        }
    }
publicstaticdatetime平均值(这是IEnumerable元素)
{
if(elements==null)
{
抛出新的ArgumentNullException(nameof(elements));
}
var enumerated=elements.ToArray();//因此我们不会多次迭代一个潜在的单用途流。
如果(!enumerated.Any())
{
抛出新ArgumentException(“0个元素的平均值未定义”,nameof(elements));
}
var epoch=enumerated.Min();
var secondsSinceEpoch=enumerated.Select(d=>(d-epoch).TotalSeconds.ToArray();
var n=secondsSinceEpoch.LongLength;
double TotalSecondSinceEpoch=secondSinceEpoch.Sum();
返回epoch.AddSeconds(totalSecondsSinceEpoch/n);
}
[测试方法]
公共空间被包围平均值不超过()
{
var epoch=新日期时间(1900,1,1);
尝试
{
变量日期=可枚举范围(1,1\u 000\u 000)
.选择(i=>epoch.AddSeconds(i));
var result=dates.Average();
}
捕获(例外情况除外)
{
Assert.Fail();
}
}
如果您真的想要退化,您可以检测溢出并在一半元素上递归,注意奇数N的情况。这是未经测试的,但想法如下:

    //NOT FOR ACTUAL USE - JUST FOR FUN
    public static DateTime AverageHuge(this IEnumerable<DateTime> elements)
    {
        if (elements == null)
        {
            throw new ArgumentNullException(nameof(elements));
        }
        var enumerated = elements.ToArray(); //so we don't iterate a potentially one-use stream multiple times.
        if (!enumerated.Any())
        {
            throw new ArgumentException("Average of 0 elements is undefined", nameof(elements));
        }

        var epoch = enumerated.Min();
        var secondsSinceEpoch = enumerated.Select(d => (d - epoch).TotalSeconds).ToArray();
        var n = secondsSinceEpoch.LongLength;
        if (n > int.MaxValue)
        {
            //we could actually support more by coding Take+Skip with long arguments.
            throw new NotSupportedException($"only int.MaxValue elements supported");
        }

        try
        {
            double totalSecondsSinceEpoch = secondsSinceEpoch.Sum(); //if this throws, we'll have to break the problem up
            //otherwise we're done.
            return epoch.AddSeconds(totalSecondsSinceEpoch / n);
        }
        catch (OverflowException) { } //fall out of this catch first so we don't throw from a catch block

        //Overengineering to support large lists whose totals would be too big for a double.
        //recursively get the average of each half of values.
        int pivot = (int)n / 2;
        var avgOfAvgs = (new []
        {
            enumerated.Take(pivot).AverageHuge(),
            enumerated.Skip(pivot).Take(pivot).AverageHuge()
        }).AverageHuge();
        if (pivot * 2 == n)
        {   // we had an even number of elements so we're done.
            return avgOfAvgs;
        }
        else
        {   //we had an odd number of elements and omitted the last one.
            //it affects the average by 1/Nth its difference from the average (could be negative)
            var adjust = ((enumerated.Last() - avgOfAvgs).TotalSeconds) / n;
            return avgOfAvgs.AddSeconds(adjust);
        }
        
    }
//不是为了实际使用-只是为了好玩
公共静态日期时间平均值巨大(此为IEnumerable元素)
{
if(elements==null)
{
抛出新的ArgumentNullException(nameof(elements));
}
var enumerated=elements.ToArray();//因此我们不会多次迭代一个潜在的单用途流。
如果(!
1/n (x1 + x2 + ... xn)
x1/n + x2/n + ... xn/n
    public static DateTime Average(this IEnumerable<DateTime> elements)
    {
        if (elements == null)
        {
            throw new ArgumentNullException(nameof(elements));
        }
        var enumerated = elements.ToArray(); //so we don't iterate a potentially one-use stream multiple times.
        if (!enumerated.Any())
        {
            throw new ArgumentException("Average of 0 elements is undefined", nameof(elements));
        }

        var epoch = enumerated.Min();
        var secondsSinceEpoch = enumerated.Select(d => (d - epoch).TotalSeconds).ToArray();
        var n = secondsSinceEpoch.LongLength;
        double totalSecondsSinceEpoch = secondsSinceEpoch.Sum();
        return epoch.AddSeconds(totalSecondsSinceEpoch / n);
    }

    [TestMethod]
    public void HugeDateAverage_DoesntThrow()
    {
        var epoch = new DateTime(1900,1,1);
        try
        {
            var dates = Enumerable.Range(1, 1_000_000_000)
             .Select(i => epoch.AddSeconds(i));
            var result = dates.Average();
        }
        catch (Exception ex)
        {
            Assert.Fail();
        }
    }
    //NOT FOR ACTUAL USE - JUST FOR FUN
    public static DateTime AverageHuge(this IEnumerable<DateTime> elements)
    {
        if (elements == null)
        {
            throw new ArgumentNullException(nameof(elements));
        }
        var enumerated = elements.ToArray(); //so we don't iterate a potentially one-use stream multiple times.
        if (!enumerated.Any())
        {
            throw new ArgumentException("Average of 0 elements is undefined", nameof(elements));
        }

        var epoch = enumerated.Min();
        var secondsSinceEpoch = enumerated.Select(d => (d - epoch).TotalSeconds).ToArray();
        var n = secondsSinceEpoch.LongLength;
        if (n > int.MaxValue)
        {
            //we could actually support more by coding Take+Skip with long arguments.
            throw new NotSupportedException($"only int.MaxValue elements supported");
        }

        try
        {
            double totalSecondsSinceEpoch = secondsSinceEpoch.Sum(); //if this throws, we'll have to break the problem up
            //otherwise we're done.
            return epoch.AddSeconds(totalSecondsSinceEpoch / n);
        }
        catch (OverflowException) { } //fall out of this catch first so we don't throw from a catch block

        //Overengineering to support large lists whose totals would be too big for a double.
        //recursively get the average of each half of values.
        int pivot = (int)n / 2;
        var avgOfAvgs = (new []
        {
            enumerated.Take(pivot).AverageHuge(),
            enumerated.Skip(pivot).Take(pivot).AverageHuge()
        }).AverageHuge();
        if (pivot * 2 == n)
        {   // we had an even number of elements so we're done.
            return avgOfAvgs;
        }
        else
        {   //we had an odd number of elements and omitted the last one.
            //it affects the average by 1/Nth its difference from the average (could be negative)
            var adjust = ((enumerated.Last() - avgOfAvgs).TotalSeconds) / n;
            return avgOfAvgs.AddSeconds(adjust);
        }
        
    }