學習軌道
/
Julia
Julia
/
練習
/
排序試煉
排序試煉

排序試煉

學習練習

簡介

如同大多數程式語言,Julia 提供了豐富多樣的集合型別。

有些集合天生就有順序(包括Vectors和更高維度的Arrays),因此我們可以依位置存取元素。 其他集合則是無序的(Sets、Dicts),元素必須透過鍵或值來存取。

最簡單的情況是,假設我們有一個由Ints組成的Vector,並想將它依遞增順序排序。

julia> v = [3, 2, 4, 1]
4-element Vector{Int64}:
 3
 2
 4
 1

julia> sort(v)
4-element Vector{Int64}:
 1
 2
 3
 4

# sort descending
julia> sort(v, rev=true)
4-element Vector{Int64}:
 4
 3
 2
 1

很簡單! sort()這個函式的名稱淺顯易懂,而且看起來會回傳排序好的Vector。

開玩笑的! 學到課程大綱的這個階段,你或許已經(正確地)猜到,有些細節值得我們好好討論。

會修改與不會修改的函式

執行sort(v)之後,v會變成什麼?

julia> v
4-element Vector{Int64}:
 3
 2
 4
 1

julia> issorted(v)
false

原本的向量並沒有改變。 sort()是一個不會修改輸入的函式,它會回傳輸入的_副本_。

在許多情境下這是個好設計:我們有一個純函式,行為很容易推斷,即使在平行、分散式的程式碼中也是如此。

另一方面,假設我們有一個包含十億個字串的陣列,並想將它排序。

排序大概會很慢。 此外,我們還得複製所有內容(很慢!),並配置足夠的記憶體(好幾 GB!)來存放副本。

比較一下這段很類似的程式碼:

# v is as previously

julia> issorted(v)
false

julia> sort!(v)
4-element Vector{Int64}:
 1
 2
 3
 4

julia> v
4-element Vector{Int64}:
 1
 2
 3
 4

julia> issorted(v)
true

現在輸入的Vector會就地排序,不需複製,而原本的順序則會永久消失。

這樣做可能很有效率,也可能藏有 bug 並導致意想不到的副作用,還可能讓其他使用你程式碼的人感到困惑。 請謹慎選擇!

sort!()是「會修改」的函式,而且依照(非常強烈)的慣例,任何會修改輸入的函式,名稱後面都會加上!,提醒程式設計師注意。

排序索引陣列

如果要排序的元素是大型物件,改為操作它們的位置索引可能會更容易。

julia> langs = ["Julia", "C++", "Python", "Elixir"]
4-element Vector{String}:
 "Julia"
 "C++"
 "Python"
 "Elixir"

julia> inxs = sortperm(langs)  # => Integers
4-element Vector{Int64}:
 2
 4
 1
 3

julia> langs[inxs]  # sort the entire vector
4-element Vector{String}:
 "C++"
 "Elixir"
 "Julia"
 "Python"

julia> langs[inxs[1:2]]  # slice the indices
2-element Vector{String}:
 "C++"
 "Elixir"

因為langs是一個 Vector,每個索引都是落在1:length(langs)範圍內的整數。 sortperm()的輸出是這些整數的一種排列,對應於輸入向量的排序結果。

說明

你被一間為烏龜製作拼布被的公司聘為助理生產工程師。

訂單整天陸續進來,內容是一組顧客名稱或編號,以及訂購數量。 這些訂單會在早上、當天生產開始之前處理完畢。

只要當天的訂單全部完成,你就可以領全薪下班回家,所以大家都有動機盡可能縮短工作日。 由於每張訂單都由單一機器處理,先把最大的訂單排進去,就能把啟動工作的額外負擔推給較小的訂單,進而縮短整體生產時間。 因此,你決定提供一些排序函式來協助加快排程,讓你可以早點回家替烏龜織件毛衣。

1. 排序數量!

如前所述,數量陣列需要由大到小排序。 由於這些陣列可能很大,排序應該以原地方式進行。 為了正確追蹤所有資料,你還需要能依照數量來排序顧客陣列,因此會需要一組數量的排序排列。

請撰寫一個函式sortquantity!(qty),它接受一個由Integer數量組成的Vector。 這個函式會以原地方式將輸入的qty由大到小排序,並回傳代表這個排序結果的排序排列。

julia> qty = [8, 9, 5, 6]
4-element Vector{Int64}:
 8
 9
 5
 6

julia> sortquantity!(qty)
4-element Vector{Int64}:
 2
 1
 4
 3

julia> qty # original vector is sorted in-place
4-element Vector{Int64}:
 9
 8
 6
 5

2. 排序顧客

如上所述,你需要依照固定的順序追蹤顧客。

請撰寫一個函式sortcustomer(cust, srtperm),它接受一個顧客的Vector cust 和一個排序排列的Vector srtperm。 這個函式會回傳一個新的陣列,其中的顧客依照這個排序排列所指定的順序排列。

julia> cust = ["a", "b", "c"]
3-element Vector{String}:
 "a"
 "b"
 "c"

julia> sortcustomer(cust, [2, 3, 1])
3-element Vector{String}:
 "b"
 "c"
 "a"

julia> cust # original vector remains unsorted
3-element Vector{String}:
 "a"
 "b"
 "c"

3. 生產排程!

現在你可以把所有東西組合起來,幫那些烏龜保暖了! 你已經能正確排序數量和顧客,但還需要能還原出貨時的原始順序。 這可以透過反向排序排列來完成。

請撰寫一個函式production_schedule!(cust, qty),它接受一個顧客的Vector和一個數量的Vector。 這個函式會以原地方式將數量由大到小排序。 它會回傳一個新的顧客Vector,排序與數量相同,以及一個反向排序排列,可用來把已排序的向量還原成原始順序。

julia> cust, qty = ["a", "b", "c"], [2, 3, 1]
(["a", "b", "c"], [2, 3, 1])

julia> orderedcust, invperm = production_schedule!(cust, qty)
(["b", "a", "c"], [2, 1, 3])

julia> qty # has been sorted in-place
3-element Vector{Int64}:
 3
 2
 1

julia> orderedcust[invperm], qty[invperm] # inverse permutation gives original ordering
(["a", "b", "c"], [2, 3, 1])
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Julia Exercism

準備好開始 排序試煉 了嗎?

註冊 Exercism,透過 35 個概念128 個練習 和真人引導來學習並精通 Julia,全部免費。