C# 中最优雅的 shell 排序(梳状/递减增量排序)方式是什么?

发布于 2024-08-07 21:33:07 字数 1167 浏览 4 评论 0原文

有没有更好的方法使用 C# 进行希尔排序?

// array of integers to hold values
private int[] a = new int[100];

// number of elements in array
private int count;

// Shell Sort Algorithm
public void sortArray()
{
  int i, j, increment, temp;

  increment = 3;

  while( increment > 0 )
  {
    for( i=0; i < count; i++ )
    {
      j = i;
      temp = a[i];

      while( (j >= increment) && (a[j-increment] > temp) )
      {
        a[j] = a[j - increment];
        j = j - increment;
      }

      a[j] = temp;
    }

    if( increment/2 != 0 )
    {
      increment = increment/2;
    }
    else if( increment == 1 )
    {
      increment = 0;
    }
    else
    {
      increment = 1;
    }
  }
}

顺便说一句,我想知道,因为我有一些不同语言中“优雅”排序的不同示例(例如 C#F#),我正在比较它们。在现实生活中,我可能大部分时间都会在 C# 中使用以下模式:

Array.Sort( object[] )

我不在乎这些模式是否是“学术​​”和非实用模式。如果你愿意的话,你可以将我修改为遗忘:)

KA

Is there a better way to shell sort using C#?

// array of integers to hold values
private int[] a = new int[100];

// number of elements in array
private int count;

// Shell Sort Algorithm
public void sortArray()
{
  int i, j, increment, temp;

  increment = 3;

  while( increment > 0 )
  {
    for( i=0; i < count; i++ )
    {
      j = i;
      temp = a[i];

      while( (j >= increment) && (a[j-increment] > temp) )
      {
        a[j] = a[j - increment];
        j = j - increment;
      }

      a[j] = temp;
    }

    if( increment/2 != 0 )
    {
      increment = increment/2;
    }
    else if( increment == 1 )
    {
      increment = 0;
    }
    else
    {
      increment = 1;
    }
  }
}

By the way, I'm wondering because I have some different examples of 'elegant' sorts in different languages (like bubble sorting in C# and F#), and I'm comparing them. In real life, I'd probably use the following most of the time in C#:

Array.Sort( object[] )

I don't care if these are 'academic' and non-pragmatic patterns. You can down-mod me into oblivion if you want :)

KA

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

对你而言 2024-08-14 21:33:07

您可以很容易地进行改进:

  • 不要在对象中保留状态。只需使用局部变量就可以很容易地做到这一点。
  • 使用传统的 C# 名称,例如 ShellSort 而不是 shellSort
  • 使用有意义的名称,例如 count 而不是 x
  • 使用条件运算符,例如

    // 这将替换您的最后 12 行
    int halfIncrement = 增量 / 2;
    增量 = 半增量 != 0 ? halfIncrement : 1 - 增量;
    
  • 使代码通用 - 为什么将自己限制为整数?

  • 让您的方法获取有问题的数据,并将其设为 IList
  • 通过 IComparer 任意排序,提供使用默认值的重载。
  • 尽可能晚地声明变量以减少其范围并提高可读性

实际上很少与排序相关 - 我还没有验证您的代码是否实际上是合法的 shell 排序...

Improvements you could very easily make:

  • Don't keep state in the object. This should be easy to do just with local variables.
  • Use conventional C# names like ShellSort rather than shellSort
  • Use meaningful names like count instead of x
  • Use the conditional operator, e.g.

    // This replaces your last 12 lines
    int halfIncrement = increment / 2;
    increment = halfIncrement != 0 ? halfIncrement : 1 - increment;
    
  • Make the code generic - why restrict yourself to integers?

  • Make your method take the data in question, and make it an IList<T>
  • Make the sort order arbitrary via an IComparer<T>, providing an overload which uses the default.
  • Declare variables as late as possible to reduce their scope and improve readability

Very little of this is actually sort-related - I haven't verified whether your code is actually a legitimate shell sort or not...

影子的影子 2024-08-14 21:33:07

在我的测试中,这比使用相同 System.Func 比较器的数组排序快 75% 到 %90。我用它来对自定义结构进行排序。您可以轻松修改它以对类进行排序。

public class DualQuickSort<T> where T : struct
    {
        private readonly System.Func<T, T, int> comparer;
        public DualQuickSort(System.Func<T, T, int> comparer)
        {
            this.comparer = comparer;
        }

    public DualQuickSort(IComparer<T> comparer)
        : this(comparer.Compare)
    {

    }

    public  void Sort(T[] a)
    {
        Sort(a, 0, a.Length);
    }
    public  void Sort(T[] a, int fromIndex, int toIndex)
    {
        RangeCheck(a.Length, fromIndex, toIndex);

        DualPivotQuicksort(a, fromIndex, toIndex - 1, 3);
    }
    private static void RangeCheck(int length, int fromIndex, int toIndex)
    {
        if (fromIndex > toIndex)
        {
            throw new ArgumentException("fromIndex > toIndex");
        }
        if (fromIndex < 0)
        {
            throw new IndexOutOfRangeException(fromIndex + " is less than 0");
        }
        if (toIndex > length)
        {
            throw new IndexOutOfRangeException(toIndex + " is greater than " + fromIndex);
        }
    }

    private static void Swap(T[] a, int i, int j)
    {
        var temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
    private  void DualPivotQuicksort(T[] a, int left, int right, int div)
    {
        var len = right - left;

        if (len < 27)
        { // insertion sort for tiny array
            for (var i = left + 1; i <= right; i++)
            {
                for (var j = i; j > left && comparer(a[j] , a[j - 1])==-1; j--)

                {
                    Swap(a, j, j - 1);
                }
            }
            return;
        }
        var third = len / div;
        // "medians"
        var m1 = left + third;
        var m2 = right - third;
        if (m1 <= left)
        {
            m1 = left + 1;
        }
        if (m2 >= right)
        {
            m2 = right - 1;
        }
        if (comparer(a[m1] , a[m2])==-1)
        {
            Swap(a, m1, left);
            Swap(a, m2, right);
        }
        else
        {
            Swap(a, m1, right);
            Swap(a, m2, left);
        }
        // pivots
        var pivot1 = a[left];
        var pivot2 = a[right];
        // pointers
        var less = left + 1;
        var great = right - 1;
        // sorting
        for (var k = less; k <= great; k++)
        {
            if (comparer(a[k] , pivot1)==-1)
            {
                Swap(a, k, less++);
            }
            else if (comparer(a[k], pivot2) == 1)
            {
                while (k < great && comparer(a[great] , pivot2)==1)
                {
                    great--;
                }
                Swap(a, k, great--);
                if (comparer(a[k], pivot1) == -1)
                {
                    Swap(a, k, less++);
                }
            }
        }
        // Swaps
        var dist = great - less;
        if (dist < 13)
        {
            div++;
        }
        Swap(a, less - 1, left);
        Swap(a, great + 1, right);
        // subarrays
        DualPivotQuicksort(a, left, less - 2, div);
        DualPivotQuicksort(a, great + 2, right, div);

        // equal elements
        if (dist > len - 13 && comparer(pivot1,pivot2)!=0)
        {
            for (int k = less; k <= great; k++)
            {
                if (comparer(a[k] , pivot1)==0)
                {
                    Swap(a, k, less++);
                }
                else if (comparer(a[k], pivot2) == 0)
                {
                    Swap(a, k, great--);
                    if (comparer(a[k], pivot1) == 0)
                    {
                        Swap(a, k, less++);
                    }
                }
            }
        }
        // subarray
        if (comparer(pivot1 , pivot2)==-1)
        {
            DualPivotQuicksort(a, less, great, div);
        }
    }
}

In my test this is 75% to %90 faster than array sort with the same System.Func comparer. I use it to sort a custom struct. you can easily modify it to sort classes.

public class DualQuickSort<T> where T : struct
    {
        private readonly System.Func<T, T, int> comparer;
        public DualQuickSort(System.Func<T, T, int> comparer)
        {
            this.comparer = comparer;
        }

    public DualQuickSort(IComparer<T> comparer)
        : this(comparer.Compare)
    {

    }

    public  void Sort(T[] a)
    {
        Sort(a, 0, a.Length);
    }
    public  void Sort(T[] a, int fromIndex, int toIndex)
    {
        RangeCheck(a.Length, fromIndex, toIndex);

        DualPivotQuicksort(a, fromIndex, toIndex - 1, 3);
    }
    private static void RangeCheck(int length, int fromIndex, int toIndex)
    {
        if (fromIndex > toIndex)
        {
            throw new ArgumentException("fromIndex > toIndex");
        }
        if (fromIndex < 0)
        {
            throw new IndexOutOfRangeException(fromIndex + " is less than 0");
        }
        if (toIndex > length)
        {
            throw new IndexOutOfRangeException(toIndex + " is greater than " + fromIndex);
        }
    }

    private static void Swap(T[] a, int i, int j)
    {
        var temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
    private  void DualPivotQuicksort(T[] a, int left, int right, int div)
    {
        var len = right - left;

        if (len < 27)
        { // insertion sort for tiny array
            for (var i = left + 1; i <= right; i++)
            {
                for (var j = i; j > left && comparer(a[j] , a[j - 1])==-1; j--)

                {
                    Swap(a, j, j - 1);
                }
            }
            return;
        }
        var third = len / div;
        // "medians"
        var m1 = left + third;
        var m2 = right - third;
        if (m1 <= left)
        {
            m1 = left + 1;
        }
        if (m2 >= right)
        {
            m2 = right - 1;
        }
        if (comparer(a[m1] , a[m2])==-1)
        {
            Swap(a, m1, left);
            Swap(a, m2, right);
        }
        else
        {
            Swap(a, m1, right);
            Swap(a, m2, left);
        }
        // pivots
        var pivot1 = a[left];
        var pivot2 = a[right];
        // pointers
        var less = left + 1;
        var great = right - 1;
        // sorting
        for (var k = less; k <= great; k++)
        {
            if (comparer(a[k] , pivot1)==-1)
            {
                Swap(a, k, less++);
            }
            else if (comparer(a[k], pivot2) == 1)
            {
                while (k < great && comparer(a[great] , pivot2)==1)
                {
                    great--;
                }
                Swap(a, k, great--);
                if (comparer(a[k], pivot1) == -1)
                {
                    Swap(a, k, less++);
                }
            }
        }
        // Swaps
        var dist = great - less;
        if (dist < 13)
        {
            div++;
        }
        Swap(a, less - 1, left);
        Swap(a, great + 1, right);
        // subarrays
        DualPivotQuicksort(a, left, less - 2, div);
        DualPivotQuicksort(a, great + 2, right, div);

        // equal elements
        if (dist > len - 13 && comparer(pivot1,pivot2)!=0)
        {
            for (int k = less; k <= great; k++)
            {
                if (comparer(a[k] , pivot1)==0)
                {
                    Swap(a, k, less++);
                }
                else if (comparer(a[k], pivot2) == 0)
                {
                    Swap(a, k, great--);
                    if (comparer(a[k], pivot1) == 0)
                    {
                        Swap(a, k, less++);
                    }
                }
            }
        }
        // subarray
        if (comparer(pivot1 , pivot2)==-1)
        {
            DualPivotQuicksort(a, less, great, div);
        }
    }
}
朱染 2024-08-14 21:33:07

请参考下面的代码。它将自动计算距离,并且将根据最多生成的三个列表来计算距离。

using System;

namespace ShellSortExample
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] num = { 70, 30, 40, 10, 80, 20, 90, 110, 75, 60, 45, 199, 35, 2, 31, 22, 1, 11, 211, 100, 23, 55, 50 };
            WriteList("Unsorted List", num);
            int distance = 0;
            // Generate the distance as per the length of the list
            //It will consider maximum 3 list should generate
            for (int i = 1; i < num.Length; i++)
            {
                var div = num.Length / i;
                // Maximum 3 list should be generate
                if (div <= 3)
                {
                    distance = i;
                    break;
                }
            }
            // Do the grouping and Apply insertion sort
            for (int i = distance; i > 0; i--)
            {
                int tmpListVal = 0;
                for (int j = i; j < num.Length;)
                {
                    int tmpVal = num[j];
                    //Applying insertion sort with each shell/group element
                    for (int k = j - i; k >= 0; k -= i)
                    {
                        if (k <= num.Length - 1 && tmpVal < num[k])
                        {
                            num[k + i] = num[k];
                            num[k] = tmpVal;
                        }
                    }
                    if (j + i >= num.Length)
                    {
                        j = tmpListVal + 1;
                        tmpListVal = j;
                        if (j == i)
                        {
                            break;
                        }
                    }
                    else
                    {
                        j += i;
                    }
                }
            }
            WriteList("After applied shell sort",num);
        }
        static void WriteList(string msg, int[] list)
        {
            Console.WriteLine($"{msg} : ");
            for (int i = 0; i < list.Length; i++)
            {
                Console.Write($" {list[i]}");
            }
            Console.Write("\r\n");
        }
    }
}

Please refer below code. Automatically it will calculate the distance and distance will be calculated on consideration of maximum three lists would generate.

using System;

namespace ShellSortExample
{
    class Program
    {
        static void Main(string[] args)
        {
            int[] num = { 70, 30, 40, 10, 80, 20, 90, 110, 75, 60, 45, 199, 35, 2, 31, 22, 1, 11, 211, 100, 23, 55, 50 };
            WriteList("Unsorted List", num);
            int distance = 0;
            // Generate the distance as per the length of the list
            //It will consider maximum 3 list should generate
            for (int i = 1; i < num.Length; i++)
            {
                var div = num.Length / i;
                // Maximum 3 list should be generate
                if (div <= 3)
                {
                    distance = i;
                    break;
                }
            }
            // Do the grouping and Apply insertion sort
            for (int i = distance; i > 0; i--)
            {
                int tmpListVal = 0;
                for (int j = i; j < num.Length;)
                {
                    int tmpVal = num[j];
                    //Applying insertion sort with each shell/group element
                    for (int k = j - i; k >= 0; k -= i)
                    {
                        if (k <= num.Length - 1 && tmpVal < num[k])
                        {
                            num[k + i] = num[k];
                            num[k] = tmpVal;
                        }
                    }
                    if (j + i >= num.Length)
                    {
                        j = tmpListVal + 1;
                        tmpListVal = j;
                        if (j == i)
                        {
                            break;
                        }
                    }
                    else
                    {
                        j += i;
                    }
                }
            }
            WriteList("After applied shell sort",num);
        }
        static void WriteList(string msg, int[] list)
        {
            Console.WriteLine($"{msg} : ");
            for (int i = 0; i < list.Length; i++)
            {
                Console.Write($" {list[i]}");
            }
            Console.Write("\r\n");
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文