C# 基于先前排序的排序

C# 基于先前排序的排序,c#,algorithm,sorting,C#,Algorithm,Sorting,我试图根据“排序映射”对ID列表进行排序,该映射是一个(ID1,ID2,timestamp)元组数组,用于确定哪些ID应在其他ID之前排序。规则如下: ID1应在ID2之前排序 时间戳可以用来打破与较新时间戳的联系,而不是与较旧时间戳的联系。e、 g.给定排序键(C,A,1/1/1900),(C,B,1/1/2000)然后B在A之前排序 可以有循环,例如(A,B,1/1/1950),(B,C,1/1/1980),(C,A,1/1/1900)。时间戳可用于中断周期,周期中较旧的时间戳记录将从排序

我试图根据“排序映射”对ID列表进行排序,该映射是一个
(ID1,ID2,timestamp)
元组数组,用于确定哪些ID应在其他ID之前排序。规则如下:

  • ID1
    应在
    ID2
    之前排序
  • 时间戳可以用来打破与较新时间戳的联系,而不是与较旧时间戳的联系。e、 g.给定排序键
    (C,A,1/1/1900),(C,B,1/1/2000)
    然后
    B
    A
    之前排序
  • 可以有循环,例如
    (A,B,1/1/1950),(B,C,1/1/1980),(C,A,1/1/1900)
    。时间戳可用于中断周期,周期中较旧的时间戳记录将从排序映射中删除,直到周期结束
  • 如果一个ID不在排序映射中,它将按照排序映射中存在的任何ID排序
示例:给定排序映射
(C,A,1/1/1900),(C,B,1/1/2000)
,以及要排序的列表
(A,B,C,D)
,排序后的输出将是
(C,B,A,D)

把这些规则变成一个算法让我很为难。以下是我目前掌握的情况:

  • 从数据库中获取最新的排序映射。对于每一对唯一的ID,我最多只能得到一条记录

  • 从排序映射中删除周期如何?或者,作为第4步的一部分,简单地忽略周期更容易吗?

  • 在内存中转换排序映射以获得最佳性能。例如,构建一个哈希表,其键是排序映射中的每个唯一ID,这样我就可以快速找到包含特定ID的所有排序映射行

  • 使用通用二进制排序库对我的ID数组进行排序,该库使用一个自定义比较函数,该函数接受任意两个ID
    ID1
    ID2
    参数。比较功能:

    a。使用步骤3中的哈希表查找包含
    ID1
    ID2
    的所有排序映射项

    b。如果我在排序映射中已经有一条同时包含
    ID1
    ID2
    的记录,请停止--我们知道哪个应该是第一个

    c。如果在排序映射中既没有找到ID1也没有找到ID2,那么这就是平局。返回决定性的任意结果(例如,较低的ID赢)

    d。如果一个ID在排序映射中,而另一个不在,请停止。应该首先对找到的进行排序

    e。如果我们到达这里,两个ID都在排序映射中,但是在排序映射中没有直接比较现在怎么办?

  • 性能不是一个大问题,因为排序映射的最大大小在20K行以下,而被排序的ID的最大数量在30行以下

    有主意吗

    FWIW,我们将使用.NET在C#中进行排序,但底层算法显然与语言和平台无关


    如果您感到好奇,以下是该算法的实际需求:

    我们公司为送货司机构建移动应用程序,送货司机每天要访问他们负责的100-150个地点中的大约20个地点。每天的位置列表是根据每个位置的库存动态分配的。库存较低的地点将获得新库存的交付,而库存仍然充足的地点则不会被访问

    司机可以按任何顺序自由参观各个地点,但他们通常每天都会走类似的路线(例如,早上交通不太拥挤时参观城镇南部的地点,然后在南部交通较拥挤时参观城镇北部的地点)

    我们选择不使用自动确定最有效驾驶路线的第三方路由软件。相反,我们发现最好让司机选择路线,因为路线软件很难满足诸如“该大楼的装货码头通常在早上7点之前才有空”或“需要签署送货收据的人在周五早早离开”等约束条件,这对送货计划有很大影响

    无论如何,我们希望使用驾驶员的历史选择,按照驾驶员上次访问相同地点的相同顺序对每天的行程进行排序。这将使驾驶员每天都能根据自己的喜好安排好行程,而无需手动重新安排行程,除非在特殊情况下。这将为驾驶员每天节省一到两分钟的时间,这将随着时间的推移而增加

    每个历史行程实际上都是这样的列表(ID1、ID2、ID3、…、IDN、时间戳),但作为存储数百个过去计划的替代方案,我认为将每个N台机器的历史行程分解为成对的机器会更容易。这意味着我必须最多存储N*N-1个元组,因为新的排序总是将旧的排序从排序映射中剔除。如果这是一个糟糕的简化,请让我知道。;-)

    我会提出另一种方法,但如果我误解了业务需要,请告诉我

    有一个类似于(DriverId、LocationId、Priority)的表,用于存储每个驱动程序的位置的相对优先级

    任何时候,您需要处理完成的行程,从列表底部(最后访问的位置)开始,并对每个位置运行以下算法,向上移动列表:

    • 如果该位置的优先级尚未大于其下位置的优先级,则newPriority=prioritybelower+1。(如果下面没有任何内容,则优先级低于=0)
    处理完列表后,将优先级重新规范化为1,2,3。。。(通过使最小优先级=1,次最小优先级=2,依此类推)

    然后,当您需要订购一个新的行程时,您只需根据该司机的相对优先级值来订购位置

    你考虑过这种方法吗


    编辑:根据下面的注释添加示例代码

    给出4条历史路线:ABCD(最新),
    static Dictionary<string, int> Priorities = new Dictionary<string, int>();
    
    static void Main(string[] args)
    {
        var itineraries = new string[][]{   
            new string[] { "C", "B", "D", "F", "A" },
            new string[] { "C", "B", "D", "F" },
            new string[] { "A", "C", "B", "E" },
            new string[] { "A", "B", "C", "D" } };
    
        //process past itineraries
        foreach (var itinerary in itineraries)
            ProcessItinerary(itinerary);
    
        //sort new itinerary
        string[] newItinerary = { "A", "B", "C", "D", "E", "F" };
        string[] sortedItinerary = newItinerary.OrderByDescending(
            x => Priorities.ContainsKey(x) ? Priorities[x] : 1).ToArray();
    
        Console.WriteLine(String.Concat(sortedItinerary));
        Console.ReadKey();
    }
    
    static void ProcessItinerary(string[] itinerary)
    {
        itinerary.Reverse().Aggregate((below, above) =>
        {
            int priBelow = Priorities.ContainsKey(below) ?
                Priorities[below] : Priorities[below] = 1;
    
            if (!(Priorities.ContainsKey(above) &&
                Priorities[above] > priBelow))
                Priorities[above] = priBelow + 1;
    
            return above;
        });
    
        //normalize priorities
        // (note: running in reverse so that if priorities tie, 
        //  the older location has higher priority)
        int i = Priorities.Count;
        foreach (var pair in Priorities.OrderByDescending(x => x.Value))
            Priorities[pair.Key] = i--;
    }
    
    while true
     allCycles = getListOfAllCycles();
     if (allCycles.length == 0) break;
     breakNode = chooseBreakNode(allCycles); //defined later
     deleteBreakNodeFrom(allCycles);
    
    chooseBreakNode:
     chose the node which has been driven to the least //node is not important
     if ambiguous: chose the node in the dependency graph which is present in the highest number of cycles //breaks multiple cycles at once
     if ambiguous: chose the node which is in the longest cycle
     if ambiguous: pick an arbitrary node