排序算法在一次oranother中大量用于软件开发。根据语言,您可能在您的处置方便的某些排序功能,但根据用例,知道在引擎盖下正在应用哪种排序算法可以是测量工作软件对变化的影响的关键。
在本文中,我想探索一些排序的实现,但我想通过单位测试驱动。我在网上没有看到过多的探索这种方法,所以我希望这引起你的兴趣。
大多数排序算法定义我将从维基百科提取,所以这里是一个摘要我们的摘要:
排序算法是一种算法,将列表中列表的元素放在特定顺序中。
这里的想法是实现一个或两个分类算法,但是这样做的是驾驶的测试。基本上,我们将首先编写一个测试,具有简单的场景输入和预期输出。这将失败,因为尚未存在任何码。然后我们将写入最低必要的代码来满足最低的并重复该过程。此技术的名称是测试驱动驱动(TDD)。如果您对此过程感到好奇,或者如果它没有声音,请访问这篇文章。
我将使用的语言将是通过测试和实现为简单的单个文件。
如果您不熟悉Go语言,请不要担心。我将发布差异的代码,以说明我们在一致上下滚动的每一步。这里的想法是探索该技术,而不是语法或特点。
您可能会注意到,我将使用该术语数组,尽管在其中阵列和计算机之间的区别,技术上,我将在代码期间usingslics。
我也会尝试描述每一步,所以假装我们正在做一些队伍
好的。正如我如下所述,我想从一个简单的测试开始,将我加入最少量的代码只是为了让事情工作,然后我们加拿到它。就哪种算法我打算选择,我会和泡泡排序一起。然后,我们评估ifthis对我们来说足够好,如果没有,我们重构我们的代码可以选择offereMplementation。这里重要的是:最后,无论我们选择的脚本算法如何,测试需要通过,因为我们仍然想要对我们进行排序。
此外,为简单起见,我们的排序算法只会将要处理数字(整数以更精确)。
让我们从最简单的输入开始,这将是一个空数组。为什么?因为如果我们想要排序空阵列,结果应该是一个空数组!我想遵循的方法是让事情变得最小的动作,因为我们需要持续反馈。我们可以从一个元素开始,写一个元素并编写一个测试,它会指出该阵列进行排序,但这会要求我们花费更多时间写入代码,更少时间写入测试。我可以获得一些平衡,并确保我们不会留下任何边缘案例。
包排序_test导入("测试"" github.com/matryer/is")func testsort(t * testing .t){t。 run("当数组为空&#34时应返回相同的值;,func(t * testing .t){是:=是。新(t)元素:= [0] int {}预期:= [0 ] int {}是。相等(排序(元素),预期)})}
去测试./2021/06/08 14:06:17退出状态2#alabeduarte.com_test [alabeduarte.com.test] ./ sorting_test.go:15:12:未定义:sortfail alabeduarte.com [build失败] failfail (0.22秒)
这是因为没有在任何地方定义排序方法。让我们做出一定的努力来获得这个传球,而不是通过定义方法并使iTRETURN成为一个空数组,因此我们的测试将通过!
现在让我们添加另一个测试场景,在那里它将强迫我们写的东西,而不是硬涂覆的响应:
//测试文件func testsort(t * testing .t){//先前的测试场景在这里省略... t.t。运行("当阵列只有一个元素&#34时,应该返回相同的值;,func(t * testing .t){是:=是。新(t)元素:= [] int {1}预期:= [] int {1}是。相等(排序(元素),预期)})}
去测试./2021/06/08 14:04:49退出状态1 sorting_test.go:29:29:[]!= [1] ---失败:testsort(0.00s)---失败:testsort / contrenturn_same_value_when_array_has_only_one_element( 0.00s)failfail alabeduarte.com 0.101sfailfail(0.31秒)
这是预期的,因为我们的实现代码始终返回EntervartAray。让我们改变它并进行测试通行证,但以一种方式要求最小的努力实现这一点:
现在我们的两种情况正在通过,我们到目前为止评估我们的代码......
好的,看着代码,我无法想到改善,除了我们的测试目前有点冗长。我们在我们的测试中,我们正在定义variables元素和期望,然后我们正在执行以下传票:
考虑到现在的测试是多么简单,我觉得我们可以做点什么,所以让我们重构它:
//测试文件func testsort(t * testing .t){t。运行("当数组为空&#34时应返回相同的值;,func(t * testing .t){是:=是。新(t)是。相等(sort([] int {}),[ ] int {})})t。运行("当阵列只有一个元素&#34时,应返回相同的值;,func(t * testing .t){是:=是。新(t)是。相等(sort([] int {1} ),[] int {1})})}}
现在,运行测试,他们仍然应该通过,因为我们没有新的东西:
好的。现在一切都是“绿色”(A.K.A通过),让我们添加一个新的场景,实际上需要我们应用任何类型的算法。但是,让我们添加真正简单的东西,如2号:
//测试文件func testsort(t * testing .t){// ... t。运行("应该返回最低元素,后跟最大的元素" func(t * testing .t){是:=是。新(t)是。相等(sort([] int {2, 1}),[] int {1,2})})}
去测试./2021/06/08 14:21:11退出状态1 sorting_test.go:31:[2 1]!= [1 2] ---失败:testsort(0.00s)---失败:testsort / contance_return_the_lowest_element_followed_by_the_largest_element(0.00s)failfail alabeduarte.com 0.098sfailfail(0.34秒)
//实现文件func排序(元素[] int)[] int {如果len(元素)< = 1 {return元素} return [] int {元素[1],元素[0]}}
那太棒了!测试都是通过的。你可能想知道...... Outimplementation听起来很可靠吗?一些事情可以疯了......也是如此,感觉就像我们欺骗一样,因为我们总是抓住流动和第二个元素并将它们恢复到相反的顺序中,有诺斯特继续下去,诚实。让我们打破代码并进一步讨论我们下一步可以做的事情:
//实现文件Func排序(元素[] int)[] int {//在此检查长度是否小于或等于1 //,这意味着如果数组为空,则会返回元素//本身(一个空数组),如果数组只有一个元素,它将//也会返回。如果LEN(元素)< = 1 {return元素} //在这种情况下,我们'重新抓住第二个elewht和第一个元素和//交换两个return [] int {元素[1]的顺序,元素[0]}}
基本上,如果数组有超过2个元素,则我们的代码只能以相反的顺序返回第二次返回。所以这里有一些可能出现问题的几件算法:
当元素的长度大于2时,我们的排序算法将归于休息,返回具有更少元素的数组
当元素已经排序时,我们的排序算法将通过启动前两个元素
很明显,我们的代码正在做错事。但是因为它是Itmight Sound,如果我们有一个测试索引,我们只能将新代码添加到我们的实施中,这将证明其存在证明!
//测试文件func testsort(t * testing .t){// ... t。运行("应该返回一个与作为输入&#34提供相同长度相同的数组; func(t * testing .t){是:=是。新(t)sortedelements:= sort([] INT {2,1,4,3})ACTUALL长度:= LEN(SortEdElements)是。相等(Actuallength,4)})}
去测试./2021/06/08 14:40:57退出状态1 sorting_test.go:44:2!= 4 ---失败:testsort(0.00s)---失败:testsort / consucturn_an_array_with_the_same_length_as_the_one_provide_as_an_input(0.00s) failfail alabeduarte.com 0.101sfailfail(0.34秒)
//实现文件func排序(元素[] int)[] int {如果len(元素)< = 1 {return元素} //从索引2上取向rest:=元素[2:] //附加休息到原始数组我们有返回附加([] int {元素[1],元素[0]},休息......)}
由于我们确保阵列长度始终相同,而是我们符合其他测试场景的符号。但是,现在的实现有点笨拙,这是一个标志,是时候实际实现了脚的时间!
让我们添加一个简单的场景,以说明我们可以在没有笨重的实现的情况下对云兰2个元素进行排序。所以让我们使用3个ElementSthis时间:
//测试文件func testsort(t * testing .t){// ... t。运行("应该将所有元素从最低到最大的元素排序,Func(t * testing .t){是:=是。新(t)是。相等(sort([] int {2 ,3,1}),[] int {1,2,3})})})
去测试./2021/06/08 14:56:24退出状态1 sorting_test.go:51:[3 2 1]!= [1 2 3] ---失败:testsort(0.00s)---失败:testsort / consuct_all_all_the_elements_from_the_lowest_to_the_larget(0.00s)FailFail Alabeduarte.com 0.099SFailfail(0.40秒)
现在让我们在这里实现一些排序算法。目前,让我们使用称为泡沫排序的算法。
如上所述,泡沫排序是最简单的排序算法之一Toundstand和实施,但其效率在较大的赌注上显着降低。这里有更多细节。
如果您想了解更多关于此算法的实现,Ialso建议此材料:https://tutorialedde.net/courses/go-algorithms-course/21-bubble-sort-in-go/
//实现文件Func排序(元素[] int)[] int {n:= len(元素)如果n< = 1 {return元素}交换:= true for swappped {swappped = false i:= 0;我< n - 1; i ++ {如果元素[i]>元素[i + 1] {元素[i],元素[i + 1] =元素[i + 1],元素[i]交换= true}}} return元素}
我们的算法(泡沫排序)不是那里最有效的。算法的复杂性是O(n²),其中n是被排序的元素数量。这意味着随着元素的数量增长,其效率降低。在这里,我们可以在此处使用的其他选项,例如被视为Myeffirity的Insertionsort或Selectionsor。
不一定改变任何内容,让我们使用GO测试基准测试我们的CurrentalGorithm进行基准测试。
首先,让我们创建一个小功能(在我们的测试文件中),以生成带有给定长度的随机元素:
//测试文件func生成dayerandeLement(n int)[] int {// inseardise and slice,其长度和容量的" n"元素:= make([] int,n,n)//填充与i的随机元素的切片,_:=范围元素{元素[i] = rand。 int()}返回元素}
现在让我们创建一个函数,它将迭代我们的方法排序,将tests.b作为参数:
//测试文件Func BenchmarkBubblesort(n int,b * testing .b){为i:= 0;我< b .n; i ++ {元素:=生成地段(n)//排序元素排序(元素)}
//测试文件func benchcharkbubblesort3(b * testing .b){benchmarkbbblesort(3,b)} func benchmarkbbollesort10(b * testing .b){benchmarkbubblesort(10,b)} func benchmarkbbollesort20(b * testing .b){benchmarkbbblesort( 20,b)} Func BenchmarkBblesort50(B *测试.b){BenchmarkBubblesort(50,B)} Func BenchmarkBbollesort100(b * testing .b){benchmarkbubblesort(100,b)} func benchmarkbbollesort1000(b * testing .b){benchmarkbbblesort (1_000,b)} Func BenchmarkBubblesort100000(b *测试.b){benchmarkbbblesort(100_000,b)}
Go test -bench = .goos:达尔文司机:amd64pkg:alabeduarte.comcpu:英特尔(r)核心(tm)i7-9750h cpu @ 2.60ghzbenchmarkbubbollesort3-12 17688249 67.22 ns / opbenchmarkbubbollesort10-12 4173184 303.4 ns / opbenchmarkbubbollesort20-12 1228124 1031 ns / OpbenchMarkBubblesort50-12 240789 4240 NS / OPBenchmarkBubblesort100-12 90360 13976 NS / OpbenchmarkBubbollsort1000-12 1626 733388 NS / OpbenchmarkBubblesorT100000-12 1 14456783580 NS / OP
正如您所看到的,当阵列中有100,000个元素时,我的机器占据了14456783580纳秒,以执行排序,相当于约14秒。
让我们尝试使用Go的StanderalLibrary实现来对我们的元素进行排序.Let的创建一个小功能,将生成随机元素并从包中调用TheThod Ints:
Func BenchmarkGosort(n int,b * testing .b){为i:= 0;我< b .n; i ++ {元素:=生成式andomeLements(n)//排序元素排序。 INTS(元素)}}
现在让我们创建类似的基准函数,这些功能将与Sort.ints函数进行比较:
Func BenchmarkGoosort3(B *测试.b){BenchmarkGoSort(3,B)} Func BenchmarkGosort10(B *测试.b){BenchmarkGoSort(10,B)} Func BenchmarkGoSort20(B *测试.b){benchmarkgosort(20,b) Func BenchmarkGosort50(B *测试.b){BenchmarkGosort(50,B)} Func BenchmarkGoSort100(B *测试.b){BenchmarkGoSort(100,B)} Func BenchmarkGosort1000(B * Testing .b){benchmarkgosort(1_000,b )} Func BenchmarkGosort100000(b *测试.b){benchmarkgosort(100_000,b)}
Go test -bench = .goos:DarwingoArch:Amd64PKG:alabeduarte.comcpu:英特尔(r)核心(tm)i7-9750h cpu @ 2.60ghzbenchmarkbubblesort3-12 18284776 67.53 ns / opbenchmarkbubblesort10-12 4207515 295.9 ns / opbenchmarkbubbollesort20-12 1201567 1006 ns / opBenchmarkBubbleSort50-12 276207 4262纳秒/ opBenchmarkBubbleSort100-12 89107 13813纳秒/ opBenchmarkBubbleSort1000-12 1620 755969纳秒/ opBenchmarkBubbleSort100000-12 1 14532398965纳秒/ opBenchmarkGoSort3-12 9781417 124.5纳秒/ opBenchmarkGoSort10-12 2595421 439.4纳秒/ opBenchmarkGoSort20-12百万1108纳秒/ OpbenchmarkGosort50-12 357010 3252 NS / OPBenchmarkGosort100-12 160202 7618 NS / OpbenchmarkGosort1000-12 10000 100856 NS / OpbenchmarkGosort100000-12 75 16205598 NS / Op
正如我们所看到的,我们的算法使用泡沫排序似乎稍微好一点,直到2017010比赛,其中Go标准库开始闪耀比我们的方式更快。
由于Go标准库更有效,让我们更改我们的实现,而是重新运行测试:
去测试-v === RUN TestSort === RUN TestSort / should_return_same_value_when_array_is_empty === RUN TestSort / should_return_same_value_when_array_has_only_one_element === RUN TestSort / should_return_the_lowest_element_followed_by_the_largest_element === RUN TestSort / should_return_an_array_with_the_same_length_as_the_one_provided_as_an_input === RUN TestSort / should_sort_all_the_elements_from_the_lowest_to_the_largest --- PASS:TestSort (0.00S)--- PASS:TestSort / should_return_same_value_when_array_is_empty(0.00S)--- PASS:TestSort / should_return_same_value_when_array_has_only_one_element(0.00S)--- PASS:TestSort / should_return_the_lowest_element_followed_by_the_largest_element(0.00S)--- PASS:TestSort / should_return_an_array_with_the_same_length_as_the_one_provided_as_an_input(0.00 s)---通过:testsort / consut_all_all_the_elements_from_the_lowest_to_the_larget(0.00s)passok alabeduarte.com 0.920s
如果您有兴趣查看我们构建的整个代码,请在下面的CONSLAPSE THECTION:
包裹排序_test导入(" math / rand"" sort""测试"" github.com/matryer/is")//实现:/ //排序将接收切片作为输入,它将返回另一个切片但是//排序。 Func排序(元素[] int)[] int {n:= len(元素)如果n< = 1 {return元素}交换:= true for swappped {swapppy = false for i:= 0;我< n - 1; i ++ {如果元素[i]>元素[i + 1] {元素[i],元素[i + 1] =元素[i + 1],元素[i]交换= true}}返回元素} //单元测试:func testsort(t *测试.t){t。运行("当数组为空&#34时应返回相同的值;,func(t * testing .t){是:=是。新(t)是。相等(sort([] int {}),[ ] int {})})t。运行("当阵列只有一个元素&#34时,应返回相同的值;,func(t * testing .t){是:=是。新(t)是。相等(sort([] int {1} ),[] int {1})})t。运行("应该返回最低元素,后跟最大的元素" func(t * testing .t){是:=是。新(t)是。相等(sort([] int {2, 1}),[] int {1,2})})t。运行("应该返回一个与作为输入&#34提供相同长度相同的数组; func(t * testing .t){是:=是。新(t)sortedelements:= sort([] INT {2,1,4,3})ACTUALL长度:= LEN(SortEdElements)是。等于(Actuall长度,4)})t。运行("应该将所有元素从最低到最大的元素排序,Func(t * testing .t){是:=是。新(t)是。相等(sort([] int {2 ,3,1}),[] int {1,2,3})})} //基准:Func生成DaySandeMelements(n int)[] int {//初始化具有&#34的长度和容量的切片; n& #34;元素:= make([] int,n,n)//填充与i的随机元素的切片,_:=范围元素{元素[i] = rand。 int()}返回元素} Func BenchmarkBubbleort(n int,b * testing .b){为i:= 0;我< b .n; i ++ {元素:=生成式aragemelements(n)//排序元素排序(元素)}} func benchmarkgosort(n int,b * testing .b){为i:= 0;我< b .n; i ++ {元素:=生成式andomeLements(n)//排序元素排序。 ints(元素)}} func benchmarkbbollesort3(b * testing .b){benchmarkbbblesort(3,b)} func benchmarkbbolsort10(b * testing .b){benchmarkbubblesort(10,b)} func benchmarkbbollesort20(b * testing .b){ BenchmarkBublesort(20,B)} Func BenchmarkBubblesort50(B *测试.b){BenchmarkBubblesort(50,B)} Func BenchmarkBbollesort100(b * Testing .b){BenchmarkBubblesort(100,B)} Func BenchmarkBubblesort1000(B *测试.b) {BenchmarkBblesort(1_000,B)} Func BenchmarkBubblesorT100000(B *测试.b){BenchmarkBubblesort(100_000,B)} Func BenchmarkGoSort3(B * Testing.B){BenchmarkGoSort(3,B)} Func BenchmarkGosort10(B *测试.b ){BenchmarkGosort(10,B)} Func BenchmarkGoSort20(B *测试.b){BenchmarkGosort(20,B)} Func BenchmarkGosort50(B *测试.b){BenchmarkGosort(50,B)} Func BenchmarkGosort100(B *测试。 b){benchmarkgosort(100,b) Func BenchmarkGoosort1000(b *测试.b){benchmarkgosort(1_000,b)} Func BenchmarkGosort100000(b *测试.b){benchmarkgosort(100_000,b)}
如果你阅读这篇文章并走了这篇文章,非常感谢你。我希望我可以说明如何开发一个排序算法驱动的字母。
你可能已经注意到我对拿起最富有的算法实施没有太担心,开始,但相反,我感兴趣
......