我的排序有什么问题?

时间:2016-01-29 19:54:04

标签: c# list sorting unity3d

首先,我必须指定我在Unity 5.3中工作,而新的MonoDevelop不允许我进行调试。 Unity崩溃了:(

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

  1. 首先应列出“活跃”目标
  2. 然后按难度等级排序
  3. 最终随机进行同级别目标
  4. 这是我的代码:

    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;
        }
    });
    

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

3 个答案:

答案 0 :(得分:3)

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

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

选项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);

is given here

选项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

随机播放算法来自Working sample

答案 1 :(得分:0)

试试这个lambda:

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

活动比级别差异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)

答案 2 :(得分:0)

我无法重现您在非活动目标之后获得一个或多个活跃目标的问题&#34;。听起来你的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排序可以随机使用。我已经对它进行了分布分析,它和渔民一样有效。