cad.net 排序(施工中...)

行式排序

// 容差需要set,而Point3d的无法set,直接暴露字段或者封装到构造函数内.
public class XYZD {
    public double X;
    public double Y;
    public double Z;
    public ObjectId ObjectId;

    public XYZD(double x, double y, double z, ObjectId id, double tol = 0.1) {
        if (tol <= 0)
            throw new ArgumentException("XYZD Tolerance <= zero.");
        if (double.IsNaN(tol))
            throw new ArgumentException("XYZD Tolerance is NaN."); 
        if (double.IsInfinity(tol))
            throw new ArgumentException("XYZD Tolerance is Infinity.");
        // 公差修正
        X = RoundToNearest(x, tol);
        Y = RoundToNearest(y, tol);
        Z = RoundToNearest(z, tol);
        ObjectId = id;
    }

    // 公差修正数值的方法
    double RoundToNearest(double value, double tol)
    {
        return Math.Round(value / tol) * tol;
    }

    // 内部比较器写法
    public int CompareTo(XYZD other)
    {
        // 首先比较X
        if (this.X != other.X) return this.X.CompareTo(other.X);
        // 如果X相同,比较Y
        if (this.Y != other.Y) return this.Y.CompareTo(other.Y);
        // 如果Y也相同,比较Z
        return this.Z.CompareTo(other.Z);
    }  
}

// 外部补充比较器写法
public class XYZDComparer : IComparer<XYZD>
{
    public int Compare(XYZD a, XYZD b)
    {
        // 首先比较X
        if (a.X != b.X) return a.X.CompareTo(b.X);            
        // 如果X相同,比较Y
        if (a.Y != b.Y) return a.Y.CompareTo(b.Y);    
        // 如果Y也相同,比较Z
        return a.Z.CompareTo(b.Z);
    }
}

// 调用:
public List<XYZD> SortDbPoints(List<ObjectId> ids) {
    var tr = DBTrans.Top;
    List<XYZD> list = new();
    for(int i = 0; i<ids.Count; i++) {
        using var ent = tr.GetObject<DBPoint>(ids[i], OpenMode.ForRead);
        if(ent is null) continue;
        list.Add(new(ent.Position.X, ent.Position.Y, ent.Position.Z, ids[i]);
    }

    // 1 内部比较器,它只会修改原有数组
    list.Sort();

/*
    // 2 外部比较器需要new,它只会修改原有数组
    list.Sort(new XYZDComparer());

    // 3 使用Linq进行排序,它会生成新的数组.
    list = list.OrderBy(item => item.X)
        .ThenBy(item => item.Y)
        .ThenBy(item => item.Z)
        .ToList();
*/
     return list;
}

列式排序

这应该是最快的方法了,
1,结构是SOA,能够SIMD.
2,排序完数组还可以在外部使用二分法Array.BinarySearch,进行其他比较.
3,比较时候是大量比较xList,因而CPU预读数组效果和储存效果最好.

原理就是共同索引,
xList内用快排获取交换的索引i和j,然后把其他数组一起交换.


public class MyData<T> {
    // 维度,载入顺序有优先级,
    // 先排序X,再相同X排序Y,相同Y排序Z
    public List<T> Mult = new();
    // 排序方式
    public List<SortOrder> SortOrders = new();
    public Count => SortOrders.Count;
    public InfoCount => Mult[0].Count;

    public MyData Add(T p, SortOrder so){
        Mult.Add(p);
        SortOrders.Add(so);
        return this;
    }
}
public enum SortOrder {
    None, // 无排序模式
    Asc, // 升序,从小到大
    Des // 降序,从大到小
}

public class XYZD{
    // 暴露成只读数组 public XArray => xList.AsReadOnly();
    public List<double> xList = [];
    public List<double> yList = [];
    public List<double> zList = [];
    public List<ObjectId> idList = [];
    double _tol;

    public XYZD(double tol = 0.1) {
        if (tol <= 0)
            throw new ArgumentException("XYZD Tolerance <= zero.");
        if (double.IsNaN(tol))
            throw new ArgumentException("XYZD Tolerance is NaN."); 
        if (double.IsInfinity(tol))
            throw new ArgumentException("XYZD Tolerance is Infinity.");
        _tol = tol;
    }

    public XYZD(List<Point3d> pts, List<ObjectId> ids, double tol = 0.1) :this(tol) {
        if(pts.Count != ids.Count)
            throw new ArgumentException("XYZD 你做咩唔对齐啊");

        for(int i = 0; i < ids.Count; i++) {
/*
            // 公差修正,可以不写在这里,而是线性比较的时候顺便处理,还可以减少除法器.
            xList.Add(RoundToNearest(pts[i].X, _tol));
            yList.Add(RoundToNearest(pts[i].Y, _tol));
            zList.Add(RoundToNearest(pts[i].Z, _tol));
*/
            xList.Add(pts[i].X);
            yList.Add(pts[i].Y);
            zList.Add(pts[i].Z);
            idList.Add(ids[i]);
        }
    }

    // 加入时候展开数据,使得内部可以替换Point字段
    public void Add(Point3d pt, ObjectId id) {
        xList.Add(pt.X);
        yList.Add(pt.Y);
        zList.Add(pt.Z);
        idList.Add(id);
    }

    // 01下上分行,行左起
    public void SortLineToBTLR() {
        var md = new MyData()
            .Add(yList, SortOrder.Asc)
            .Add(xList, SortOrder.Asc)
            .Add(zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount-1);
    }

    // 02下上分行,行右起
    public void SortLineToBTRL() {
        var md = new MyData()
            .Add(yList, SortOrder.Asc)
            .Add(xList, SortOrder.Des)
            .Add(zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount-1);
    }

    // 03上下分行,行右起(图像和01一样,只是逆序)
    public void SortLineToTBRL() {
        var md = new MyData()
            .Add(yList, SortOrder.Des)
            .Add(xList, SortOrder.Des),
            .Add(zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount-1);
    }

    // 04上下分行,行左起
    public void SortLineToTBLR() {
        var md = new MyData()
            .Add(yList, SortOrder.Des)
            .Add(xList, SortOrder.Asc)
            .Add(zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount-1);
    }

    // 05左右分列,列下起
    public void SortRowToLRBT() {
        var md = new MyData()
            .Add(xList, SortOrder.Asc)
            .Add(yList, SortOrder.Asc)
            .Add(zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount-1);
    }

    // 06左右分列,列上起
    public void SortRowToLRTB() {
        var md = new MyData()
            .Add(xList, SortOrder.Asc)
            .Add(yList, SortOrder.Des)
            .Add(zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount-1);
    }
    
    // 07右左分列,列上起(图像和05一样,只是逆序)
    public void SortRowToRLTB() {
        var md = new MyData()
            .Add(xList, SortOrder.Des)
            .Add(yList, SortOrder.Des)
            .Add(zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount-1);
    }

    // 08右左分列,列下起
    public void SortRowToRLBT() {
        var md = new MyData()
            .Add(xList, SortOrder.Des)
            .Add(yList, SortOrder.Asc)
            .Add(zList, SortOrder.Asc);
        Sort(md, 0, 0, md.InfoCount-1);
    }

    // 递归排序
    void Sort(MyData md, int mdIndex, int start, int end) {
        if (end < 0)
            throw new ("错误的end");
        var so = md.SortOrders[mdIndex];
        if (so == SortOrder.None) 
            return;
        var xyzList = md.Mult[mdIndex];
        var des = so == SortOrder.Des; //升序降序
        QuickSort(xyzList, start, end, des);
        
        // 排序完之后,同X排序Y,同Y排序Z
        // 降序就要倒序遍历
        int step = des ? -1 : 1;
        if (des) (start,end)=(end,start);
        // 连续相同范围,起始索引
        int qqstart = -1;
        for (int i = start; i != end; i += step) {
            int a = i + step;
            // 下上/上下,若反转这里是y,下面是x;
            // 这里不需要abs,因为快排执行肯定有序
            var x = xyzList[a] - xyzList[i];
            if (x > _tol) {
                if (qqstart != -1) {
                    Sort(md, mdIndex+1/*当前xList,加1就是yList*/, qqstart, i - step);
                    qqstart = -1;
                }
                continue;
            }
            // 抹平微量偏差,斜线变成阶梯,排序多次之后岂不是越来越平了?
            xyzList[a] = xyzList[i];
            if (qqstart == -1) 
                qqstart = i;
        }
    }

    void QuickSort(List<double> aList, int low, int high, bool descending = false) {
        if (low < high) {
            int pi = Partition(aList, low, high, descending);
            QuickSort(aList, low, pi - 1, descending);
            QuickSort(aList, pi + 1, high, descending);
        }
    }

#if NET45_OR_GREATER
    [MethodImpl(MethodImplOptions.AggressiveInlining)]  
#endif
    int Partition(List<double> aList, int low, int high, bool descending = false) {
        var pivot = aList[high];
        int i = low - 1;
        for (int j = low; j < high; j++) {
            // 这里存在相等条件,所以必须要大于小于
            if (descending ? aList[j] > pivot : aList[j] < pivot) {
                i++;
                Swap(xList, i, j);
                Swap(yList, i, j);
                Swap(zList, i, j);
                Swap(idList, i, j);
            }
        }
        Swap(xList, i+1, high);
        Swap(yList, i+1, high);
        Swap(zList, i+1, high);
        Swap(idList, i+1, high);
        return i + 1;
    }

#if NET45_OR_GREATER
    [MethodImpl(MethodImplOptions.AggressiveInlining)]  
#endif
    // 元组交换
    void Swap<T>(List<T> aList, int index1, int index2) {
        (aList[index1], aList[index2]) = (aList[index2], aList[index1]);
    }

}

命令测试

[CommandMethod(nameof(TestCmdXYZD))]
public void TestCmdXYZD()
{
    var pso = new PromptSelectionOptions();
    var ssPsr = Env.Editor.GetSelection(pso);
    if (ssPsr.Status != PromptStatus.OK)return;

    using DBTrans tr = new();
    XYZD xyzd = new(1.0); // 公差数值

    var ids = ssPsr.Value.GetObjectIds();
    for (int i = 0; i < ids.Length; i++)
    {
        using var ent = tr.GetObject<DBPoint>(ids[i], OpenMode.ForRead);
        if(ent is null) continue;
        xyzd.Add(ent.Position, ids[i]);
    }

    // 利用多段线查看排序
    if (xyzd.xList.Count < 2) return;

    xyzd.SortLineToBTLR(); //已测试
    PolylinePrint(xyzd, 1);
    xyzd.SortLineToBTRL();
    PolylinePrint(xyzd, 2);
    xyzd.SortLineToTBRL(); // 已测试
    PolylinePrint(xyzd, 3);
    xyzd.SortLineToTBLR();
    PolylinePrint(xyzd, 4);
    xyzd.SortRowToLRBT(); // 已测试
    PolylinePrint(xyzd, 5);
    xyzd.SortRowToLRTB();
    PolylinePrint(xyzd, 6);
    xyzd.SortRowToRLTB(); // 已测试
    PolylinePrint(xyzd, 7);
    xyzd.SortRowToRLBT();
    PolylinePrint(xyzd, 8);
}

void PolylinePrint(XYZD xyzd, int color) {
    var tr = DBTrans.Top;

    Polyline pl = new();
    pl.SetDatabaseDefaults();
    pl.ColorIndex = color;
    for (int i = 0; i < xyzd.xList.Count; i++)
        pl.AddVertexAt(i, new Point2d(xyzd.xList[i], xyzd.yList[i]), 0, 0, 0);
    tr.CurrentSpace.AddEntity(pl);

    // 逆序的线看不出差别,用文字数据来区别.
    StringBuilder sb = new();
    for (int i = 0; i < xyzd.xList.Count; i++)
        sb.AppendLine($"({xyzd.xList[i]},{xyzd.yList[i]},{xyzd.zList[i]})");
    Env.Printl(sb.ToString());
}

块排测试,从小到大

using System;

public class Program
{
    public static void Main(string[] args)
    {
        int[] arr = { 3, 6, 8, 10, 1, 2, 1 };
        QuickSort(arr, 0, arr.Length - 1);
        foreach (var item in arr)
        {
            Console.Write(item + " ");
        }
    }

    public static void QuickSort(int[] arr, int low, int high)
    {
        if (low < high)
        {
            int pi = Partition(arr, low, high);

            QuickSort(arr, low, pi - 1);  // Before pi
            QuickSort(arr, pi + 1, high); // After pi
        }
    }

    private static int Partition(int[] arr, int low, int high)
    {
        int pivot = arr[high];
        int i = (low - 1);

        for (int j = low; j < high; j++)
        {
            if (arr[j] < pivot)
            {
                i++;

                // Swap arr[i] and arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }

        // Swap arr[i+1] and arr[high] (or pivot)
        int temp2 = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp2;

        return i + 1;
    }
}

快排测试,从大到小

using System;
public class Program
{
    public static void Main()
    {
        int[] arr = { 3, 6, 8, 10, 1, 2, 1 };
        QuickSortDescending(arr, 0, arr.Length - 1);
        foreach (var item in arr)
        {
            Console.Write(item + " ");
        }
    }

    private static void QuickSortDescending(int[] arr, int left, int right)
    {
        if (left < right)
        {
            int pivotIndex = PartitionDescending(arr, left, right);
            QuickSortDescending(arr, left, pivotIndex - 1);
            QuickSortDescending(arr, pivotIndex + 1, right);
        }
    }

    private static int PartitionDescending(int[] arr, int left, int right)
    {
        int pivot = arr[right]; // 选择最右侧的元素作为基准
        int i = left - 1;
        for (int j = left; j < right; j++)
        {
            if (arr[j] > pivot) // 逆序排序,所以这里是大于pivot
            {
                i++;
                // 交换arr[i]和arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
        // 交换arr[i+1]和arr[right](基准值)
        int temp2 = arr[i + 1];
        arr[i + 1] = arr[right];
        arr[right] = temp2;

        return i + 1; // 返回基准值的最终位置
    }
}

树排序

红黑树,AVL树,跳表,B树家族(B树/B+树/BW树/LSM树)
C#的SortedDictionary来进行,
并且用类似定点数的组合数作为hashcode,
把hashcode的int32分成三块,32/3=10bit,多出来2bit不用.
[空][X][Y][Z]
这样就能同X比较Y,同Y比较Z.
能利用上><=比较了.

缺点:
局限精度,10bit表达范围太少了
这种有序结构会截断式hash吗?应该不会吧.

using System;

public struct Point3d
{
    public double X;
    public double Y;
    public double Z;

    public Point3d(double x, double y, double z)
    {
        X = x;
        Y = y;
        Z = z;
    }

    public override int GetHashCode()
    {
        // 公差值用于四舍五入
        double tol = 0.0001;

        // 四舍五入X, Y, Z到最接近的整数
        int roundedX = (int)Math.Round(X / tol) * (int)tol;
        int roundedY = (int)Math.Round(Y / tol) * (int)tol;
        int roundedZ = (int)Math.Round(Z / tol) * (int)tol;

        // 组合数
        return = roundedX << 20 | roundedY << 10 | roundedZ;
    }
}

公差原理

using System;

class Program
{
    static void Main()
    {
        // 千位舍入的例子
        double value1 = 2011;
        double tol1 = 1000;
        double roundedValue1 = RoundToNearest(value1, tol1);
        Console.WriteLine($"{value1} rounded to the nearest {tol1} is {roundedValue1}"); 
// 输出:2011 rounded to the nearest 1000 is 2000

        // 小数点后一位舍入的例子
        double value2 = 1.5;
        double tol2 = 0.1;
        double roundedValue2 = RoundToNearest(value2, tol2);
        Console.WriteLine($"{value2} rounded to the nearest {tol2} is {roundedValue2}"); 
// 输出:1.5 rounded to the nearest 0.1 is 1.5

        // 更多测试
        double value3 = 1234.5678;
        double tol3 = 0.05; // 保留两位小数
        double roundedValue3 = RoundToNearest(value3, tol3);
        Console.WriteLine($"{value3} rounded to the nearest {tol3} is {roundedValue3}"); 
// 输出:1234.5678 rounded to the nearest 0.05 is 1234.60
    }

    // 公差修正数值的方法
    static double RoundToNearest(double value, double tol)
    {
        if (tol == 0)
            throw new ArgumentException("Tolerance cannot be zero.");

        return Math.Round(value / tol) * tol;
    }
}
posted @ 2024-11-20 16:28  惊惊  阅读(73)  评论(0编辑  收藏  举报