Apakah ada cara untuk mengukur seberapa daftar diurutkan?

161

Apakah ada cara untuk mengukur seberapa daftar diurutkan?

Maksud saya, ini bukan tentang mengetahui apakah suatu daftar diurutkan atau tidak (boolean), tetapi sesuatu seperti rasio "penyortiran", sesuatu seperti koefisien korelasi dalam statistik.

Sebagai contoh,

  • Jika item daftar berada dalam urutan menaik, maka nilainya adalah 1,0

  • Jika daftar diurutkan secara turun, nilainya akan -1.0

  • Jika daftar hampir diurutkan naik, nilainya akan menjadi 0,9 atau nilai mendekati 1.

  • Jika daftar tidak diurutkan sama sekali (acak), nilainya akan mendekati 0

Saya sedang menulis perpustakaan kecil di Scala untuk latihan. Saya pikir tingkat penyortiran akan berguna, tetapi saya tidak menemukan informasi tentang sesuatu seperti itu. Mungkin saya tidak tahu istilah yang memadai untuk konsep ini.

Josell
sumber
4
Apakah ini akan digunakan untuk menentukan algoritma yang ideal untuk mengurutkan daftar? Misalnya untuk nilai yang mendekati 0, QuickSort akan ideal, tetapi nilai di kedua ujung skala (hampir diurutkan atau hampir diurutkan terbalik), MergeSort akan jauh lebih cepat, karena QC beralih ke O (N ^ 2) dalam kasus tersebut.
Darrel Hoffman
8
+1 untuk "rasio penyortiran"
0x499602D2
1
@Fuhrmanator Versi stokastik dari algoritma tidak harus melakukan semacam untuk sampai pada perkiraan probabilistik dari penyortiran. Hanya jika Anda ingin mendapatkan ukuran yang tepat , Anda perlu melakukan pengurutan.
Timothy Shields
1
Insting pertama yang sarkastik tapi lucu: Anda dapat menyisipkan susunan daftar dan melihat berapa lama, dan kemudian membandingkannya dengan berapa lama yang diperlukan untuk menyortir (sekarang disortir) daftar dan kebalikannya.
kqr

Jawaban:

142

Anda cukup menghitung jumlah inversi dalam daftar.

Inversi

Suatu inversi dalam suatu urutan elemen-elemen tipe Tadalah sepasang elemen-elemen sekuens yang muncul tidak sesuai dengan beberapa pemesanan <pada set elemen T.

Dari Wikipedia :

Secara formal, biarkan A(1), A(2), ..., A(n)menjadi urutan nangka.
Jika i < jdan A(i) > A(j), maka pasangan (i,j)disebut inversi dari A.

Nomor inversi dari urutan adalah salah satu ukuran umum penyortirannya.
Secara formal, nomor inversi didefinisikan sebagai jumlah inversi, yaitu,

definisi

Untuk membuat definisi ini lebih jelas, pertimbangkan urutan contoh 9, 5, 7, 6. Urutan ini memiliki inversi (0,1), (0,2), (0,3), (2,3) dan nomor inversi 4 .

Jika Anda menginginkan nilai antara 0dan 1, Anda dapat membagi nomor inversi dengan N choose 2.

Untuk benar-benar membuat algoritma untuk menghitung skor ini untuk bagaimana diurutkan daftar itu, Anda memiliki dua pendekatan:

Pendekatan 1 (Deterministik)

Ubah algoritma penyortiran favorit Anda untuk melacak berapa banyak inversi yang dikoreksi saat dijalankan. Meskipun ini bukan trivial dan memiliki implementasi yang bervariasi tergantung pada algoritma pengurutan yang Anda pilih, Anda akan berakhir dengan algoritma yang tidak lebih mahal (dalam hal kompleksitas) daripada algoritma pengurutan yang Anda mulai.

Jika Anda mengambil rute ini, perlu diketahui bahwa tidak sesederhana menghitung "swap". Mergesort, misalnya, adalah kasus terburuk O(N log N), namun jika dijalankan pada daftar yang diurutkan dalam urutan menurun, itu akan memperbaiki semua N choose 2inversi. Itu O(N^2)inversi diperbaiki di O(N log N)operasi. Jadi beberapa operasi pasti mengoreksi lebih dari satu inversi pada suatu waktu. Anda harus berhati-hati dengan implementasi Anda. Catatan: Anda dapat melakukan ini dengan O(N log N)kerumitan, itu hanya rumit.

Terkait: menghitung jumlah "inversi" dalam permutasi

Pendekatan 2 (Stochastic)

  • Pasangan sampel secara acak (i,j), di manai != j
  • Untuk setiap pasangan, tentukan apakah list[min(i,j)] < list[max(i,j)](0 atau 1)
  • Hitung rata-rata perbandingan ini dan kemudian normalkan dengan N choose 2

Saya pribadi akan pergi dengan pendekatan stokastik kecuali jika Anda memiliki persyaratan ketepatan - jika hanya karena sangat mudah diimplementasikan.


Jika yang benar-benar Anda inginkan adalah nilai ( z') antara -1(diurutkan menurun) ke 1(diurutkan naik), Anda cukup memetakan nilai di atas ( z), yang antara 0(diurutkan naik) dan 1(diurutkan turun), ke rentang ini menggunakan rumus ini :

z' = -2 * z + 1
Perisai Timothy
sumber
2
Agak menarik bagi saya bahwa menyortir daftar adalah (biasanya) O (n * logn), dan metode inversi komputasi yang naif / jelas adalah O (n ^ 2). Saya ingin tahu apakah ada algoritma yang lebih baik di luar sana untuk menghitung jumlah inversi?
Mark Bessey
5
Ada beberapa pendekatan menarik dalam pertanyaan SO ini: stackoverflow.com/questions/6523712/... Pada dasarnya, mereka sama dengan menyortir array untuk mengetahui berapa banyak inversi yang ada.
Mark Bessey
4
Saya naif berpikir Anda hanya bisa menghitung pasangan yang berdekatan yang rusak. Tapi itu akan sangat mengurangi jumlah: 1 2 3 1 2 3 hanya memiliki satu inversi yang berdekatan, tetapi 50% terbalik dengan ukuran yang lebih tepat.
Barmar
2
@Barmar Saya pikir daftar 1 2 3 1 2 3 akan memenuhi syarat sebagai agak diurutkan ;-)
scunliffe
2
@TimothyShields, well, tidak, tidak. Tapi saya tidak akan mengulangi intinya. Hanya saran untuk menambahkan definisi non-formal yang lebih mudah diakses oleh orang yang cenderung tidak simbolis.
Chris Calo
24

Ukuran tradisional tentang bagaimana diurutkan daftar (atau struktur berurutan lainnya) adalah, adalah jumlah inversi.

Jumlah inversi adalah jumlah pasangan (a, b) st indeks a <b DAN b <<a. Untuk tujuan ini <<mewakili hubungan pemesanan apa pun yang Anda pilih untuk jenis khusus Anda.

Daftar yang sepenuhnya diurutkan tidak memiliki inversi, dan daftar yang sepenuhnya terbalik memiliki jumlah inversi maksimum.

Marcin
sumber
5
Secara teknis, 5 4 3 2 1sepenuhnya disortir karena pesanan tidak ditentukan, tapi saya menjadi pedantic :-)
paxdiablo
7
@ paxdiablo Itu tergantung pada definisi <.
Marcin
@paxdiablo, well seseorang bisa mengukur sortir berdasarkan jarak dari jumlah inversi ke yang terdekat dari 0 atau n choose 2.
huon
17

Anda dapat menggunakan korelasi aktual.

Misalkan untuk setiap item dalam daftar diurutkan, Anda menetapkan peringkat integer mulai dari nol. Perhatikan bahwa grafik indeks posisi elemen versus peringkat akan terlihat seperti titik-titik dalam garis lurus (korelasi 1,0 antara posisi dan peringkat).

Anda dapat menghitung korelasi pada data ini. Untuk pengurutan terbalik, Anda akan mendapatkan -1 dan seterusnya.

Kaz
sumber
1
Maaf, tapi ini terlalu banyak yang tidak bisa dijelaskan, seperti bagaimana Anda menetapkan bilangan bulat.
Marcin
2
Anda perlu daftar yang diurutkan untuk menetapkan bilangan bulat; maka itu hanya penghitungan item.
Kaz
1
Persis apa yang akan saya sarankan. Tentukan korelasi antara posisi objek dalam daftar asli dan posisinya dalam daftar diurutkan. Berita buruknya adalah bahwa rutinitas korelasi mungkin berjalan di O (n ^ 2); kabar baiknya adalah mereka mungkin tidak cocok untuk lingkungan Anda.
Peter Webb
2
Ya, hanya Spearman rho en.wikipedia.org/wiki/…
Lucas
Saya ingin tahu ... apakah pendekatan ini setara dengan penskalaan jumlah inversi?
Clayton Stanley
4

Ada jawaban yang bagus, dan saya ingin menambahkan aspek matematika untuk kelengkapan:

  • Anda dapat mengukur seberapa daftar diurutkan dengan mengukur seberapa banyak itu berkorelasi dengan daftar yang diurutkan. Untuk melakukan itu, Anda dapat menggunakan korelasi peringkat (yang paling dikenal adalah Spearman's ), yang persis sama dengan korelasi biasa, tetapi menggunakan peringkat elemen dalam daftar, bukan nilai analog dari item-itemnya.

  • Ada banyak ekstensi, seperti koefisien korelasi (+1 untuk pengurutan yang tepat, -1 untuk inversi yang tepat)

  • Ini memungkinkan Anda untuk memiliki properti statistik untuk ukuran ini, seperti teorema batas pusat permutasional, yang memungkinkan Anda untuk mengetahui distribusi ukuran ini untuk daftar acak.

meduz
sumber
3

Terlepas dari jumlah inversi, untuk daftar angka, jarak kuadrat rata-rata dari status yang diurutkan dapat dibayangkan:

#! ruby
d = -> a { a.zip( a.sort ).map { |u, v| ( u - v ) ** 2 }.reduce( :+ ) ** 0.5 }

a = 8, 7, 3, 4, 10, 9, 6, 2, 5, 1
d.( a ) #=> 15.556
d.( a.sort ) #=> 0.0
d.( a.sort.reverse ) # => 18.166 is the worrst case
Boris Stitnicky
sumber
Saya pikir itulah kuadrat dari fungsi korelasi standar, lihat en.wikipedia.org/wiki/Correlation_ratio . Dan berlaku sama untuk daftar non-numerik; dua nilai yang dibandingkan adalah posisi objek dalam dua daftar.
Peter Webb
Saya bodoh. Saya bahkan tidak tahu apa rasio korelasinya. Ketika saya membaca artikel Wikipedia itu, tepat di atas, saya diminta untuk mempelajari apa itu "dispersi statistik", kemudian "standar deviasi", lalu "variasi", lalu "koefisien korelasi antar kelas". Saya belajar semua itu, beberapa kali, dan beberapa kali, saya lupa lagi. Dalam jawaban pragmatis saya ini, saya cukup mengukur jarak antara dua vektor dengan teorema Pythagoras, yang saya ingat dari sekolah dasar, itu saja.
Boris Stitnicky
1

Saya tidak yakin dengan metode "terbaik", tetapi yang sederhana adalah membandingkan setiap elemen dengan yang sesudahnya, menambah penghitung jika elemen2> elemen 1 (atau apa pun yang ingin Anda uji) dan kemudian dibagi dengan jumlah total elemen. Itu akan memberi Anda persentase.

pengguna2369405
sumber
1

Saya akan menghitung perbandingan dan membaginya dengan jumlah total perbandingan. Berikut ini adalah contoh Python sederhana .

my_list = [1,4,5,6,9,-1,5,3,55,11,12,13,14]

right_comparison_count = 0

for i in range(len(my_list)-1):
    if my_list[i] < my_list[i+1]: # Assume you want to it ascending order
        right_comparison_count += 1

if right_comparison_count == 0:
    result = -1
else:
    result = float(right_comparison_count) / float((len(my_list) - 1))

print result
ibrahim
sumber
0

Bagaimana dengan sesuatu yang seperti ini?

#!/usr/bin/python3

def sign(x, y):
   if x < y:
      return 1
   elif x > y:
      return -1
   else:
      return 0

def mean(list_):
   return float(sum(list_)) / float(len(list_))

def main():
   list_ = [ 1, 2, 3, 4, 6, 5, 7, 8 ]
   signs = []
   # this zip is pairing up element 0, 1, then 1, 2, then 2, 3, etc...
   for elem1, elem2 in zip(list_[:-1], list_[1:]):
      signs.append(sign(elem1, elem2))

   # This should print 1 for a sorted list, -1 for a list that is in reverse order
   # and 0 for a run of the same numbers, like all 4's
   print(mean(signs))

main()
dstromberg
sumber
2
Ini hanya menghitung inversi yang berdekatan. Jika Anda melihat jawaban lain Anda akan melihat bahwa ini tidak cukup.
Konrad Rudolph
1
@KonradRudolph: Saya pikir jawaban ini memuaskan pertanyaan yang diajukan. Fakta bahwa jawaban lain lebih komprehensif tidak berarti jawaban ini tidak mencukupi; itu tergantung pada persyaratan OP.
LarsH
0

Jika Anda mengambil daftar Anda, menghitung peringkat nilai-nilai dalam daftar itu dan memanggil daftar peringkat Ydan daftar lain, Xyang berisi bilangan bulat dari 1hingga length(Y), Anda bisa mendapatkan ukuran pengurutan yang Anda cari dengan menghitung koefisien korelasi ,, di rantara dua daftar.

r = \frac{\sum ^n _{i=1}(X_i - \bar{X})(Y_i - \bar{Y})}{\sqrt{\sum ^n _{i=1}(X_i - \bar{X})^2} \sqrt{\sum ^n _{i=1}(Y_i - \bar{Y})^2}} 

Untuk daftar yang sepenuhnya diurutkan r = 1.0,, untuk daftar yang diurutkan terbalik r=-1.0,, dan perbedaan rantara batas-batas ini untuk berbagai tingkat pengurutan.

Masalah yang mungkin terjadi dengan pendekatan ini, tergantung pada aplikasinya, adalah bahwa menghitung peringkat setiap item dalam daftar sama dengan menyortirnya, sehingga ini merupakan operasi O (n log n).

Simon
sumber
Tapi itu tidak akan mengabaikan bentuk kurva. Jika array-nya diurutkan, tetapi, katakanlah, berisi nilai-nilai yang meningkat secara eksponensial, korelasinya akan kecil di mana ia ingin menjadi 1,0.
Lee Daniel Crocker
@ LeeDanielCrocker: Ya, itu poin yang bagus. Saya telah mengubah jawaban saya untuk mengatasi ini dengan mengambil peringkat nilai-nilai.
Simon