【问题标题】:What is wrong with my sorting?我的排序有什么问题?
【发布时间】:2016-01-29 19:54:03
【问题描述】:

首先我必须说明我正在使用 Unity 5.3 并且新的 MonoDevelop 不允许我进行调试。 Unity 崩溃了 :(

所以我有一个“目标”列表,我需要根据 3 个标准进行排序:

  1. 首先应列出“活动”目标
  2. 然后按难度级别排序
  3. 最终随机获得相同级别的目标

这是我的代码:

public class Goal {
    public int ID;
    public int Level;
    public bool Active;
}

...

List<Goal> goals;

goals.Sort((a, b) => {
    // first chooses the Active ones (if any)
    var sort = b.Active.CompareTo(a.Active);
    if (sort == 0) {
        // then sort by level
        sort = (a.Level).CompareTo(b.Level);
        // if same level, randomize. Returns -1, 0 or 1
        return sort == 0 ? UnityEngine.Random.Range(-1, 2) : sort;
    } else {
        return sort;
    }
});

当我运行此代码时,有时我会在不活跃的目标之后获得一个或多个活跃的目标,但我不明白为什么。

【问题讨论】:

  • 你能把行为缩小到一组特定的输入吗?
  • 问题可能与排序顺序不唯一有关。也就是说:任何给定的订单可能都是正确的,但由于相同优先级项目的随机化,任何验证此订单的尝试都将返回不同的订单。我会找到一种不同的洗牌方式
  • 您能否提供一个重现(或有可能重现)该问题的示例输入?
  • 您是否有更改现有目标的 Active 属性的并发代码?如果是,那么我猜测目标的 Active 属性在排序期间甚至之后发生了变化。另外:也许目标是在排序之后添加的。
  • @mcmorry 请让我们知道它是什么 :)

标签: c# list sorting unity3d


【解决方案1】:

为了正常工作,排序算法不应依赖于变异状态。解释为什么在比较值时使用随机生成器不是一个好主意is given here。

这个问题可以通过两种方式解决:

选项 1:预先计算随机数

    var tmp = goals.Select( g=> new {goal = g, weight = rnd.NextDouble()})
        .OrderByDescending(t=>t.goal.Active) // Active first
        .ThenBy(t=>t.goal.Level)
        .ThenBy(t=>t.weight)
        .Select(t=>t.goal)
        .ToList();



    goals.Clear();
    goals.AddRange(tmp);

Working sample

选项 2:排序然后随机播放

Random rnd = new Random();

Comparison<Goal> comparison =  (a, b) => {
// first chooses the Active ones (if any)
var sort = b.Active.CompareTo(a.Active);

if (sort == 0) {
// then sort by level
    return sort = (a.Level).CompareTo(b.Level);
  } else 
{
   return sort;
}
};



int startIndex = 0;
int endIndex = 0;

goals.Sort(comparison);

while (startIndex < goals.Count)
{
    for (endIndex = startIndex + 1; endIndex < goals.Count; ++endIndex)
    {
       if (comparison(goals[startIndex], goals[endIndex]) != 0)
       {
          //End of tie
          break;
       }
    }

    if (endIndex - startIndex > 1)
    {
       // Shuffle goals of the same level
       ShuffleRange(goals, startIndex, endIndex - startIndex, rnd);
    }

    startIndex = endIndex;
}   

static void ShuffleRange<T>(List<T> list, int startIndex, int count, Random rnd)
{
     int n = startIndex + count;  
     while (n > startIndex + 1) 
     {  
        int k = rnd.Next(startIndex, n--);  
        T value = list[k];  
        list[k] = list[n];  
        list[n] = value;  
    }  
}           

Working sample

Shuffle 算法借鉴自here

【讨论】:

  • OP 的随机化仅在更深层次元素的排序中(在根据活动和优先级对其他优先级分配正确的顺序之后),这无关紧要。没有?
  • 也请尝试 OP 的代码。它按照 OP 的意图工作——它们之间的随机排序值的顺序是不相关的。
  • @andeart:OP 最后指出代码不能总是正常工作。
  • ...这就是 OP 认为的问题所在。您是说 OP 发布的代码有潜在的错误,不会给出 OP 想要的结果吗?正如其他一些人在 cmets 中直接讨论的那样,代码正确地给出了 OP 想要的结果,并且问题可能根本不存在(正如 OP 在发布时可能已经想到的那样)。
  • 只是分析;)
【解决方案2】:

试试这个 lambda:

(a, b) => ((b.Active ? 1000 : 0) + b.Level) - ((a.Active ? 1000 : 0) + a.Level)

Active 比等级差 1 重要 1000 倍。这适用于多达 1000 个等级。当 Active 相同时,级别变得相关。最后,如果仍然是相同的,它将以确定性但不相关的方式进行排序,这与随机相同。

无需使用真正的随机数。一次运行中的顺序将始终相同,但运行之间可能会有所不同。如果你真的需要随机顺序,你可以使用这个:

(a, b) =>
    ((b.Active ? 10000 : 0) + b.Level * 10) -
    ((a.Active ? 10000 : 0) + a.Level * 10) + UnityEngine.Random.Range(-1, 2)

【讨论】:

    【解决方案3】:

    我无法重现您的问题,即“在不活跃的目标之后获得一个或多个活跃的目标”。听起来您的 Goal 实例在排序后正在发生变异。我建议尽可能制作只读对象。

    我的另一个建议是简化您的排序代码,使其更清晰、更易于推理 - 尽管在这种情况下这可能不会直接帮助。

    就这样吧:

    var sorted =
    (
        from g in goals
        orderby g.Active descending, g.Level, UnityEngine.Random.Range(-1, 2)
        select g
    .ToList();
    

    ...或者替代地和等效地像这样:

    var sorted =
        goals
            .OrderByDescending(g => g.Active)
            .ThenBy(g => g.Level)
            .ThenBy(g => rnd.Next())
            .ToList();
    

    LINQ 排序可以很好地处理随机源。我已经对其进行了分布分析,它的效果和 Fisher-yates 一样好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-27
      相关资源
      最近更新 更多