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