You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#技术问题:如何将自定义对象列表划分为不同大小的分区?

嘿,我来帮你搞定这个自定义对象列表按相似度聚类分区的问题!

基于相似度的自定义对象列表聚类方案

第一步:先明确相似度的定义

聚类的核心是怎么判定两个Foo对象是相似的——这完全取决于你的业务场景。比如如果Foo有数值型属性,你可以用距离转相似度;如果是文本类属性,可能要用到文本相似度算法。这里给你举个简单的数值属性示例:

public class Foo
{
    public int Id { get; set; }
    public double FeatureValue { get; set; }

    // 计算当前Foo和另一个Foo的相似度(值越接近,相似度越高)
    public double GetSimilarity(Foo other)
    {
        // 用欧氏距离的倒数转成0-1区间的相似度,距离为0时相似度=1
        var distance = Math.Abs(this.FeatureValue - other.FeatureValue);
        return 1 / (1 + distance);
    }
}

第二步:选择合适的聚类算法

根据你“分区大小可不同、单个对象也能成区”的需求,推荐这几个方向:

  • 层次聚类:不需要提前指定聚类数量,你可以设置一个相似度阈值,当簇之间的相似度低于阈值时就停止合并,自然形成大小不一的分区,非常贴合你的需求。
  • DBSCAN:适合有密度特征的数据,能自动识别孤立点(也就是单个对象的分区),不过需要调整邻域半径和最小点数两个参数。
  • 如果能接受提前指定聚类数量,K-Means也可以用,但需要后处理调整分区大小,灵活性稍差。

第三步:实现聚类逻辑(以层次聚类为例)

下面是把Bar类里的fooList按相似度分区的具体实现:

public class Bar
{
    public List<Foo> fooList = new List<Foo>();

    // 传入相似度阈值,返回聚类后的分区列表
    public List<List<Foo>> PartitionBySimilarity(double similarityThreshold)
    {
        // 初始化:每个Foo单独作为一个簇
        var clusters = fooList.Select(foo => new List<Foo> { foo }).ToList();

        // 循环合并最相似的簇,直到没有符合条件的簇可合并
        while (true)
        {
            double highestSimilarity = -1;
            int clusterAIdx = -1;
            int clusterBIdx = -1;

            // 遍历所有簇对,找到相似度最高的一对
            for (int i = 0; i < clusters.Count; i++)
            {
                for (int j = i + 1; j < clusters.Count; j++)
                {
                    var currentSimilarity = CalculateClusterSimilarity(clusters[i], clusters[j]);
                    if (currentSimilarity > highestSimilarity)
                    {
                        highestSimilarity = currentSimilarity;
                        clusterAIdx = i;
                        clusterBIdx = j;
                    }
                }
            }

            // 如果最高相似度低于阈值,停止合并
            if (highestSimilarity < similarityThreshold)
                break;

            // 合并两个簇
            clusters[clusterAIdx].AddRange(clusters[clusterBIdx]);
            clusters.RemoveAt(clusterBIdx);
        }

        return clusters;
    }

    // 计算两个簇的相似度:用簇内所有对象对的平均相似度
    private double CalculateClusterSimilarity(List<Foo> clusterA, List<Foo> clusterB)
    {
        double totalSimilarity = 0;
        int pairCount = 0;

        foreach (var fooA in clusterA)
        {
            foreach (var fooB in clusterB)
            {
                totalSimilarity += fooA.GetSimilarity(fooB);
                pairCount++;
            }
        }

        return pairCount == 0 ? 0 : totalSimilarity / pairCount;
    }
}

第四步:使用示例

// 构造测试数据
var bar = new Bar();
bar.fooList.AddRange(new List<Foo>
{
    new Foo { Id = 1, FeatureValue = 0.2 },
    new Foo { Id = 2, FeatureValue = 0.25 },
    new Foo { Id = 3, FeatureValue = 0.8 },
    new Foo { Id = 4, FeatureValue = 0.78 },
    new Foo { Id = 5, FeatureValue = 0.1 }
});

// 设置相似度阈值为0.8,进行分区
var partitionedList = bar.PartitionBySimilarity(0.8);

// 输出结果
foreach (var cluster in partitionedList)
{
    Console.WriteLine($"分区包含的Foo ID:{string.Join(", ", cluster.Select(f => f.Id))}");
}

一些实用建议

  • 相似度函数一定要贴合你的业务!比如如果Foo是用户对象,可能要基于兴趣标签的重合度计算相似度;如果是图像特征,要用余弦相似度。
  • 如果你的数据量特别大(比如上万条),上面的手写层次聚类效率会很低,建议用成熟的机器学习库,比如ML.NET或者Accord.NET,它们已经实现了优化的聚类算法,直接调用就行。
  • 阈值需要多测试调整,找到最符合你预期的分区效果。

内容的提问来源于stack exchange,提问作者Chippy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:37:07