美国国旗排序


要了解这种“多波段”分类的操作原理,以带有三个条纹的标志为例比较容易上手。 并且为了轻松处理三色标志,最好先了解一下它在二色示例中的工作方式。 并处理两种颜色...
EDISON软件-网络开发
本文是在EDISON的支持下编写的。

我们从事软件的移植和迁移 ,以及Android和iOS移动应用程序开发

我们喜欢算法理论! ;-)

两色旗



首先,考虑排序数组中的数字仅以两位分配的情况。 为了简单起见,我们假设我们有一个零(低阶)和一个(高阶)数组。

因此,我们只有两个“带”:一个将移动最低有效位(零),而另一个将移动最高位(单位)。 任何两个颜色的标志都将进行演示。 例如,乌克兰的国旗。



这是怎么回事 由于在第一阶段尚不知道有多少个零和我们有多少个单位,因此不清楚每个“带”的最终大小。 因此,我们在数组的键上放置了两个指针。 对于低阶,指针设置为数组的开头,对于高阶,指针设置为结尾。 然后,我们从左到右单次遍历数组,并查看每个位元素。

如果在传递过程中某个元素等于最高顺序,则第二个指针会告诉我们该元素的传递位置(我们进行交换)。 插入下一个元素的指针向左移动,高级数字的“条”已扩展。

如果它等于最低有效位,则将其指针向右移动一个元素。 由于我们只有两位数,因此不必转移元素,因为它已经存在。 较年轻类别的“条带”自然变宽了。

结果,两个指向彼此的指针将在一个点上碰撞,并且放电将在其“带”中排序。 同时,您将不需要遍历整个数组-当指针在中间的某个地方相遇时,算法将完成其工作。

荷兰国旗问题::荷兰国旗问题



我们使任务稍微复杂化,不考虑两位数,而是考虑三位数字。 让我们让数组的元素属于最低(零),中间(单位)和高级(两个)数字。

为了演示,我们以法国, 俄罗斯 卢森堡,荷兰的石勒苏益格-戈尔德斯坦的三向红白蓝旗标为准 。 为什么是荷兰的国旗呢? 因为Edsger Dijkstra在有关该标志示例的演讲中检查了相应的算法,该算法被称为“荷兰国旗的任务”。



如您所见,没有什么特别新的东西。 每个类别都有其自己的指针。 最初,初中和中级的标签占据数组开头的起始位置,并在遇到相应元素时向右移动。 高阶指针首先在数组的末尾,并向左移动。

实际上,通过数组也将是不完整的,如果位或多或少均匀地分布,则该算法将通过数组的2/3,然后将所有元素散布在其“带”中。

美国国旗分类::美国国旗分类



现在,在我们的解释中,我们可以继续使用多频段美国国旗。

当我们没有两个,不是三个,而是任意数量的数字时,我们确定每个数字应从何处开始(其“带”),并在其“带”中重新绘制元素。

在此算法中,数字通常不视为小数,而是以不同的数字表示,通常是2的幂。 通常,数字256被用作位深度的基础(比128小一些),这使您可以有效地进行排序以排列字符串。 对于位深度的数字,较小的2 n (2、4、16等)更方便,它允许您通过按位移位进行比较,而不是在比较十进制数字时提高幂。

动画显示了一个基数为16的位深度的示例:



  1. 在第一遍-搜索最大值,以便确定阵列中元素之间的最大位数(以便从元素中正确提取从帐户中确定的位数)。
  2. 然后发生递归处理。 调用时,将指示子数组和当前处理位的边界。 第一次调用时,整个数组是一个子数组;左侧的第一位被占用。
  3. 在子数组的元素中,将执行初始计算-当前类别中每个数字出现多少次。 通过此计数,您可以确定这些数字位数的位置(即,已知要将特定数字中具有下一个数字的元素移至“带”的范围和位置)。 实际上,本地化是指向“带”的指针,在这些带中需要移动相应的元素。
  4. 根据本地化指针,将在现场进行交换-类别中的数字显示您要向其发送元素的位置,与之发生交换的另一个元素将发生位置。 该子句将一直执行,直到在下一次交换中不存在从另一个位置到达的元素为止(然后您可以移至子数组的下一个元素,并类似地对其执行此子句)。
  5. 作为交换的结果,元素通过下一个数字中的数字重新分配到块中,然后进行递归-将相同的算法作为子数组应用于每个块,下一个作为当前数字。

在关于对具有近似分布的排序进行计数的文章中有一种视觉上非常相似的算法- 近似排序 。 在那里,我们计算了数组中每个数字出现了多少次-并根据获得的位置重新分配了元素。 在这里,我们考虑子数组元素中类别中的每个数字出现多少次-并根据获得的位置重新分配子数组中的元素。 如果近似值是一种计数排序,则“ American”是计数位排序。

美国国旗排序-Python实现
#           def get_radix_val(x, digit, radix) -> int: return int(floor(x / radix**digit)) % radix #             def compute_offsets(a_list, start: int, end: int, digit, radix) -> list: #          counts = [0 for _ in range(radix)] for i in range(start, end): #        #         val = get_radix_val(a_list[i], digit, radix) counts[val] += 1 #       #          offsets = [0 for _ in range(radix)] sum = 0 #         for i in range(radix): offsets[i] = sum sum += counts[i] return offsets #      def swap(a_list, offsets, start: int, end: int, digit, radix) -> None: i = start #          next_free = copy(offsets) #        #   (       ) cur_block = 0 while cur_block < radix-1: # if i >= start + offsets[cur_block+1]: cur_block += 1 continue radix_val = get_radix_val(a_list[i], digit, radix) if radix_val == cur_block: i += 1 continue swap_to = next_free[radix_val] a_list[i], a_list[swap_to] = a_list[swap_to], a_list[i] next_free[radix_val] += 1 #   def american_flag_sort_helper(a_list, start: int, end: int, digit, radix) -> None: #          offsets = compute_offsets(a_list, start, end, digit, radix) #      swap(a_list, offsets, start, end, digit, radix) if digit == 0: #     ? return #   #       for i in range(len(offsets)-1): #      #         american_flag_sort_helper(a_list, offsets[i], offsets[i+1], digit-1, radix) #   def american_flag_sort(a_list, radix) -> None: #,         for x in a_list: assert(type(x) == int) #    max_val = max(a_list) #    (  ) max_digit = int(floor(log(max_val, radix))) #   -     american_flag_sort_helper(a_list, 0, len(a_list), max_digit, radix) 

斯卡排序::斯卡排序


德国程序员Malte Skarupke宣布,他已经开发出一种新的排序算法,该算法是一种经过彻底改进的“美国国旗”,其性能优于std :: sort平均2倍(std :: sort-这种算法也称为自省排序) - 快速排序按堆 排序的混合)。

  1. 递归地对数组进行排序,在递归的第一级,将整个数组作为子数组。
  2. 如果子数组少于128个元素,则为其调用std :: sort。
  3. 如果子数组包含128到1024个元素,则将对其进行美国国旗排序。
  4. 如果子数组中有超过1024个元素,则需要进行ska排序。
  5. 另外,为避免最坏的情况,如果递归嵌套太大(超过16个级别),即使子数组中有128个以上的元素,该算法也会切换到std :: sort

显然,它是一种非常有效的算法,同时又非常复杂-其作者的实现几乎需要一行5,000行。 也许有一天我们会考虑进行这种排序,现在我们不再赘述。 有兴趣的人可以点击下面的链接。

参考文献


荷兰国旗问题美国国旗排序

ska排序: 我编写了一种更快的排序算法( 第1 部分第2部分 Github代码

系列文章:


在AlgoLab Excel应用程序中,出现了按两种颜色的标记(将零和一排序),按三种颜色的标记(将零,一和二进位排序),按美国国旗进行排序的排序。 要对“美国国旗”进行排序,可以(在带有算法名称的单元格中的注释中)指定要分配的数字系统-默认值为十六进制。

Source: https://habr.com/ru/post/zh-CN481304/


All Articles