Bagaimana menemukan set maksimal elemen dari array sehingga setiap elemen dalam lebih besar atau sama dengan kardinalitas ?

14

Saya memiliki masalah algoritmik.

Diberikan array (atau set) dari bilangan bulat tidak negatif. Temukan set maksimal dari sedemikian rupa sehingga untuk semua ,.TnSTaSa|S|

Sebagai contoh:

  1. Jika T = [1, 3, 4, 1, 3, 6], maka S dapat menjadi [3, 3, 6] atau [3, 4, 6] atau [4, 3, 6].
  2. Dalam T = [7, 5, 1, 1, 7, 4], maka S adalah [7, 5, 7, 4].

Saya sudah mencoba fungsi rekursif ini.

function(T):
    if minimum(T) >= length(T): 
        return T
    else: 
        return function(T\minimum(T))

Apakah ada algoritma non-rekursif. (Saya tidak memeriksa algoritme rekursif saya, sehingga bisa memiliki beberapa kekurangan.)

drzbir
sumber

Jawaban:

14

Sortir T. Lalu ambil elemen sementara T[i] >= i+1.

Sebagai contoh sorted(T)=[6,4,3,3,1,1]. Kemudian, T[0] = 6 > 1, T[1] = 4 > 2, T[2] = 3 <= 3dan akhirnya, T[3] = 3 < 4jadi kita harus S = [T[0], T[1], T[2]].

Karolis Juodelė
sumber
3
Ini, tentu saja, melewatkan solusi yang lain , tetapi tampaknya OP sedang mencari solusi apa pun , bukan semua solusi. {6,3,3}
Rick Decker
2
Ini mendapatkan jumlah elemen yang benar. Kami tahu kami memiliki solusi dengan 3 elemen tetapi tidak dengan 4; dalam hal ini kami memiliki 4 elemen ≥ 3, jadi kami tahu kami dapat memilih 3 dari mereka untuk solusi.
gnasher729
3
Saya menghargai argumen tentang kebenaran.
Raphael
Saya pikir Anda mungkin bisa melakukannya dalam waktu O (n) dengan varian introselect.
user2357112 mendukung Monica
8

Dari komentar saya awalnya: Ini terkait erat dengan kuantitas di mana-mana dalam penilaian produktivitas akademik, indeks Hirsh, lebih dikenal sebagai -indexh . Singkatnya itu didefinisikan sebagai jumlah publikasi satu memiliki rupa sehingga masing-masing dari mereka memiliki setidaknya h kutipan (yang tersebut terbesar h ).hhh

Satu-satunya cara masalah Anda berbeda adalah bahwa Anda akan tertarik tidak hanya pada berapa banyak publikasi yang memenuhi kriteria tetapi juga apa yang dihitung oleh kutipan mereka , tetapi itu adalah modifikasi sepele. Data sudah ada di sana, algoritma asli hanya menjatuhkannya.

Perhitungan yang diterapkan secara umum agak mudah dan setuju dengan jawaban Karolis Juodelė .

Pembaruan: Bergantung pada ukuran dan karakter data Anda, mungkin perlu mengeksplorasi metode yang mengurutkan sebagian array dengan memfilter data di atas dan di bawah titik penting (quicksort muncul di benak). Kemudian tergantung pada apakah ada terlalu sedikit atau terlalu banyak menyesuaikan pivot dan mengulang pada subset yang berisi itu dan seterusnya. Anda tidak perlu urutan antara elemen lebih tinggi dari , dan tentu saja tidak antara yang lebih rendah dari itu. Jadi misalnya, setelah Anda menemukan semua elemen lebih besar atau sama dengan h 1 dan ada kurang dari h 1 dari mereka, Anda tidak perlu menyentuh subset itu lagi, cukup tambahkan saja. Ini mengubah rekursi yang melekat pada quicksort ke rekursi ekor dan dengan demikian dapat ditulis ulang sebagai loop.hh1h1

Haskell saya agak berkarat tetapi ini harus melakukan apa yang saya jelaskan di atas dan tampaknya berhasil. Semoga bisa dipahami sampai taraf tertentu, saya senang bisa memberikan penjelasan lebih lanjut.

-- just a utility function
merge :: [a] -> [a] -> [a]
merge [] ys = ys
merge (x:xs) ys = x : merge xs ys

-- the actual implementation
topImpl :: [Int] -> [Int] -> [Int]
topImpl [] granted = granted
topImpl (x:xs) granted
  | x == (1 + lGreater + lGranted) = x : merge greater granted
  | x > (1 + lGreater + lGranted) = topImpl smaller (x : merge greater granted)
  | otherwise = topImpl greater granted
  where smaller = [y | y <- xs, y < x]
        greater = [y | y <- xs, y >= x]
        lGreater = length greater
        lGranted = length granted

-- starting point is: top of whole array, granted is empty
top :: [Int] -> [Int]
top arr = topImpl arr []

Idenya adalah untuk mengumpulkan grantedapa yang Anda ketahui pasti akan berpartisipasi dalam hasil, dan tidak mengurutkannya lebih jauh. Jika greaterbersama dengan xkecocokan, kita beruntung, jika tidak kita perlu mencoba dengan subset yang lebih kecil. (Pivot xadalah apa pun yang kebetulan menjadi item pertama dari sublist yang saat ini dipertimbangkan.) Perhatikan bahwa keuntungan signifikan terhadap pengambilan elemen terbesar satu per satu adalah bahwa kami melakukan ini pada blok ukuran rata-rata dan tidak perlu menyortirnya lebih lanjut.remaining/2

Contoh:

Mari kita ambil set Anda [1,3,4,1,3,6].

  1. x = 1, granted = [], greater = [3,4,1,3,6]. Aduh, kita menabrak kasus patologis ketika pivot terlalu kecil (sebenarnya sangat kecil yang smallerkosong) tepat di langkah pertama. Untungnya algo kami siap untuk itu. Itu membuang xdan mencoba lagi dengan greatersendirian.

  2. x = 3, granted = [], greater = [4,3,6]. Bersama-sama, mereka membentuk array dengan panjang 4 tetapi kami hanya memiliki yang terbatas dari bawah dengan 3 jadi itu terlalu banyak. Ulangi greatersendirian.

  3. x = 4, granted = [], greater = [6]. Ini memberikan array 2 elemen ≥ 4 masing-masing, tampaknya kita mungkin telah menggunakan beberapa elemen lagi. Simpan ini dan ulangi terus smaller = [3].

  4. x = 3, granted = [4,6], greater = []. Ini bersama-sama memberikan array 3 elemen ≥ 3 masing-masing, sehingga kami memiliki solusi kami [3,4,6]dan kami dapat kembali. (Perhatikan bahwa permutasi dapat bervariasi tergantung pada urutan input, tetapi akan selalu mengandung syarat setinggi mungkin, tidak pernah [3,3,6]atau [3,3,4]sebagai contoh Anda.)

(Btw. Perhatikan bahwa rekursi memang hanya runtuh ke satu siklus.) Kompleksitasnya agak lebih baik daripada quicksort karena banyaknya perbandingan yang disimpan:

n1

O(logn)O(n)

nO(n2)

Ada beberapa perbandingan yang tidak perlu dalam kode di atas, seperti menghitung smallerapakah kita membutuhkannya atau tidak, mereka dapat dengan mudah dihapus. (Aku pikir evaluasi malas akan membereskannya.)

Vee
sumber
6

Tidak ada yang salah dengan algoritme Anda, dan tentu saja sebagian besar algoritme rekursif dapat dikonversi menjadi loop, di sini versi loop kode rekursif Anda:

function(T):
    while minimum(T) <= lenght(T):
         remove minimum(T) from T
    loop
fernando.reyes
sumber
6
Semua algoritma rekursif dapat dikonversi menjadi loop. Bagaimanapun, mesin Turing tidak tahu apa-apa tentang rekursi.
David Richerby
4

Algoritma rekursif apa pun dapat ditulis ulang untuk menggunakan iterasi. Bagaimanapun, mesin Turing tidak tahu apa-apa tentang rekursi tetapi bisa mengimplementasikan algoritma apa pun. Pada prinsipnya, Anda dapat menulis ulang fungsi rekursif Anda dengan menulis kode manipulasi tumpukan Anda sendiri untuk mengingat nilai-nilai parameter fungsi dan variabel lokal apa pun yang mungkin dimilikinya. Dalam kasus khusus ini, fungsi Anda bersifat ekor-rekursif (sekali panggilan rekursif kembali, hal yang memanggilnya segera kembali juga) sehingga Anda bahkan tidak perlu mempertahankan tumpukan.

David Richerby
sumber
3

Gunakan min-heap untuk melakukan heapsort parsial, karena Anda tidak perlu seluruh array diurutkan.

Terus popping elemen dengan rakus sampai Anda melebihi ambang yang diberikan.

pengguna541686
sumber
2
Di sini juga, saya menghargai gagasan tentang kebenaran.
Raphael