Bagaimana saya bisa menemukan (beralih di atas) SEMUA siklus dalam grafik berarah dari / ke simpul yang diberikan?
Sebagai contoh, saya ingin sesuatu seperti ini:
A->B->A
A->B->C->A
tetapi tidak: B-> C-> B
algorithm
graph-theory
graph-algorithm
pengguna7305
sumber
sumber
Jawaban:
Saya menemukan halaman ini dalam pencarian saya dan karena siklus tidak sama dengan komponen yang terhubung kuat, saya terus mencari dan akhirnya, saya menemukan algoritma yang efisien yang mencantumkan semua siklus (dasar) dari grafik yang diarahkan. Itu dari Donald B. Johnson dan makalahnya dapat ditemukan di tautan berikut:
http://www.cs.tufts.edu/comp/150GA/homeworks/hw1/Johnson%2075.PDF
Implementasi java dapat ditemukan di:
http://normalisiert.de/code/java/elementaryCycles.zip
Sebuah Mathematica demonstrasi algoritma Johnson dapat ditemukan disini , implementasi dapat didownload dari kanan ( "Download penulis kode" ).
Catatan: Sebenarnya, ada banyak algoritma untuk masalah ini. Beberapa dari mereka tercantum dalam artikel ini:
http://dx.doi.org/10.1137/0205007
Menurut artikel itu, algoritma Johnson adalah yang tercepat.
sumber
A->B->C->A
juga SD?simple_cycle
pada networkx.Pencarian pertama dengan backtracking yang dalam harus bekerja di sini. Menyimpan array nilai boolean untuk melacak apakah Anda mengunjungi sebuah node sebelumnya. Jika Anda kehabisan node baru untuk pergi (tanpa memukul node Anda sudah pernah), maka cukup mundur dan coba cabang yang berbeda.
DFS mudah diimplementasikan jika Anda memiliki daftar adjacency untuk mewakili grafik. Misalnya adj [A] = {B, C} menunjukkan bahwa B dan C adalah anak-anak dari A.
Misalnya, pseudo-code di bawah ini. "start" adalah simpul tempat Anda memulai.
Panggil fungsi di atas dengan simpul mulai:
sumber
if (node == start):
- apa yang adanode and start
dalam panggilan pertamastart
). Dimulai pada titik itu dan melakukan DFS sampai kembali ke titik itu lagi, kemudian ia tahu itu menemukan siklus. Tapi itu sebenarnya tidak menghasilkan siklus, hanya hitungan dari mereka (tetapi mengubahnya untuk melakukan itu seharusnya tidak terlalu sulit).start
. Anda tidak benar-benar perlu menghapus bendera yang dikunjungi karena setiap bendera yang dikunjungi akan dihapus karenanyavisited[node]=NO;
. Namun perlu diingat bahwa jika Anda memiliki siklusA->B->C->A
, Anda akan mendeteksi itu 3 kali, sama sepertistart
3 siklus lainnya . Satu ide untuk mencegah hal ini adalah memiliki array yang dikunjungi di mana setiap node yang telah menjadistart
node pada suatu titik ditetapkan, dan kemudian Anda tidak mengunjungi kembali ini.Pertama-tama - Anda tidak benar-benar ingin mencoba menemukan semua siklus karena jika ada 1 maka ada jumlah tak terbatas dari mereka. Misalnya ABA, ABABA dll. Atau dimungkinkan untuk bergabung bersama 2 siklus menjadi 8-seperti siklus dll, dll ... Pendekatan yang berarti adalah untuk mencari semua siklus sederhana yang disebut - yang tidak melintasi sendiri kecuali di titik awal / akhir. Kemudian jika Anda mau, Anda bisa menghasilkan kombinasi siklus sederhana.
Salah satu algoritma dasar untuk menemukan semua siklus sederhana dalam grafik terarah adalah ini: Lakukan traversal kedalaman-pertama dari semua jalur sederhana (yang tidak memotong sendiri) dalam grafik. Setiap kali ketika node saat ini memiliki penerus di stack, siklus sederhana ditemukan. Ini terdiri dari elemen-elemen pada stack yang dimulai dengan penerus yang diidentifikasi dan diakhiri dengan bagian atas stack. Traversal pertama yang dalam dari semua jalur sederhana mirip dengan pencarian pertama yang dalam, tetapi Anda tidak menandai / merekam node yang dikunjungi selain yang saat ini ada di stack sebagai titik berhenti.
Algoritma brute force di atas sangat tidak efisien dan selain itu menghasilkan banyak salinan siklus. Namun ini adalah titik awal dari beberapa algoritma praktis yang menerapkan berbagai peningkatan untuk meningkatkan kinerja dan menghindari duplikasi siklus. Saya terkejut mengetahui beberapa waktu lalu bahwa algoritma ini tidak tersedia di buku teks dan di web. Jadi saya melakukan riset dan mengimplementasikan 4 algoritma dan 1 algoritma untuk siklus dalam grafik tidak langsung di perpustakaan Java open source di sini: http://code.google.com/p/niographs/ .
BTW, karena saya sebutkan grafik tidak terarah: Algoritma untuk mereka berbeda. Bangun pohon yang merentang dan kemudian setiap tepi yang bukan bagian dari pohon membentuk siklus sederhana bersama dengan beberapa tepi di pohon. Siklus yang ditemukan dengan cara ini membentuk basis siklus. Semua siklus sederhana kemudian dapat ditemukan dengan menggabungkan 2 atau lebih siklus dasar yang berbeda. Untuk detail lebih lanjut lihat misalnya ini: http://dspace.mit.edu/bitstream/handle/1721.1/68106/FTL_R_1982_07.pdf .
sumber
jgrapht
yang digunakan dihttp://code.google.com/p/niographs/
Anda dapat mengambil contoh dari github.com/jgrapht/jgrapht/wiki/DirectedGraphDemoPilihan paling sederhana yang saya temukan untuk menyelesaikan masalah ini adalah menggunakan python lib bernama
networkx
.Itu mengimplementasikan algoritma Johnson yang disebutkan dalam jawaban terbaik dari pertanyaan ini tetapi itu membuat cukup sederhana untuk dieksekusi.
Singkatnya, Anda membutuhkan yang berikut:
Jawaban: [['a', 'b', 'd', 'e'], ['a', 'b', 'c']]
sumber
nx.DiGraph({'a': ['b'], 'b': ['c','d'], 'c': ['a'], 'd': ['e'], 'e':['a']})
Untuk memperjelas:
Komponen yang terhubung dengan kuat akan menemukan semua subgraph yang memiliki setidaknya satu siklus di dalamnya, tidak semua siklus yang mungkin dalam grafik. misalnya jika Anda mengambil semua komponen yang sangat terhubung dan runtuh / kelompok / gabungkan masing-masing menjadi satu simpul (yaitu simpul per komponen), Anda akan mendapatkan pohon tanpa siklus (sebenarnya DAG). Setiap komponen (yang pada dasarnya adalah subgraph dengan setidaknya satu siklus di dalamnya) dapat mengandung lebih banyak siklus yang mungkin secara internal, jadi SCC TIDAK akan menemukan semua siklus yang mungkin, itu akan menemukan semua kelompok yang mungkin memiliki setidaknya satu siklus, dan jika Anda mengelompokkan mereka, maka grafik tidak akan memiliki siklus.
untuk menemukan semua siklus sederhana dalam grafik, seperti yang disebutkan lainnya, algoritma Johnson adalah kandidat.
sumber
Saya pernah diberikan ini sebagai pertanyaan wawancara, saya curiga ini terjadi pada Anda dan Anda datang ke sini untuk meminta bantuan. Pecahkan masalah menjadi tiga pertanyaan dan itu menjadi lebih mudah.
Masalah 1) Gunakan pola iterator untuk menyediakan cara mengulangi hasil rute. Tempat yang baik untuk meletakkan logika untuk mendapatkan rute berikutnya mungkin adalah "moveNext" dari iterator Anda. Untuk menemukan rute yang valid, itu tergantung pada struktur data Anda. Bagi saya itu adalah tabel sql yang penuh dengan kemungkinan rute yang valid, jadi saya harus membuat kueri untuk mendapatkan tujuan yang valid dengan sumber.
Masalah 2) Dorong setiap simpul saat Anda menemukan mereka ke dalam koleksi saat Anda mendapatkannya, ini berarti bahwa Anda dapat melihat apakah Anda "menggandakan kembali" pada suatu titik dengan sangat mudah dengan menginterogasi koleksi yang Anda bangun dengan cepat.
Masalah 3) Jika pada suatu saat Anda melihat Anda menggandakan kembali, Anda dapat menghapus hal-hal dari koleksi dan "cadangan". Kemudian sejak saat itu cobalah untuk "bergerak maju" lagi.
Hack: jika Anda menggunakan Sql Server 2008 ada beberapa hal "hierarki" baru yang dapat Anda gunakan untuk menyelesaikan masalah ini dengan cepat jika Anda menyusun data di pohon.
sumber
Varian berbasis DFS dengan tepi belakang memang akan menemukan siklus, tetapi dalam banyak kasus BUKAN siklus minimal . Secara umum DFS memberi Anda tanda bahwa ada siklus tetapi tidak cukup baik untuk benar-benar menemukan siklus. Misalnya, bayangkan 5 siklus berbeda yang berbagi dua sisi. Tidak ada cara sederhana untuk mengidentifikasi siklus hanya menggunakan DFS (termasuk varian backtracking).
Algoritma Johnson memang memberikan semua siklus sederhana yang unik dan memiliki kompleksitas ruang dan waktu yang baik.
Tetapi jika Anda hanya ingin menemukan siklus MINIMAL (artinya mungkin ada lebih dari satu siklus melewati titik mana pun dan kami tertarik untuk menemukan yang minimal) DAN grafik Anda tidak terlalu besar, Anda dapat mencoba menggunakan metode sederhana di bawah ini. Ini SANGAT sederhana tetapi agak lambat dibandingkan dengan Johnson.
Jadi, salah satu benar-benar cara termudah untuk menemukan siklus MINIMAL adalah dengan menggunakan algoritma Floyd untuk menemukan jalan minimal antara semua simpul menggunakan matriks ketetanggaan. Algoritma ini tidak seoptimal Johnson, tetapi sangat sederhana dan loop dalamnya sangat ketat sehingga untuk grafik yang lebih kecil (<= 50-100 node) sangat masuk akal untuk menggunakannya. Kompleksitas waktu adalah O (n ^ 3), kompleksitas ruang O (n ^ 2) jika Anda menggunakan pelacakan induk dan O (1) jika tidak. Pertama-tama mari kita cari jawaban untuk pertanyaan jika ada siklus. Algoritma ini sangat sederhana. Di bawah ini cuplikan di Scala.
Awalnya algoritma ini beroperasi pada grafik tepi tertimbang untuk menemukan semua jalur terpendek di antara semua pasangan node (karenanya argumen bobot). Agar dapat bekerja dengan benar, Anda harus memberikan 1 jika ada tepi yang diarahkan antara node atau NO_EDGE sebaliknya. Setelah algoritma dijalankan, Anda dapat memeriksa diagonal utama, jika ada nilai kurang dari NO_EDGE dari node ini berpartisipasi dalam siklus panjang yang sama dengan nilai. Setiap simpul lain dari siklus yang sama akan memiliki nilai yang sama (pada diagonal utama).
Untuk merekonstruksi siklus itu sendiri kita perlu menggunakan versi algoritma yang sedikit dimodifikasi dengan pelacakan induk.
Matriks induk pada awalnya harus mengandung indeks titik sumber dalam sel tepi jika ada tepi antara simpul dan -1. Setelah fungsi kembali, untuk setiap tepi Anda akan memiliki referensi ke simpul induk di pohon jalur terpendek. Dan kemudian mudah untuk memulihkan siklus yang sebenarnya.
Secara keseluruhan, kami memiliki program berikut untuk menemukan semua siklus minimal
dan metode utama kecil hanya untuk menguji hasilnya
dan hasilnya
sumber
Dalam kasus grafik tidak terarah, sebuah makalah yang baru-baru ini diterbitkan ( daftar optimal siklus dan jalur di grafik tidak berarah ) menawarkan solusi optimal asimtotik. Anda dapat membacanya di sini http://arxiv.org/abs/1205.2766 atau di sini http://dl.acm.org/citation.cfm?id=2627951 Saya tahu itu tidak menjawab pertanyaan Anda, tetapi karena judul pertanyaan Anda tidak menyebutkan arah, mungkin masih berguna untuk pencarian Google
sumber
Mulai dari simpul X dan periksa semua simpul anak (simpul induk dan simpul sama jika tidak diarahkan). Tandai simpul anak tersebut sebagai anak-anak X. Dari simpul anak A seperti itu, tandai anak-anak menjadi anak-anak A, X ', di mana X' ditandai sebagai 2 langkah jauhnya.). Jika Anda kemudian menekan X dan menandainya sebagai anak X '', itu berarti X dalam siklus 3 simpul. Mengulangi ke induknya itu mudah (apa adanya, algoritme tidak memiliki dukungan untuk ini sehingga Anda akan menemukan orangtua mana pun yang memiliki X ').
Catatan: Jika grafik tidak diarahkan atau memiliki tepi dua arah, algoritma ini menjadi lebih rumit, dengan asumsi Anda tidak ingin melintasi tepi yang sama dua kali untuk satu siklus.
sumber
Jika yang Anda inginkan adalah menemukan semua rangkaian elementer dalam grafik, Anda dapat menggunakan algoritma EC, oleh JAMES C. TIERNAN, yang ditemukan di kertas sejak 1970.
The sangat asli algoritma EC karena saya berhasil menerapkannya di php (harapan tidak ada kesalahan ditampilkan di bawah). Itu dapat menemukan loop juga jika ada. Sirkuit dalam implementasi ini (yang mencoba mengkloning yang asli) adalah elemen bukan nol. Nol di sini berarti tidak ada (nol seperti yang kita tahu).
Terlepas dari itu di bawah ini mengikuti implementasi lain yang memberikan algoritma lebih mandiri, ini berarti node dapat mulai dari mana saja bahkan dari angka negatif, misalnya -4, -3, -2, .. dll.
Dalam kedua kasus itu diperlukan bahwa node adalah berurutan.
Anda mungkin perlu mempelajari makalah asli, James C. Tiernan Elementary Circuit Algorithm
maka ini adalah implementasi lain, lebih independen dari grafik, tanpa nilai goto dan tanpa array, alih-alih menggunakan kunci array, path, grafik dan sirkuit disimpan sebagai kunci array (gunakan nilai array jika Anda suka, cukup ubah yang diperlukan baris). Contoh grafik mulai dari -4 untuk menunjukkan independensinya.
Saya telah menganalisis dan mendokumentasikan EC tetapi sayangnya dokumentasinya dalam bahasa Yunani.
sumber
Ada dua langkah (algoritma) yang terlibat dalam menemukan semua siklus dalam DAG.
Langkah pertama adalah menggunakan algoritma Tarjan untuk menemukan set komponen yang sangat terhubung.
Langkah kedua adalah menemukan siklus (jalur) di dalam komponen yang terhubung. Saran saya adalah menggunakan versi modifikasi dari algoritma Hierholzer.
Idenya adalah:
Berikut ini tautan ke implementasi Java dengan test case:
http://stones333.blogspot.com/2013/12/find-cycles-in-directed-graph-dag.html
sumber
http://www.me.utexas.edu/~bard/IP/Handouts/ikes.pdf
sumber
Saya tersandung pada algoritma berikut yang tampaknya lebih efisien daripada algoritma Johnson (setidaknya untuk grafik yang lebih besar). Namun saya tidak yakin tentang kinerjanya dibandingkan dengan algoritma Tarjan.
Selain itu, saya hanya memeriksanya untuk segitiga sejauh ini. Jika tertarik, silakan lihat "Algoritma Pencatatan Subgraph dan Subgraph" oleh Norishige Chiba dan Takao Nishizeki ( http://dx.doi.org/10.1137/0214017 )
sumber
Solusi Javascript menggunakan daftar tertaut yang disjoint set. Dapat ditingkatkan untuk memisahkan pengaturan hutan untuk waktu lari yang lebih cepat.
sumber
DFS dari mulai simpul s, melacak jalur DFS selama traversal, dan merekam jalur jika Anda menemukan tepi dari simpul v di jalur ke s. (v, s) adalah back-edge di pohon DFS dan dengan demikian menunjukkan siklus yang mengandung s.
sumber
Mengenai pertanyaan Anda tentang Siklus Permutasi , baca lebih lanjut di sini: https://www.codechef.com/problems/PCYCLE
Anda dapat mencoba kode ini (masukkan ukuran dan nomor digit):
sumber
Versi DFS c ++ untuk kode semu di jawaban lantai dua:
sumber