Saya adalah pengembang beberapa perangkat lunak silsilah keluarga (ditulis dalam C ++ dan Qt). Saya tidak punya masalah sampai salah satu pelanggan saya mengirimi saya laporan bug. Masalahnya adalah bahwa pelanggan memiliki dua anak dengan putri mereka sendiri, dan, akibatnya, ia tidak dapat menggunakan perangkat lunak saya karena kesalahan.
Kesalahan-kesalahan itu adalah hasil dari berbagai pernyataan dan invarian saya tentang grafik keluarga yang sedang diproses (misalnya, setelah berjalan satu siklus, program menyatakan bahwa X tidak bisa menjadi ayah dan kakek dari Y).
Bagaimana saya bisa menyelesaikan kesalahan-kesalahan itu tanpa menghapus semua pernyataan data?
c++
graph
cycle
assertions
family-tree
Partick Höse
sumber
sumber
Jawaban:
Tampaknya Anda (dan / atau perusahaan Anda) memiliki kesalahpahaman mendasar tentang apa yang seharusnya menjadi silsilah keluarga.
Izinkan saya mengklarifikasi, saya juga bekerja untuk sebuah perusahaan yang memiliki (sebagai salah satu produknya) silsilah keluarga dalam portofolionya, dan kami telah berjuang dengan masalah yang sama.
Masalahnya, dalam kasus kami, dan saya menganggap kasus Anda juga, berasal dari format GEDCOM yang sangat beralasan tentang apa yang seharusnya menjadi sebuah keluarga. Namun format ini mengandung beberapa kesalahpahaman yang parah tentang seperti apa sebenarnya silsilah keluarga.
GEDCOM memiliki banyak masalah, seperti ketidakcocokan dengan hubungan seks yang sama, inses, dll ... Yang dalam kehidupan nyata lebih sering terjadi daripada yang Anda bayangkan (terutama ketika kembali ke masa 1700-1800).
Kami telah memodelkan silsilah keluarga kami dengan apa yang terjadi di dunia nyata: Peristiwa (misalnya, kelahiran, pernikahan, pertunangan, serikat pekerja, kematian, adopsi, dll.). Kami tidak membatasi hal ini, kecuali untuk hal-hal yang mustahil secara logis (misalnya, seseorang tidak dapat menjadi orang tua sendiri, hubungan memerlukan dua individu, dll ...)
Kurangnya validasi memberi kita solusi yang lebih "dunia nyata", lebih sederhana dan lebih fleksibel.
Adapun kasus khusus ini, saya akan menyarankan menghapus pernyataan karena mereka tidak berlaku secara universal.
Untuk menampilkan masalah (yang akan muncul) saya sarankan menggambar simpul yang sama sebanyak yang diperlukan, mengisyaratkan duplikasi dengan menerangi semua salinan pada memilih salah satu dari mereka.
sumber
Santai pernyataan Anda.
Bukan dengan mengubah aturan, yang kemungkinan besar sangat membantu 99,9% pelanggan Anda dalam menangkap kesalahan dalam memasukkan data mereka.
Alih-alih, ubah dari kesalahan "tidak dapat menambahkan hubungan" menjadi peringatan dengan "tambahkan pula".
sumber
Inilah masalah dengan pohon keluarga: mereka bukan pohon. Mereka diarahkan grafik asiklik atau DAG. Jika saya memahami prinsip-prinsip biologi reproduksi manusia dengan benar, tidak akan ada siklus.
Sejauh yang saya tahu, bahkan orang Kristen menerima pernikahan (dan dengan demikian anak-anak) antara sepupu, yang akan mengubah pohon keluarga menjadi DAG keluarga.
Moral dari cerita ini adalah: pilih struktur data yang tepat.
sumber
Saya kira Anda memiliki beberapa nilai yang secara unik mengidentifikasi seseorang di mana Anda dapat mendasarkan cek Anda.
Ini yang sulit. Dengan asumsi Anda ingin menjaga struktur pohon, saya sarankan ini:
Asumsikan ini:
A
punya anak dengan putrinya sendiri.A
menambahkan dirinya ke program sebagaiA
dan sebagaiB
. Begitu berperan sebagai ayah, sebut saja pacarnya.Tambahkan
is_same_for_out()
fungsi yang memberi tahu bagian penghasil output dari program Anda bahwa semua tautan akan dilihat secaraB
internalA
pada presentasi data.Ini akan membuat beberapa pekerjaan tambahan bagi pengguna, tapi saya kira TI akan relatif mudah diimplementasikan dan dipelihara.
Membangun dari itu, Anda dapat bekerja pada sinkronisasi kode
A
danB
untuk menghindari inkonsistensi.Solusi ini tentu saja tidak sempurna, tetapi merupakan pendekatan pertama.
sumber
Anda harus fokus pada apa yang benar-benar membuat nilai untuk perangkat lunak Anda . Apakah waktu yang dihabiskan untuk membuatnya bekerja untuk SATU konsumen sepadan dengan harga lisensi? Mungkin tidak.
Saya menyarankan Anda untuk meminta maaf kepada pelanggan ini, katakan kepadanya bahwa situasinya di luar cakupan untuk perangkat lunak Anda dan berikan pengembalian uang kepadanya.
sumber
Anda harus mengatur keluarga Atreides (baik modern, Dune , atau kuno, Oedipus Rex ) sebagai kasus pengujian. Anda tidak menemukan bug dengan menggunakan data yang disanitasi sebagai kasing.
sumber
Ini adalah salah satu alasan mengapa bahasa seperti "Go" tidak memiliki asersi. Mereka terbiasa menangani kasus yang mungkin tidak Anda pikirkan, terlalu sering. Anda seharusnya hanya menegaskan yang mustahil, bukan sekadar yang tidak mungkin . Melakukan yang terakhir inilah yang memberi reputasi buruk pada pernyataan. Setiap kali Anda mengetik
assert(
, berjalanlah selama sepuluh menit dan benar - benar memikirkannya.Dalam kasus Anda yang sangat mengganggu, dapat dibayangkan dan mengejutkan bahwa pernyataan seperti itu akan palsu dalam situasi yang jarang tetapi mungkin terjadi. Oleh karena itu, tangani di aplikasi Anda, jika hanya mengatakan "Perangkat lunak ini tidak dirancang untuk menangani skenario yang Anda sajikan".
Menegaskan bahwa kakekmu yang hebat, hebat, dan hebat menjadi ayahmu adalah hal yang wajar untuk dilakukan.
Jika saya bekerja untuk perusahaan pengujian yang disewa untuk menguji perangkat lunak Anda, tentu saja saya akan menyajikan skenario itu. Mengapa? Setiap 'pengguna' remaja namun cerdas akan melakukan hal yang sama persis dan menikmati 'laporan bug' yang dihasilkan.
sumber
Saya benci mengomentari situasi kacau seperti itu, tetapi cara termudah untuk tidak menyatukan kembali semua invarian Anda adalah dengan membuat simpul hantu di grafik Anda yang bertindak sebagai proxy kembali ke ayah inses.
sumber
Jadi, saya telah melakukan beberapa pekerjaan pada perangkat lunak tree keluarga. Saya pikir masalah yang Anda coba selesaikan adalah Anda harus dapat berjalan di pohon tanpa harus berada di loop yang tak terbatas - dengan kata lain, pohon tersebut harus bersifat asiklikal.
Namun, sepertinya Anda menegaskan bahwa hanya ada satu jalan antara seseorang dan salah satu leluhur mereka. Itu akan menjamin bahwa tidak ada siklus, tetapi terlalu ketat. Secara biologis, keturunan adalah grafik asiklik terarah (DAG). Kasing yang Anda miliki tentu saja kasing, tetapi hal semacam itu selalu terjadi di pohon yang lebih besar.
Misalnya, jika Anda melihat leluhur ke-2 yang Anda miliki pada generasi ke-n, jika tidak ada tumpang tindih, maka Anda akan memiliki lebih banyak leluhur pada 1000 AD daripada ada orang yang masih hidup. Jadi, harus ada tumpang tindih.
Namun, Anda juga cenderung mendapatkan siklus yang tidak valid, hanya data yang buruk. Jika Anda melintasi pohon, maka siklus harus ditangani. Anda dapat melakukan ini di setiap algoritma individu, atau saat memuat. Saya melakukannya dengan beban.
Menemukan siklus sejati di pohon dapat dilakukan dengan beberapa cara. Cara yang salah adalah menandai setiap leluhur dari individu yang diberikan, dan ketika melintasi, jika orang yang akan Anda tuju berikutnya sudah ditandai, maka potong tautannya. Ini akan memutuskan hubungan yang berpotensi akurat. Cara yang benar untuk melakukannya adalah mulai dari masing-masing individu, dan tandai setiap leluhur dengan jalan menuju individu itu. Jika jalur baru berisi jalur saat ini sebagai subpath, maka itu adalah siklus, dan harus diputus. Anda dapat menyimpan jalur sebagai vektor <bool> (MFMF, MFFFMF, dll.) Yang membuat perbandingan dan penyimpanan sangat cepat.
Ada beberapa cara lain untuk mendeteksi siklus, seperti mengirimkan dua iterator dan melihat apakah mereka pernah bertabrakan dengan tes subset, tetapi saya akhirnya menggunakan metode penyimpanan lokal.
Perhatikan juga bahwa Anda tidak perlu memutus tautan, Anda bisa mengubahnya dari tautan normal ke tautan 'lemah', yang tidak diikuti oleh beberapa algoritme Anda. Anda juga harus berhati-hati saat memilih tautan mana yang akan ditandai sebagai lemah; kadang-kadang Anda dapat mengetahui di mana siklus harus diputus dengan melihat informasi tanggal lahir, tetapi seringkali Anda tidak dapat menemukan apa pun karena begitu banyak data yang hilang.
sumber
Jawaban serius lain yang mengejek untuk pertanyaan konyol:
Jawaban sebenarnya adalah, gunakan struktur data yang sesuai. Silsilah manusia tidak dapat sepenuhnya diekspresikan menggunakan pohon murni tanpa siklus. Anda harus menggunakan semacam grafik. Juga, berbicaralah dengan seorang antropolog sebelum melangkah lebih jauh dengan ini, karena ada banyak tempat lain kesalahan serupa dapat dibuat dengan mencoba memodelkan silsilah, bahkan dalam kasus paling sederhana "perkawinan monogami patriarkal Barat."
Bahkan jika kita ingin mengabaikan hubungan tabu secara lokal seperti yang dibahas di sini, ada banyak cara yang benar-benar legal dan sama sekali tidak terduga untuk memperkenalkan siklus ke pohon keluarga.
Misalnya: http://en.wikipedia.org/wiki/Cousin_marriage
Pada dasarnya, pernikahan sepupu tidak hanya umum dan diharapkan, itu adalah alasan manusia beralih dari ribuan kelompok keluarga kecil ke populasi dunia sebesar 6 miliar. Tidak bisa dengan cara lain.
Sebenarnya ada sangat sedikit hal universal dalam hal silsilah, keluarga, dan garis keturunan. Hampir semua asumsi ketat tentang norma yang menunjukkan siapa bibi, atau siapa yang bisa menikahi siapa, atau bagaimana anak-anak dilegitimasi untuk tujuan pewarisan, dapat dikecewakan oleh beberapa pengecualian di suatu tempat di dunia atau sejarah.
sumber
Selain potensi implikasi hukum, tampaknya Anda perlu memperlakukan 'simpul' pada silsilah keluarga sebagai orang pendahulunya alih-alih mengasumsikan bahwa simpul tersebut dapat menjadi satu-satunya orang.
Mintalah simpul pohon menyertakan seseorang dan juga penerusnya - dan kemudian Anda dapat memiliki simpul lain yang lebih dalam di bawah pohon yang mencakup orang yang sama dengan penerus yang berbeda.
sumber
Beberapa jawaban telah menunjukkan cara untuk mempertahankan asersi / invarian, tetapi ini tampaknya seperti penyalahgunaan asersi / invarian. Penegasan untuk memastikan sesuatu yang seharusnya benar itu benar, dan invarian harus memastikan sesuatu yang tidak boleh berubah tidak berubah.
Apa yang Anda tegaskan di sini adalah bahwa hubungan inses tidak ada. Jelas mereka memang ada, jadi pernyataan Anda tidak valid. Anda dapat mengatasi pernyataan ini, tetapi bug sebenarnya ada di pernyataan itu sendiri. Pernyataan itu harus dihapus.
sumber
Silsilah keluarga Anda harus menggunakan hubungan terarah. Dengan cara ini Anda tidak akan memiliki siklus.
sumber
Data silsilah adalah siklik dan tidak cocok dengan grafik asiklik, jadi jika Anda memiliki pernyataan yang menentang siklus Anda harus menghapusnya.
Cara untuk menangani ini dalam tampilan tanpa membuat tampilan kustom adalah memperlakukan orangtua siklik sebagai orangtua "hantu". Dengan kata lain, ketika seseorang adalah ayah dan kakek dari orang yang sama, maka simpul kakek ditampilkan secara normal, tetapi simpul ayah diterjemahkan sebagai simpul "hantu" yang memiliki label sederhana seperti ("lihat kakek" ) dan menunjuk ke kakek.
Untuk melakukan perhitungan, Anda mungkin perlu meningkatkan logika untuk menangani grafik siklik sehingga sebuah simpul tidak dikunjungi lebih dari sekali jika ada siklus.
sumber
Yang paling penting adalah
avoid creating a problem
, jadi saya percaya Anda harus menggunakan hubungan langsung untuk menghindari siklus.Seperti @markmywords katakan, #sertakan "fritzl.h".
Akhirnya harus saya katakan
recheck your data structure
. Mungkin ada yang tidak beres di sana (mungkin daftar yang dwi-link memecahkan masalah Anda).sumber
Pernyataan tidak bertahan dari kenyataan
Biasanya pernyataan tidak bertahan dari kontak dengan data dunia nyata. Ini adalah bagian dari proses rekayasa perangkat lunak untuk memutuskan, dengan data mana yang ingin Anda tangani dan yang di luar jangkauan.
Grafik keluarga siklik
Mengenai "pohon" keluarga (sebenarnya itu adalah grafik yang lengkap, termasuk siklus), ada anekdot yang bagus:
Hal-hal menjadi lebih aneh, ketika Anda mempertimbangkan pengganti atau "ayah kebal" ke dalam akun.
Bagaimana menghadapinya
Tentukan siklus sebagai di luar cakupan
Anda dapat memutuskan bahwa perangkat lunak Anda tidak boleh menangani kasus yang jarang terjadi. Jika hal seperti itu terjadi, pengguna harus menggunakan produk yang berbeda. Ini membuat berurusan dengan kasus-kasus yang lebih umum jauh lebih kuat, karena Anda dapat mempertahankan lebih banyak pernyataan dan model data yang lebih sederhana.
Dalam hal ini, tambahkan beberapa fitur impor dan ekspor yang baik ke perangkat lunak Anda, sehingga pengguna dapat dengan mudah bermigrasi ke produk yang berbeda bila perlu.
Izinkan hubungan manual
Anda dapat mengizinkan pengguna untuk menambahkan hubungan manual. Hubungan ini bukan "warga negara kelas satu", yaitu perangkat lunak mengambil apa adanya, tidak memeriksa mereka dan tidak menangani mereka dalam model data utama.
Pengguna kemudian dapat menangani kasus yang jarang terjadi dengan tangan. Model data Anda akan tetap sederhana dan pernyataan Anda akan tetap bertahan.
Hati-hati dengan hubungan manual. Ada godaan untuk membuatnya benar-benar dapat dikonfigurasi dan karenanya membuat model data yang sepenuhnya dapat dikonfigurasi. Ini tidak akan berfungsi: Perangkat lunak Anda tidak akan skala, Anda akan mendapatkan bug aneh dan akhirnya antarmuka pengguna menjadi tidak dapat digunakan. Anti-pola ini disebut "soft coding" , dan "The WTF daily" penuh dengan contoh untuk itu.
Jadikan model data Anda lebih fleksibel, lewati asersi, uji invarian
Pilihan terakhir adalah membuat model data Anda lebih fleksibel. Anda harus melewati hampir semua asersi dan mendasarkan model data Anda pada grafik lengkap. Seperti yang ditunjukkan contoh di atas, mudah untuk menjadi kakek Anda sendiri, sehingga Anda bahkan dapat memiliki siklus.
Dalam hal ini, Anda harus menguji perangkat lunak Anda secara ekstensif. Anda harus melewati hampir semua pernyataan, jadi ada peluang bagus untuk bug tambahan.
Gunakan generator data uji untuk memeriksa kasus uji yang tidak biasa. Ada cek perpustakaan cepat untuk Haskell , Erlang atau C . Untuk Java / Scala ada ScalaCheck dan Nyaya . Satu ide tes adalah mensimulasikan populasi acak, biarkan kawin silang secara acak, lalu biarkan perangkat lunak Anda terlebih dahulu mengimpor dan kemudian mengekspor hasilnya. Harapannya adalah, bahwa semua koneksi dalam output juga dalam input dan sebaliknya.
Kasing, di mana properti tetap sama disebut invarian. Dalam hal ini, invarian adalah set "hubungan romantis" antara individu dalam populasi yang disimulasikan. Cobalah untuk menemukan invarian sebanyak mungkin dan mengujinya dengan data yang dihasilkan secara acak. Invarian dapat berfungsi, misalnya:
Atau mereka dapat bersifat teknis:
Dengan menjalankan tes simulasi, Anda akan menemukan banyak kasus sudut aneh. Memperbaikinya akan membutuhkan banyak waktu. Juga Anda akan kehilangan banyak optimasi, perangkat lunak Anda akan berjalan jauh lebih lambat. Anda harus memutuskan, apakah itu layak dan apakah ini dalam lingkup perangkat lunak Anda.
sumber
Alih-alih menghapus semua pernyataan, Anda masih harus memeriksa hal-hal seperti seseorang menjadi orang tuanya sendiri atau situasi tidak mungkin lainnya dan menyajikan kesalahan. Mungkin mengeluarkan peringatan jika tidak memungkinkan sehingga pengguna masih dapat mendeteksi kesalahan input umum, tetapi akan berfungsi jika semuanya benar.
Saya akan menyimpan data dalam vektor dengan bilangan bulat permanen untuk setiap orang dan menyimpan orang tua dan anak-anak di objek orang di mana int mengatakan adalah indeks dari vektor. Ini akan cukup cepat untuk dilakukan antar generasi (tetapi lambat untuk hal-hal seperti pencarian nama). Benda-benda akan berada di urutan kapan mereka dibuat.
sumber
Gandakan ayah (atau gunakan symlink / referensi).
Misalnya, jika Anda menggunakan basis data hierarkis:
sumber
ln -s
perintah tidak bekerja seperti itu; resolusi tautanFamily/Son/Father
akan dicariFamily/Son/Daughter/Father
dari bawahFamily/Son
, tempat tautan berada, bukan dari.
tempat Anda mengeluarkanln -s
perintah.