【问题标题】:Sorting Racers in timing application在计时应用程序中对赛车手进行排序
【发布时间】:2013-03-23 20:09:31
【问题描述】:

我正在创建一个使用 AMB MyLaps 解码器 P3 协议的应用程序。

我想不出一种根据圈数和圈速对赛车手进行分类的方法。例如,第 1 位的人跑了 3 圈,第 2 位的人跑了 2 圈。但是,我如何订购 2 人在同一圈的情况?

这是我用来保存信息的记录:

type
  TTimingRecord = record
    position: integer;
    transId: integer;
    racerName:  string;
    kartNumber: integer;
    lastPassingN: integer;
    laps: integer;
    lastRTCTime:  TDateTime;
    bestTimeMs: Extended;
    lastTimeMs: Extended;
    gapTimeMs:  Extended;
    splitTimeMs:  Extended;
    timestamp:  TDateTime;
  end;

为每位赛车手创造一个新纪录。

我目前使用的代码是:

procedure sortRacers();
var
  Pos, Pos2: Integer;
  Temp: TTimingRecord;
  GapTime: Extended;
begin
  for Pos := 0 to length(DriversRecord)-1 do
  begin
    for Pos2 := 0 to Length(DriversRecord)-2 do
    begin
      if(DriversRecord[Pos2].laps < DriversRecord[Pos2+1].laps)then
      begin
        Temp := DriversRecord[Pos2];
        DriversRecord[Pos2] := DriversRecord[Pos2+1];
        DriversRecord[Pos2+1] := Temp;
      end
      else if DriversRecord[Pos2].laps = DriversRecord[Pos2+1].laps then
           begin
             if DriversRecord[Pos2].lastRTCTime > DriversRecord[Pos2+1].lastRTCTime then
             begin
              Temp := DriversRecord[Pos2];
              DriversRecord[Pos2] := DriversRecord[Pos2+1];
              DriversRecord[Pos2+1] := Temp;
             end;
           end;
    end;
  end;

  for pos := 1 to length(DriversRecord) -1 do   //Gap Time
  begin
    if DriversRecord[Pos].laps = DriversRecord[0].laps then
    begin
      DriversRecord[Pos].gapTimeMs := DriversRecord[Pos].lastRTCTime - DriversRecord[0].lastRTCTime;

      DriversRecord[Pos].splitTimeMs := DriversRecord[Pos].lastRTCTime - DriversRecord[Pos-1].lastRTCTime;
    end;
  end;
end;

但效果不太好:)

【问题讨论】:

  • 首先你需要把排序和比较分开。编写(或查找)排序方法。然后写一个方法来比较。然后将两者放在一起。像这样将它们混合在一起是无处可去的。
  • 所以我使用简单的冒泡排序重写了排序方法...procedure sortRacers2(); var i,x: integer; Temp: TTimingRecord; begin for i := 0 to length(DriversRecord)-2 do begin for x := i to length(DriversRecord)-1 do begin if DriversRecord[i].laps &lt; DriversRecord[x].laps then begin Temp := DriversRecord[i]; DriversRecord[i] := DriversRecord[x]; DriversRecord[x] := Temp; end; end; end; end;我现在如何使用 lastRTCTime 考虑同一圈的那些?

标签: delphi sorting datetime racing


【解决方案1】:

我假设根据您对问题的评论,您已将问题分解为排序和比较,并且您已经涵盖了排序部分。剩下的就是订单比较。

您需要一个函数来执行字典顺序比较,该比较首先基于完成的圈数,然后是自该圈开始以来的时间。基本上它会是这样的:

function CompareRacers(const Left, Right: TTimingRecord): Integer;
begin
  Result := CompareValue(Left.laps, Right.laps);
  if Result=0 then
    Result := CompareDateTime(Left.lastRTCTime, Right.lastRTCTime);
end;

您会在Math 中找到CompareValue,在DateUtils 中找到CompareDateTime

我不确定lastRTCTime 值的含义是什么。您可能需要对调用CompareDateTime 的结果取反才能得到您想要的结果。

Result := -CompareDateTime(Left.lastRTCTime, Right.lastRTCTime);

另外,如果在单圈中超车会怎样?大概在赛车手完成当前圈之前,您无法检测到这一点。

【讨论】:

  • 大卫,感谢您的意见。我可能应该详细说明一下。赛道上有一个“检测循环”,所以是的,就超车而言,直到它被排序的那一圈结束。 lastRTCTime 是一个日期时间戳,它指示赛车手最后一次越过检测循环的时间。因此,如果两个人在第 2 圈,那么最先越线的人将在前面。
  • 在这种情况下,您需要否定我提到的标志。我想你现在已经有了答案。还有什么需要的吗?
  • 是的,我刚刚实现了它,它可以工作.. 几乎! TimingRecords 数组是升序排列的,需要降序排列。我想这取决于我的排序循环?
  • 没有。只需反转比较函数返回的值的符号即可。
  • 呃!谢谢!完美运行!
【解决方案2】:

不要自己做排序算法,试试这个技术(如果你有兼容的 Delphi 版本):Best way to sort an array

您的函数可能如下所示:

uses Types;

function CustomSort(const Left, Right: TTimingRecord): Integer
begin
  If (left.laps > right.laps) then
     result := GreaterThanValue
  else 
  if (left.laps < right.laps) then
      result := LessThanValue
  else 
  begin
      // Same laps count... Test on LastRTCTime 
      if (left.lastRTCTime < right.lastRTCTime) then
          result := GreaterThanValue1
      else
      if (left.lastRTCTime > right.lastRTCTime) then
          result := LessThanValue
      else
          result := EqualsValue; 
  end;
end));

【讨论】:

    【解决方案3】:

    将其视为两种不同的类别可能更容易。

    显然你知道冒泡排序方法,所以我不再赘述。

    对您的排序进行 2 次传递。 第一,你对圈数进行排序。

    第二,您遍历整个排序圈列表。在数组中找到相同圈值的起点和终点。从开始点和结束点再次进行排序,但这次只比较次要值。如果相同值的计数大于 1,则遍历所有相同的辅助值。

    【讨论】:

    • 这比单程复杂得多
    • 制作计算机程序的一件事是制作可重用的代码。任何仅用于一种排序和二次排序的专门例程都不太可能在以后再次使用。> 这会减慢进度和新的开发。通过为数组排序制定一个工作例程,该例程也可以在第二次通过时重用,以及以后的所有排序需要。
    • 什么意思?二次比较在比较功能中要容易得多。您的方式涉及编写一种排序,该排序需要了解主要比较的知识才能执行次要排序。试着画出来。
    • 嗯.. 我的回答不适合这里。我会尝试添加另一个答案:)
    【解决方案4】:

    这段代码是关于使用索引对数据进行排序的。比冒泡排序快得多。 它是动态的,并提供从数组中的起点到终点进行排序的能力。 代码本身比 Bubble-Sort 大,但没有多少算法可以在速度上进行比较。 代码(当理解它的工作原理时)可以很容易地修改以适应大多数类型的排序。在 65536 个字符串的数组上,它只需要进行 17 次比较(或大约) 与冒泡排序相比,每个比较周期的 CPU 周期要多一些,但仍然是最快的方法之一。 搜索与 BTREE 一样快。实际排序可能会慢一些,但之后数据更易于管理,无需真正平衡树。

    享受吧。

    注意:例程并不是实际问题的完整解决方案,但它提供了一种极快方法的开始。


    TYPE
      DynamicIntegerArray = ARRAY OF INTEGER;
      DynamicStringArray  = ARRAY OF STRING;
    
    VAR
      BinSortLo, BinSortMid, BinSortHi : INTEGER; 
    
    FUNCTION FindMid:INTEGER;
      BEGIN
        FindMid:=BinSortLo+((BinSortHi-BinSortLo) DIV 2);
      END;
    
    PROCEDURE ShiftIndexUpAndStorePointer(VAR ArrParamIndex: DynamicIntegerArray; HighBound:INTEGER);
      VAR
        x : INTEGER;
      BEGIN
        FOR x:=HighBound-1 DOWNTO BinSortMid DO ArrParamIndex[x+1] := ArrParamIndex[x];// Shift the index.
        ArrParamIndex[BinSortMid]:=HighBound;// Store the pointer to index at its sorted place
      END;
    
    PROCEDURE BinarySortUp(CONST ArrParam:DynamicStringArray; VAR ArrParamIndex: DynamicIntegerArray; CONST LoBound,HighBound:INTEGER); OVERLOAD;
      VAR
        TempVar : STRING;
      BEGIN
        BinSortLo:=LoBound; BinSortHi:=HighBound; BinSortMid:=FindMid;
        TempVar := ArrParam[HighBound];
        REPEAT
      IF TempVar>ArrParam[ArrParamIndex[BinSortMid]] THEN BinSortLo:=BinSortMid ELSE BinSortHi:=BinSortMid;
      BinSortMid:=FindMid;
        UNTIL (BinSortMid=BinSortLo); {OR (BinSortMid=BinSortHi);}
        IF TempVar>ArrParam[ArrParamIndex[BinSortMid]] THEN INC(BinSortMid);// We always need a last check just in case.
        ShiftIndexUpAndStorePointer(ArrParamIndex,HighBound);
      END;
    
    PROCEDURE DynamicCreateIndex(CONST ArrParam:DynamicStringArray; VAR ArrParamIndex: DynamicIntegerArray; CONST LoBound,HighBound:INTEGER);
      VAR
        x : INTEGER;
      BEGIN
        FOR x:=LoBound TO HighBound DO
        BinarySortUp(ArrParam,ArrParamIndex,LoBound,x);
      END;
    
    
    BEGIN
    {
    1. Create your STRING ARRAY as a DynamicStringArray. 
    2. Create your INDEX ARRAY as a DynamicIntegerArray.
    3. Set the size of these arrays to any INTEGER size and fill the strings with data.
    4. Run a call to DynamicCreateIndex(YourStringArray,YourIndexArray,0,SizeOfArray
    Now you have a sorted Index of all the strings.
    
    }
    END.
    

    【讨论】:

    • 如何只用 17 次比较就可以对 65536 个字符串进行排序?你不可能是那个意思。
    • 对不起。我意识到我的说法有点模糊,不够详细。我的意思是,该例程只需要 17 次比较,就可以将一个字符串添加到已索引的 65536 个字符串数组中。它开始使用很少的比较实际数据。要排序的字符串越少,比较就越少。未修改的冒泡排序需要对 256 个字符串的数组进行大约 65535 次比较(在对所有内容进行排序之前)。这里介绍的例程(我自己的)需要对每个字符串进行 1-8 次比较,在大约 1023 次比较中对 256 个字符串进行排序(没有真正计算,只是有根据的猜测)。
    • 是插入排序?你为什么不这么说?没有人认为冒泡排序有什么好处。快速排序将是我的选择。但是这个答案又一次错失了重点。
    • 我不会把我的例程归为一个固定的类别,因为它是一种令人难以置信的快速通用方法,适用于各种排序。尽管没有针对 QuickSort 的良好实现对其进行测试,但我认为它的性能优于 QuickSort。我的回答没有错过重点。它只是没有把它拼出来。数据的存储使得这个例程可以像比较字符串数组一样使用。存储圈数和时间时,将它们存储为字符串,如“lap,hh:mm:ss:hs”。确保圈数始终包含 3 个字符。 001, 002 等等。无论是否插入排序,它都可以排序:)
    • 插入排序通常比快速排序慢。限制使用字符串是相当有限的。这意味着您不能直接使用它来解决此问题中的问题。与其尝试教我们,不如阅读公认的答案并尝试学习新技巧。
    猜你喜欢
    • 1970-01-01
    • 2011-06-16
    • 1970-01-01
    • 2017-04-24
    • 2020-04-30
    • 1970-01-01
    • 1970-01-01
    • 2011-05-10
    • 2012-04-13
    相关资源
    最近更新 更多