【问题标题】:What is the best algorithm for sorting 12 items对 12 个项目进行排序的最佳算法是什么
【发布时间】:2014-03-27 13:09:39
【问题描述】:

我需要对startIndexstartIndex + 12 之间的整数数组进行排序。此操作对我的表现至关重要。

你建议我使用什么算法?

现在我正在使用Bubble sort,但它的表现并不好......

更新:抱歉,缺少详细信息。我正在使用随机数组。我经常做这个动作,我正在使用 Java。

更新 2:我不确定 插入排序 是否是个好主意,因为我使用的是原生数组而不是 ArrayList。所以我需要自己实现插入,或者将它与气泡搜索结合起来。

【问题讨论】:

  • 您使用什么数据结构以及如何填充它?
  • 你使用什么语言和平台?
  • 当然冒泡是不好的,但对于 12 个项目来说,这无关紧要。提供更多上下文,您需要多久执行一次?在什么排序/大小的数组中?为什么?
  • 如何对 12 个项目进行排序至关重要?

标签: java performance algorithm sorting


【解决方案1】:

你可以试试这个排序网络:

http://jgamble.ripco.net/cgi-bin/nw.cgi?inputs=12&algorithm=best&output=svg

这是 12 个项目的最佳配置。这意味着,最小化操作的必要比较器和深度(如果可以并行工作,则为连续步骤)。

要利用并行化,请使用 SIMD (SEE) 指令。我不知道如何用 Java 做到这一点。

【讨论】:

  • @Ilya_Gazman 无论你在哪里看到带有输入 i 和 j 的比较器,你都会编写一行代码,例如 if (a[i] > a[j]) { int t = a[i]; a[i] = a[j]; a[j] = t; }
  • 基本上您会看到需要比较哪些元素(并根据值进行交换)。您还可以看到它分几个步骤工作。所以完成第一行,然后继续第二行。现在,如果您可以一次执行其中几个(理论上可以同时完成一行中的所有比较/交换),您可以获得巨大的速度提升。为此,您需要使用向量计算,即 CPU 上的特定指令(现代 CPU 的 SSE 或 AVX)。
  • @DavidEisenstat 所以基本上这些是我需要检查和交换的数组索引对(如果需要),并且每一行可以并行运行?
【解决方案2】:

对于 12 个项目,可能是插入排序。它通常具有 O(n^2) 排序算法的最佳经验性能。 O(n log n) 算法对于这么小的集合来说可能是多余的,算法的复杂性通常意味着它们在你排序的集合更大之前不会得到回报。

当然,如果你真的想挤出最后一滴性能,你可能需要编写一些程序集并在寄存器或其他东西中完成。

如果您知道它们是有界的,那么基数排序也可能是一个好方法。

【讨论】:

    【解决方案3】:

    我也会尝试硬编码合并排序。 首先使用硬代码(无索引变量)对每个连续的 3 组进行排序。 这是 3 次比较乘以 4 = 12。

    然后合并前两组 3 个。 这是 3 次比较乘以 2 = 6。

    然后合并两组 6。 这是 6 次比较。

    总共 24 次比较(和数据移动)。

    可能比 72 次比较和可能的交换要快。

    我会在汇编程序中单步执行,看看是否有任何指令没有发挥作用。

    【讨论】:

    • 实际上有一个本地植入:Arrays.sort()。它似乎有点色拉。我刚刚完成了@ypnos 建议的实施,而且速度更快。
    • @Ilya:排序网络可能是最快的,但我敢打赌,硬编码方法的性能会比 Arrays.sort() 好一点,只是避免了方法调用等。
    • 呵呵其实这正是我想要做的。我马上分享我的来源。
    【解决方案4】:

    QuickSort 和 MergeSort 对于如此小的数组大小无效。 我会使用我测得比气泡排序更快的插入排序。

    private static void insertionSort(int[] intArray) {
        for(int i=1; i<intArray.length; i++){
            int temp = intArray[i];
            int j;
            for(j=i-1; j>=0 && temp<intArray[j]; j--){
                intArray[j+1]=intArray[j];
            }
            intArray[j+1] = temp;
        }
    }
    

    【讨论】:

      【解决方案5】:

      我实现了小测试,一次使用Arrays.sort,一次使用基于@ypnos 在https://stackoverflow.com/a/22688819 中引用的排序网络的自己的排序实现。

      但是,不要把这个比较看得太严肃。这不是一个非常复杂的微基准,当然还有很多影响因素还没有考虑。我想到的一个是:12 个元素的段是线性排序的吗?也就是说,您是先对元素进行排序 [0,12),然后是 [12,24),依此类推,还是这些段分散在数组中?由于缓存,这可能会对性能产生影响。这种影响可能对所有方法都相同,但仍应予以考虑。

      无论如何,使用这种排序网络(或一般的“展开”排序方法)似乎可以挤出一点性能。

      但只是为了比较,我添加了一种并行方法,其中对 12 元素段集进行排序的任务分布在所有可用内核中,并且似乎可以通过这种方式实现显着的加速。因此,一般来说,您可能应该考虑对该任务进行某种并行化。

      (以-Xmx2000m 开头,以便为大型数组提供足够的内存)

      import java.util.ArrayList;
      import java.util.Arrays;
      import java.util.List;
      import java.util.Random;
      import java.util.concurrent.Callable;
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      import java.util.concurrent.TimeUnit;
      
      public class SmallSortTest
      {
          public static void main(String[] args)
          {
              Random random = new Random(0);
              for (int size=8000000; size<=8000000*10; size+=8000000)
              {
                  int array[] = createRandomArray(size, 0, 1000, random);
      
                  int array0[] = array.clone();
                  testArrays(array0);
      
                  int array1[] = array.clone();
                  testOwn(array1);
      
                  int array2[] = array.clone();
                  testParallel(array2);
      
                  if (!Arrays.equals(array0, array1)) System.out.println("Error");
                  if (!Arrays.equals(array0, array2)) System.out.println("Error");
              }
          }
      
          private static void testArrays(int array[])
          {
              long before = System.nanoTime();
              for (int i=0; i<array.length/12; i++)
              {
                  Arrays.sort(array, i*12, i*12+12);
              }
              long after = System.nanoTime();
      
              System.out.println(
                  "Arrays   size "+array.length+
                  " duration "+(after-before)*1e-6+
                  ", some result "+array[array.length/2]);
          }
      
          private static void testOwn(int array[])
          {
              long before = System.nanoTime();
              for (int i=0; i<array.length/12; i++)
              {
                  sort(array, i*12);
              }
              long after = System.nanoTime();
              System.out.println(
                  "Own      size "+array.length+
                  " duration "+(after-before)*1e-6+
                  ", some result "+array[array.length/2]);
          }
      
          private static void testParallel(final int array[])
          {
              int n = Runtime.getRuntime().availableProcessors();
              ExecutorService executor = Executors.newFixedThreadPool(n);
      
              int batchSize = (int)Math.ceil((double)array.length / 12  / n);
              final List<Callable<Object>> tasks = new ArrayList<Callable<Object>>();
              for (int i=0; i<n; i++)
              {
                  final int minIndex = (i+0)*batchSize;
                  final int maxIndex = Math.min(array.length, (i+1)*batchSize);
                  Runnable runnable = new Runnable()
                  {
                      @Override
                      public void run()
                      {
                          for (int i=minIndex; i<maxIndex; i++)
                          {
                              Arrays.sort(array, i*12, i*12+12);
                          }
                      }
                  };
                  tasks.add(Executors.callable(runnable));
              }
      
              long before = System.nanoTime();
              try
              {
                  executor.invokeAll(tasks);
              }
              catch (InterruptedException e1)
              {
                  Thread.currentThread().interrupt();
              }        
              long after = System.nanoTime();
      
              executor.shutdown();
              try
              {
                  executor.awaitTermination(5, TimeUnit.SECONDS);
              }
              catch (InterruptedException e)
              {
                  Thread.currentThread().interrupt();
              }
      
              System.out.println(
                  "Parallel size "+array.length+
                  " duration "+(after-before)*1e-6+
                  ", some result "+array[array.length/2]);
          }
      
          private static void sort(int array[], int startIndex)
          {
              int i0 = startIndex+11;
              int i1 = startIndex+10;
              int i2 = startIndex+9;
              int i3 = startIndex+8;
              int i4 = startIndex+7;
              int i5 = startIndex+6;
              int i6 = startIndex+5;
              int i7 = startIndex+4;
              int i8 = startIndex+3;
              int i9 = startIndex+2;
              int i10 = startIndex+1;
              int i11 = startIndex+0;
      
              if (array[i0] < array[i1]) swap(array, i0, i1);
              if (array[i2] < array[i3]) swap(array, i2, i3);
              if (array[i4] < array[i5]) swap(array, i4, i5);
              if (array[i6] < array[i7]) swap(array, i6, i7);
              if (array[i8] < array[i9]) swap(array, i8, i9);
              if (array[i10] < array[i11]) swap(array, i10, i11);
      
              if (array[i1] < array[i3]) swap(array, i1, i3);
              if (array[i5] < array[i7]) swap(array, i5, i7);
              if (array[i9] < array[i11]) swap(array, i9, i11);
              if (array[i0] < array[i2]) swap(array, i0, i2);
              if (array[i4] < array[i6]) swap(array, i4, i6);
              if (array[i8] < array[i10]) swap(array, i8, i10);
      
              if (array[i1] < array[i2]) swap(array, i1, i2);
              if (array[i5] < array[i6]) swap(array, i5, i6);
              if (array[i9] < array[i10]) swap(array, i9, i10);
              if (array[i0] < array[i4]) swap(array, i0, i4);
              if (array[i7] < array[i11]) swap(array, i7, i11);
      
              if (array[i1] < array[i5]) swap(array, i1, i5);
              if (array[i6] < array[i10]) swap(array, i6, i10);
              if (array[i3] < array[i7]) swap(array, i3, i7);
              if (array[i4] < array[i8]) swap(array, i4, i8);
      
              if (array[i5] < array[i9]) swap(array, i5, i9);
              if (array[i2] < array[i6]) swap(array, i2, i6);
              if (array[i0] < array[i4]) swap(array, i0, i4);
              if (array[i7] < array[i11]) swap(array, i7, i11);
              if (array[i3] < array[i8]) swap(array, i3, i8);
      
              if (array[i1] < array[i5]) swap(array, i1, i5);
              if (array[i6] < array[i10]) swap(array, i6, i10);
              if (array[i2] < array[i3]) swap(array, i2, i3);
              if (array[i8] < array[i9]) swap(array, i8, i9);
      
              if (array[i1] < array[i4]) swap(array, i1, i4);
              if (array[i7] < array[i10]) swap(array, i7, i10);
              if (array[i3] < array[i5]) swap(array, i3, i5);
              if (array[i6] < array[i8]) swap(array, i6, i8);
      
              if (array[i2] < array[i4]) swap(array, i2, i4);
              if (array[i7] < array[i9]) swap(array, i7, i9);
              if (array[i5] < array[i6]) swap(array, i5, i6);
      
              if (array[i3] < array[i4]) swap(array, i3, i4);
              if (array[i7] < array[i8]) swap(array, i7, i8);
          }
      
          private static void swap(int array[], int i0, int i1)
          {
              int temp = array[i0];
              array[i0] = array[i1];
              array[i1] = temp;
          }
      
      
          private static int[] createRandomArray(int size, int min, int max, Random random)
          {
              int array[] = new int[size];
              for (int i=0; i<size; i++)
              {
                  array[i] = min+random.nextInt(max-min);
              }
              return array;
          }
      }
      

      【讨论】:

      • 要恢复排序顺序,您可以镜像所有索引。 i0 = startIndex+11; i1 = startIndex10; ... i11 = startIndex + 0;。网络本身,包括比较,保持不变。我不是 100% 确定,但非常确定!回来报告:-)
      • @ypons 我想我试过这个,并将比较从&lt; 更改为&gt;,以及两者的组合,认为这些都不是达到了预期的效果,但也许此时还有其他问题。我将不得不再次测试它。
      • 是的,不要改变比较!可以这样想:网络排序到存储桶 1、2、3.. 12。然后您反转数组,使存储桶 1 成为新存储桶 12,存储桶 2 成为新存储桶 11,等等。而是让网络在存储桶中排序12, 11, 10 .. 1. 怎么做?让网络认为 1 == 12, 2 == 11, 3 == 10, 12 == 1。这是在运行网络之前更改索引时所做的。
      • @ypons 确实,我又试了一次,现在它起作用了(相应地编辑了答案)。我绝对确定我已经尝试过了。我什至在答案中说〜*“......这不是通过改变一些指数来完成......”*。但是我第一次尝试的时候好像还有其他问题。
      猜你喜欢
      • 1970-01-01
      • 2011-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-07
      • 2010-12-16
      相关资源
      最近更新 更多