Masukan: "tableapplechairtablecupboard..."
banyak kata
Algoritma apa yang efisien untuk membagi teks seperti itu ke dalam daftar kata dan mendapatkan:
Keluaran: ["table", "apple", "chair", "table", ["cupboard", ["cup", "board"]], ...]
Hal pertama yang terlintas dalam pikiran adalah menelusuri semua kemungkinan kata (dimulai dengan huruf pertama) dan menemukan kata terpanjang mungkin, lanjutkan dari position=word_position+len(word)
NB
Kami memiliki daftar semua kemungkinan kata.
Kata "cupboard" bisa "cup" dan "board", pilih terpanjang.
Bahasa: python, tetapi yang utama adalah algoritmanya sendiri.
['able', 'air', 'apple', 'boa', 'boar', 'board', 'chair', 'cup', 'cupboard', 'ha', 'hair', 'lea', 'leap', 'oar', 'tab', 'table', 'up']
Jawaban:
Algoritme yang naif tidak akan memberikan hasil yang baik jika diterapkan pada data dunia nyata. Berikut adalah algoritme 20 baris yang memanfaatkan frekuensi kata relatif untuk memberikan hasil yang akurat untuk teks kata nyata.
(Jika Anda menginginkan jawaban atas pertanyaan awal Anda yang tidak menggunakan frekuensi kata, Anda perlu memperjelas apa yang sebenarnya dimaksud dengan "kata terpanjang": apakah lebih baik memiliki kata 20 huruf dan sepuluh kata 3 huruf, atau lebih baik memiliki lima kata 10 huruf? Setelah Anda menentukan definisi yang tepat, Anda hanya perlu mengubah definisi baris
wordcost
untuk mencerminkan arti yang dimaksudkan.)Ide
Cara terbaik untuk melanjutkan adalah memodelkan distribusi keluaran. Perkiraan pertama yang baik adalah menganggap semua kata terdistribusi secara independen. Maka Anda hanya perlu mengetahui frekuensi relatif dari semua kata. Masuk akal untuk mengasumsikan bahwa mereka mengikuti hukum Zipf, yaitu kata dengan peringkat n dalam daftar kata memiliki probabilitas kira-kira 1 / ( n log N ) di mana N adalah jumlah kata dalam kamus.
Setelah Anda memperbaiki model, Anda dapat menggunakan pemrograman dinamis untuk menyimpulkan posisi spasi. Kalimat yang paling mungkin adalah kalimat yang memaksimalkan produk dari probabilitas setiap kata, dan mudah untuk menghitungnya dengan pemrograman dinamis. Alih-alih langsung menggunakan probabilitas, kami menggunakan biaya yang didefinisikan sebagai logaritma dari kebalikan dari probabilitas untuk menghindari luapan.
Kode
yang dapat Anda gunakan dengan
Hasil
Saya menggunakan kamus 125k kata yang cepat dan kotor yang saya kumpulkan dari sebagian kecil Wikipedia.
Seperti yang Anda lihat, ini pada dasarnya tanpa cela. Bagian terpenting adalah memastikan daftar kata Anda dilatih untuk korpus yang mirip dengan apa yang sebenarnya akan Anda hadapi, jika tidak, hasilnya akan sangat buruk.
Optimasi
Implementasinya menghabiskan sejumlah waktu dan memori linier, sehingga cukup efisien. Jika Anda membutuhkan percepatan lebih lanjut, Anda dapat membuat pohon sufiks dari daftar kata untuk mengurangi ukuran kumpulan kandidat.
Jika Anda perlu memproses string berurutan yang sangat besar, sebaiknya pisahkan string tersebut untuk menghindari penggunaan memori yang berlebihan. Misalnya, Anda dapat memproses teks dalam blok 10.000 karakter ditambah margin 1000 karakter di kedua sisi untuk menghindari efek batas. Ini akan meminimalkan penggunaan memori dan hampir pasti tidak akan berpengaruh pada kualitas.
sumber
pip install wordninja
words.txt
contains "comp": `` `$ grep" ^ comp $ "words.txt comp` `dan diurutkan menurut abjad. kode ini mengasumsikan itu diurutkan dalam penurunan frekuensi kemunculan (yang umum untuk daftar n-gram seperti ini). jika Anda menggunakan daftar yang diurutkan dengan benar, string Anda akan baik-baik saja: `` >>> wordninja.split ('namethecompanywherebonniewasemployedwhenwestarteddating') ['name', 'the', 'company', 'where', 'bonnie', ' adalah ',' dipekerjakan ',' kapan ',' kita ',' mulai ',' berkencan '] ``Berdasarkan karya luar biasa di jawaban atas , saya telah membuat
pip
paket agar mudah digunakan.Untuk menginstal, jalankan
pip install wordninja
.Satu-satunya perbedaan kecil. Ini mengembalikan a
list
daripada astr
, berfungsi dipython3
, itu termasuk daftar kata dan membagi dengan benar bahkan jika ada karakter non-alfa (seperti garis bawah, tanda hubung, dll).Terima kasih sekali lagi kepada Manusia Generik!
https://github.com/keredson/wordninja
sumber
Berikut solusi menggunakan pencarian rekursif:
hasil
sumber
Menggunakan struktur data trie , yang menyimpan daftar kemungkinan kata, tidaklah terlalu rumit untuk melakukan hal berikut:
sumber
"tableprechaun"
kemudian harus dipisahkan setelahnya"tab"
."tableprechaun"
kecocokan terlama dari awal adalah"table"
, pergi"prechaun"
, yang tidak dapat dipisahkan menjadi kata-kata kamus. Jadi, Anda harus mengambil pertandingan yang lebih pendek"tab"
sehingga Anda memiliki file"leprechaun"
.Solusi Unutbu cukup dekat tetapi saya menemukan kodenya sulit untuk dibaca, dan itu tidak memberikan hasil yang diharapkan. Solusi Generic Human memiliki kekurangan yaitu membutuhkan frekuensi kata. Tidak sesuai untuk semua kasus penggunaan.
Berikut solusi sederhana menggunakan algoritma Divide and Conquer .
find_words('cupboard')
akan kembali['cupboard']
daripada['cup', 'board']
(dengan asumsi itucupboard
,cup
danboard
berada dalam kamus)find_words('charactersin')
bisa kembali['characters', 'in']
atau mungkin akan kembali['character', 'sin']
(seperti yang terlihat di bawah). Anda dapat dengan mudah memodifikasi algoritme untuk mengembalikan semua solusi optimal.Kode:
Ini akan memakan waktu sekitar 5 detik pada mesin 3GHz saya:
sumber
Jawabannya oleh https://stackoverflow.com/users/1515832/generic-human sangat bagus. Tetapi implementasi terbaik dari ini yang pernah saya lihat ditulis sendiri oleh Peter Norvig dalam bukunya 'Beautiful Data'.
Sebelum saya menempelkan kodenya, izinkan saya menjelaskan mengapa metode Norvig lebih akurat (meskipun sedikit lebih lambat dan lebih lama dalam hal kode).
1) Datanya sedikit lebih baik - baik dalam hal ukuran dan presisi (ia menggunakan jumlah kata daripada peringkat sederhana) 2) Lebih penting lagi, logika di balik n-gram yang benar-benar membuat pendekatannya begitu akurat .
Contoh yang dia berikan dalam bukunya adalah masalah pemisahan string 'sitdown'. Sekarang metode pemisahan string non-bigram akan mempertimbangkan p ('sit') * p ('down'), dan jika ini kurang dari p ('sitdown') - yang akan sering terjadi - TIDAK akan terpecah itu, tapi kami menginginkannya (sebagian besar waktu).
Namun ketika Anda memiliki model bigram, Anda dapat menilai p ('sit down') sebagai bigram vs p ('sitdown') dan yang pertama menang. Pada dasarnya, jika Anda tidak menggunakan bigram, ini memperlakukan probabilitas kata-kata yang Anda pisahkan sebagai independen, yang tidak terjadi, beberapa kata lebih mungkin muncul satu demi satu. Sayangnya itu juga kata-kata yang sering menempel bersama dalam banyak kasus dan membingungkan splitter.
Berikut link ke datanya (data untuk 3 masalah terpisah dan segmentasi hanya satu. Silakan baca bab untuk detailnya): http://norvig.com/ngrams/
dan ini tautan ke kode: http://norvig.com/ngrams/ngrams.py
Tautan ini sudah aktif beberapa saat, tetapi saya akan menyalin dan menempelkan bagian segmentasi kode di sini
sumber
RuntimeError: maximum recursion depth exceeded in cmp
Berikut adalah jawaban yang diterima yang diterjemahkan ke JavaScript (membutuhkan node.js, dan file "wordninja_words.txt" dari https://github.com/keredson/wordninja ):
sumber
Jika Anda mengkompilasi daftar kata menjadi DFA (yang akan sangat lambat), maka waktu yang dibutuhkan untuk mencocokkan masukan akan sebanding dengan panjang string (sebenarnya, hanya sedikit lebih lambat daripada hanya mengulang string).
Ini secara efektif adalah versi yang lebih umum dari algoritma trie yang telah disebutkan sebelumnya. Saya hanya menyebutkannya jika tidak lengkap - hingga saat ini, belum ada implementasi DFA yang bisa Anda gunakan. RE2 akan berfungsi, tetapi saya tidak tahu apakah pengikatan Python memungkinkan Anda menyesuaikan seberapa besar Anda mengizinkan DFA sebelum hanya membuang data DFA yang dikompilasi dan melakukan pencarian NFA.
sumber
Sepertinya kemunduran yang cukup biasa akan dilakukan. Mulailah dari awal string. Pindai sampai Anda mendapatkan sepatah kata pun. Kemudian, panggil fungsi tersebut di string lainnya. Fungsi mengembalikan "salah" jika memindai sepenuhnya ke kanan tanpa mengenali satu kata pun. Jika tidak, kembalikan kata yang ditemukan dan daftar kata yang dikembalikan oleh panggilan rekursif.
Contoh: "tableapple". Menemukan "tab", lalu "leap", tetapi tidak ada kata dalam "ple". Tidak ada kata lain dalam "leapple". Menemukan "tabel", lalu "aplikasi". "le" bukan sebuah kata, jadi apel mencoba, mengenali, kembali.
Untuk mendapatkan waktu yang lebih lama, lanjutkan, hanya mengeluarkan (daripada mengembalikan) solusi yang benar; kemudian, pilih yang optimal dengan kriteria apa pun yang Anda pilih (maxmax, minmax, average, dll.)
sumber
Berdasarkan solusi unutbu, saya telah menerapkan versi Java:
Memasukkan:
"tableapplechairtablecupboard"
Keluaran:
[table, apple, chair, table, cupboard]
Memasukkan:
"tableprechaun"
Keluaran:
[tab, leprechaun]
sumber
Untuk bahasa Jerman ada CharSplit yang menggunakan pembelajaran mesin dan berfungsi cukup baik untuk string beberapa kata.
https://github.com/dtuggener/CharSplit
sumber
Memperluas saran @ miku untuk menggunakan a
Trie
, append-onlyTrie
relatif mudah diterapkan dipython
:Kami kemudian dapat membangun
Trie
kamus berbasis dari sekumpulan kata:Yang akan menghasilkan pohon yang terlihat seperti ini (
*
menunjukkan awal atau akhir kata):Kita dapat memasukkan ini ke dalam solusi dengan menggabungkannya dengan heuristik tentang cara memilih kata. Misalnya kita dapat memilih kata yang lebih panjang daripada kata yang lebih pendek:
Kita bisa menggunakan fungsi ini seperti ini:
Karena kami mempertahankan posisi kami di
Trie
saat kami mencari lagi dan kata-kata lagi, kami melintasitrie
paling banyak sekali per solusi yang mungkin (bukan2
kali untukpeanut
:pea
,peanut
). Hubungan pendek terakhir menyelamatkan kita dari berjalan dengan bijak melalui tali dalam kasus terburuk.Hasil akhirnya hanya sedikit dari inspeksi:
Manfaat dari solusi ini adalah kenyataan bahwa Anda mengetahui dengan sangat cepat jika ada kata yang lebih panjang dengan awalan tertentu, yang menghemat kebutuhan untuk menguji kombinasi urutan secara menyeluruh terhadap kamus. Itu juga membuat untuk sebuah
unsolvable
jawaban relatif murah untuk implementasi lain.Kelemahan dari solusi ini adalah jejak memori yang besar untuk biaya
trie
dan biaya pembuatan ditrie
muka.sumber
Jika Anda memiliki daftar lengkap kata-kata yang terkandung dalam string:
word_list = ["table", "apple", "chair", "cupboard"]
Menggunakan pemahaman daftar untuk mengulang daftar untuk menemukan kata dan berapa kali kata itu muncul.
Fungsi mengembalikan
string
output kata-kata dalam urutan daftartable table apple chair cupboard
sumber
Terima kasih banyak atas bantuannya di https://github.com/keredson/wordninja/
Kontribusi kecil yang sama di Jawa dari sisi saya.
Metode publik
splitContiguousWords
dapat disematkan dengan 2 metode lain di kelas yang memiliki ninja_words.txt di direktori yang sama (atau dimodifikasi sesuai pilihan pembuat kode). Dan metodesplitContiguousWords
tersebut dapat digunakan untuk tujuan tersebut.sumber
public
metode ini menerima kalimat jenisString
yang dibagi berdasarkan tingkat pertama dengan regex. Dan untuk daftarnyaninja_words
tersedia untuk diunduh dari git repo.Ini akan membantu
sumber
Anda perlu mengidentifikasi kosakata Anda - mungkin daftar kata gratis apa pun bisa digunakan.
Setelah selesai, gunakan kosakata itu untuk membangun pohon sufiks, dan cocokkan aliran masukan Anda dengan itu: http://en.wikipedia.org/wiki/Suffix_tree
sumber