【问题标题】:Select X with Max(F(X))用 Max(F(X)) 选择 X
【发布时间】:2010-09-04 04:27:16
【问题描述】:

我正在尝试为跳棋游戏编写一些 AI。我想选择棋盘得分最高的棋子。

需要这样的东西:

var bestMove = from m in validMoves
                where BoardScore(opp, board.Clone().ApplyMove(m)) is max
                select m

除了我无法弄清楚“最大”部分。更希望它返回单个项目而不是枚举。


基本上相当于这样:

Move bestMove = null;
float highestScore = float.MinValue;
foreach (var move in validMoves)
{
    float score = BoardScore(opp, board.Clone().ApplyMove(move));
    if (score > highestScore)
    {
        highestScore = score;
        bestMove = move;
    }
}

【问题讨论】:

标签: c# linq


【解决方案1】:

你基本上不是已经弄清楚了吗?如果您编写自己的扩展方法,则可以以典型的 LINQ 方式实现此功能:

public static T MaxFrom<T, TValue>(this IEnumerable<T> source, Func<T, TValue> selector, IComparer<TValue> comparer)
{
    T itemWithMax = default(T);
    TValue max = default(TValue);

    using (var e = source.GetEnumerator())
    {
        if (e.MoveNext())
        {
            itemWithMax = e.Current;
            max = selector(itemWithMax);
        }

        while (e.MoveNext())
        {
            T item = e.Current;
            TValue value = selector(item);
            if (comparer.Compare(value, max) > 0)
            {
                itemWithMax = item;
                max = value;
            }
        }
    }

    return itemWithMax;
}

public static T MaxFrom<T, TValue>(this IEnumerable<T> source, Func<T, TValue> selector)
{
    return source.MaxFrom(selector, Comparer<TValue>.Default);
}

这样你就可以做到:

var bestMove = validMoves
    .MaxFrom(m => BoardScore(opp, board.Clone().ApplyMove(m)));

【讨论】:

  • 是的...我只是认为必须有一种内置的方式来使用 Linq 做到这一点。写我自己并不难 :) 尽管如此,你还是得到了我做脏活的支票。
【解决方案2】:
var scores = from m in validMoves
                select BoardScore(opp, board.Clone().ApplyMove(m));
var bestMove = scores.Max();

编辑:

我应该更仔细地阅读。你可以这样做:

var descendingMoves = validMoves.OrderByDescending(m=>BoardScore(opp, board.Clone().ApplyMove(m)));
var bestMove = descendingMoves.First();

但这是O(n log n)。我找到了一个blog post,讨论了这个问题。它提出了一个Max 扩展函数,它返回最大的原始值,而不是转换后的值。不幸的是,我不知道如何在 LINQ 中执行此操作。

【讨论】:

  • 对不起,没有。这正是我遇到的问题——我找到了Max() 函数,但它返回了float。我需要得分最高的move,而不是得分本身。
  • 总有一种方法可以不用对整个列表进行排序,不是吗?
  • @Mark,你可以使用我链接的代码,或者直接使用循环。
【解决方案3】:
    float max = validMoves.Max(m => BoardScore(opp, board.Clone().ApplyMove(m)));
    var bestMove = validMoves
          .Where(m => BoardScore(opp, board.Clone().ApplyMove(m)) == max)
          .First();

【讨论】:

  • 看起来它应该可以工作,但我很确定你重复了两次移动,这不应该是必要的。
【解决方案4】:

你会想看看Enumerable.Max(TSource) Method (IEnumerable(TSource), Func(TSource, Single))。即:

var bestMove = validMoves.Max((m)=>BoardScore(opp, board.Clone().ApplyMove(m)));

如果计算分数的成本很高,您可以预先计算所有分数,将它们与相应的移动配对,但我怀疑这是必要的。示例:

var scores = from m in validMoves
             select new {Move=m, Score=BoardScore(opp, board.Clone().ApplyMove(m))};
var bestMove = scores.Max(s=>s.Score).Move;

【讨论】:

  • 这些解决方案都不起作用。 Max() 返回一个分数。第二个示例甚至无法编译。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-18
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
  • 1970-01-01
  • 2023-03-07
相关资源
最近更新 更多