“一年中的一周”算法需要改进

Posted

技术标签:

【中文标题】“一年中的一周”算法需要改进【英文标题】:"Week of the year" Algorithm needs improvements 【发布时间】:2010-07-28 16:32:55 【问题描述】:

我有一个算法,它扫描从 .csv 文件(大约 3700 行)读取的数据,并通过为该年的每个星期日运行 count++ 并将计数值指定为来评估每个条目在一年中的哪个交易周日期在该周内的交易周。

它工作正常,但性能滞后。这是使用 Task.Factory.StartNew 运行的第三个函数(我也尝试过 parallel.Invoke)。

计时测试结果。

之前:00:00:05.58

之后:00:00:23.27

更新

在设置每个交易周后增加休息时间。时间有所改善,但仍然很慢。

新时间:00:00:15.74

就我们而言,一年中的第一周是第 1 周(不是 0),定义为从一年的第一天到星期日。如果一年的第一天是星期日,则第 1 周的长度为 1 天。

private void SetDefiniteWeeks()
        
            string FileLoc = FilePath + Market + ".csv";
            string[] Data = File.ReadAllLines(FileLoc);
            var FileData = from D in Data
                           let DataSplit = D.Split(',')
                           select new
                           
                               Date = DateTime.Parse(DataSplit[0]),
                               ClosingPrice = double.Parse(DataSplit[4])
                           ;

            //assign each date to it's relevant week
            TradingWeek TW;
            List<TradingWeek> tradingWeek = new List<TradingWeek>();
            foreach (var pe in FileData)
            
               // DateTime dt = pe.Date;
                int Year = pe.Date.Year;
                string End_of_Week = "Sunday";
                int WeekCount = 0;

                DateTime LoopDate_Begin = new DateTime(Year,1,1);
                DateTime LoopDate_End = new DateTime(Year,12,31);
                do
                
                    if (LoopDate_Begin.DayOfWeek.ToString() == End_of_Week)
                    
                        WeekCount++;
                        if (LoopDate_Begin.DayOfYear > pe.Date.DayOfYear && LoopDate_Begin.DayOfYear < (pe.Date.DayOfYear + 7))
                        
                            TW = new TradingWeek  Week = WeekCount, Date = pe.Date ;
                            tradingWeek.Add(TW);
                            break;
                        
                    
                    LoopDate_Begin = LoopDate_Begin.AddDays(1);

                 while (LoopDate_Begin.Date.ToString() != LoopDate_End.Date.ToString());

            

        

请帮忙。

更新

新时代

00:00:06.686

巨大的改进。谢谢大家的帮助。

修改后的代码:

    CalendarWeekRule cw = CalendarWeekRule.FirstDay;
    var calendar = CultureInfo.CurrentCulture.Calendar;
    var trad_Week = (from pe in FileData
                        select new TradingWeek
                        
                        Date = pe.Date,
                        Week = (calendar.GetWeekOfYear(pe.Date, cw,DayOfWeek.Sunday))
                        ).ToList();

【问题讨论】:

【参考方案1】:

我不确定这是否是您想要的,但在阅读了 cmets 之后,我觉得这可能有效(?)

var calendar = CultureInfo.CurrentCulture.Calendar;
var tradingWeek = (from pe in FileData
                  select new TradingWeek
                  
                    Date = pe.Date,
                    Week = calendar.GetWeekOfYear(pe.Date, CalendarWeekRule.FirstDay, DayOfWeek.Sunday);
                  ).ToList();

编辑:更改为 CalendarWeekRule.FirstDay,因为它是(更多?)OP 正在寻找的内容。

【讨论】:

Jesper 由于 CalendarWeekRule,我不确定这是否会产生正确的结果。请查看我的更新。 @Chris:如果你使用 CalendarWeekRule.FirstDay 会怎样? 感谢 Jesper,检查了过去几年的头几天,值都正确!【参考方案2】:

三个快速的想法:

为什么每次只添加一天并检查是否是星期天。当然,一旦你找到了你的第一个星期天,你就可以增加 7 天来找到下一个星期天?

如果您在开始前按日期时间订购 pes,那么您不需要在年初重新开始每个,您可以从上次中断的地方继续。

正如 Nicolas 所说,在添加交易周后休息。在您已经知道答案之后,无需再经历这一年的剩余时间。

我猜你最终会得到这样的结果(可能会或可能不会真正起作用,但应该足够接近)

TradingWeek TW;
List<TradingWeek> tradingWeek = new List<TradingWeek>();
string End_of_Week = "Sunday";
var orderedData = FileData.OrderBy(x => x.Date)
DateTime LoopDate_Begin = new DateTime(orderedData[0].Date.Year,1,1);
int WeekCount = 1; 

while (LoopDate_Begin.DayOfWeek.ToString() != End_of_Week)

    LoopDate_Begin = LoopDate_Begin.AddDays(1);


foreach (var pe in orderedData)

    do
               
        if (LoopDate_Begin.DayOfYear > pe.Date.DayOfYear && LoopDate_Begin.DayOfYear < (pe.Date.DayOfYear + 7))
        
            TW = new TradingWeek  Week = WeekCount, Date = pe.Date ;
            tradingWeek.Add(TW);
            break;
        
        WeekCount++;
        LoopDate_Begin = LoopDate_Begin.AddDays(7);

     while (true); //need to be careful here


【讨论】:

感谢 Martin,orderedData[0].Date.Year 给了我一个错误:错误 2 无法使用 [] 对类型为“System.Linq.IOrderedEnumerable”的表达式应用索引 试试orderedDate.First().Date.Year。 一件小事,但是当你最终将它与 DayOfWeek 枚举进行比较时,为什么要将 End_Of_Week 存储在一个字符串中呢?最好使用 DayOfWeek End_Of_Week = DayOfWeek.Sunday; ... LoopDate_Begin.DayOfWeek != End_of_Week【参考方案3】:

如果我没听错的话,一旦您添加了 TradingWeek,您就不需要再看下去了

所以,你可以

break;

之后

tradingWeek.Add(TW);

你甚至可以省略

&& LoopDate_Begin.DayOfYear < (pe.Date.DayOfYear + 7)

条件因为第一部分将只为真一次:对于您想要的时间间隔。

【讨论】:

【参考方案4】:

您甚至可以通过将自开始周以来的天数除以 7 并进行一些清理工作来采用无循环方法;)

【讨论】:

我同意;并且无法理解如何支持“优化”循环方法的解决方案。泽勒的胜利一致!【参考方案5】:

您可以通过直接计算周数来完全摆脱do 循环吗?类似于接受的答案here。

【讨论】:

如果这是一个选项,您可以使用内置函数: CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(someDate, CalendarWeekRule.FirstFourDayWeek, DayOfWeek.Sunday) 非常好。甚至不知道它的存在。【参考方案6】:

在@nicolas78 的回复之后,这样的事情应该可以工作

int Year = pe.Date.Year;
DateTime Year_Begin = new DateTime(Year,1,1);
int Jan1DayOfWeek = Year_Begin.DayOfWeek;

foreach (var pe in FileData)

    int WeekCount = (pe.Date.DayOfYear - Jan1DayOfWeek) % 7 + 1;
    TradingWeek TW = new TradingWeek  Week = WeekCount, Date = pe.Date ;
    tradingWeek.Add(TW);

根据 DayOfWeek 和 DayOfYear 的计数方式,即从 0 或 1,以及您的 mod 操作的工作方式,您可能需要稍微调整 WeekCount 计算。

【讨论】:

【参考方案7】:

有一个内置功能可以根据 .NET 中的日期获取一年中的星期几。下面显示了一个示例,但可能需要进行一些调整以适应您的业务场景:

System.Globalization.CultureInfo myCI = new System.Globalization.CultureInfo("en-US");

int week = myCI.Calendar.GetWeekOfYear(DateTime.Now.ToUniversalTime(), System.Globalization.CalendarWeekRule.FirstFourDayWeek, System.DayOfWeek.Sunday);

【讨论】:

【参考方案8】:

您根本不需要计算 - 只需快速计算一下即可。这假设年初的部分周是第 1 周,第 2 周从第一个星期一开始。

List<TradingWeek> tradingWeek = new List<TradingWeek>();
foreach (var pe in FileData)

    var date = pe.Date;
    while (date.DayOfWeek != DayOfWeek.Sunday)
        date = date.AddDays(1);
    var week = date.DayOfYear/7+1;
    var TW = new TradingWeek Week = week, Date = pe.Date;
    tradingWeek.Add(TW);

【讨论】:

以上是关于“一年中的一周”算法需要改进的主要内容,如果未能解决你的问题,请参考以下文章

确定性标量函数,用于获取日期的一年中的一周

将一年中的一周与“溢出”进行比较

Presto SQL date_format 提取一年中的一周

一年中的一周使用 bigquery / bigrquery 和 dbplyr? (相当于 lubridate::week)

c++:从 yyyyMMdd 获取周数

每个星期六安排一个cron工作,间隔为21天