【问题标题】:Distinct values from List of objects对象列表中的不同值
【发布时间】:2013-06-05 08:51:33
【问题描述】:

我需要你的帮助。我正在尝试从对象列表中获取不同的值。 我的班级是这样的:

class Chromosome
{
    public bool[][] body { get; set; }
    public double fitness { get; set; }
}

现在我有List<Chromosome> population。现在我需要的是一种方法,如何获得新列表:List<Chromosome> newGeneration。这个新列表将仅包含来自原始列表 - 种群的唯一染色体。

染色体是独一无二的,当他的整个身体(在本例中为 2D bool 数组)与其他染色体相比是独一无二的。 我知道,有类似 MoreLINQ 的东西,但我不确定是否应该使用 3rd 方代码,并且我知道我应该覆盖一些方法,但我有点迷茫。所以我真的很感激一些很好的一步一步的描述,即使是白痴也能做到:) 谢谢

【问题讨论】:

  • 你能给我们一些输入/输出的例子吗?
  • 嗯,我正在做遗传算法,染色体的主体代表关联规则的二进制表示。 Body 代表整个规则,我稍后对其进行解码,然后根据适应度函数评估其质量并将其保存到适应度中。它背后的代码要大得多,我只是从中抽象出来的。因为我只是不知道任何优雅的方式,所以我如何获得其他列表的不同对象列表(根据 2D 布尔数组的相似性进行比较)...

标签: c# linq distinct-values


【解决方案1】:

首先,实现相等运算符(这进入class Chromosome):

public class Chromosome : IEquatable<Chromosome>
{

    public bool[][] body { get; set; }
    public double fitness { get; set; }

    bool IEquatable<Chromosome>.Equals(Chromosome other)
    {
        // Compare fitness
        if(fitness != other.fitness) return false;

        // Make sure we don't get IndexOutOfBounds on one of them
        if(body.Length != other.body.Length) return false;

        for(var x = 0; x < body.Length; x++)
        {
            // IndexOutOfBounds on inner arrays
            if(body[x].Length != other.body[x].Length) return false;

            for(var y = 0; y < body[x].Length; y++)
                // Compare bodies
                if(body[x][y] != other.body[x][y]) return false;
        }

        // No difference found
        return true;
    }

    // ReSharper's suggestion for equality members

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj))
        {
            return false;
        }
        if (ReferenceEquals(this, obj))
        {
            return true;
        }
        if (obj.GetType() != this.GetType())
        {
            return false;
        }
        return this.Equals((Chromosome)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((this.body != null ? this.body.GetHashCode() : 0) * 397) ^ this.fitness.GetHashCode();
        }
    }
}

然后,使用Distinct:

var newGeneration = population.Distinct().ToList();

【讨论】:

  • 你真的应该同时覆盖EqualsGetHashCode。此外,如果您不想将其直接添加到您的课程中,您可以使用 DistinctIEqualityComparer&lt;T&gt; 重载。
  • @Trustme-I'maDoctor 你的用户名非常适合这个问题。
  • @Trustme-I'maDoctor:你对GetHashCode有什么建议?我想不出任何有意义且易于实施的想法
  • @csharpler 我要么使用 ReSharper 建议我的默认值(我现在不记得了),要么使用基于素数的有效 Java,Jon Skeet's answer shows it best
  • 我可能有一个愚蠢的问题 :) 但我怎么能在函数中使用 void 而不是使用 return?
【解决方案2】:
public class ChromosomeBodyComparer : IEqualityComparer<Chromosome>
{
  private bool EqualValues(bool[][] left, bool[][] right)
  {
    if (left.Length != right.Length)
    {
      return false;
    }
    return left.Zip(right, (x, y) => x.SequenceEquals(y)).All();
  }

  public bool Equals(Chromosome left, Chromosome right)
  {
    return EqualValues(left.body, right.body)
  }

     //implementing GetHashCode is hard.
     // here is a rubbish implementation.
  public int GetHashCode(Chromosome c)
  {
    int numberOfBools = c.body.SelectMany(x => x).Count();
    int numberOfTrues = c.body.SelectMany(x => x).Where(b => b).Count();
    return (17 * numberOfBools) + (23 * numberOfTrues);

  }
}

调用者:

List<Chromosome> nextGeneration = population
  .Distinct(new ChromosomeBodyComparer())
  .ToList();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-07
    • 1970-01-01
    • 1970-01-01
    • 2015-03-07
    • 1970-01-01
    相关资源
    最近更新 更多