ナイツ関数(ボケの方)
Posted feedbacks - Nested
Flatten HiddenSqueak Smalltalk で。
単語のリストから文字数が似ていて最初と最後の文字が一致するものをピックアップし、該当がないときは元の単語を、複数ある時はランダムにチョイスして出力しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | | in wordList file |
in := 'ドウカク org ヘ ヨウコソ
コノ サイト ハ ダサレタ オダイ ヲ イカニ トクカ キソイアウ
プログラマ ノ タメノ コロシアム デス
トウコウ ヲ タメシテ ミタイ カタ ハ テスト
トリアエズ ナガメテ ミタイ カタ ハ ゲンゴ ノ イチラン ガ オススメ デス'.
wordList := OrderedCollection new.
#('25_10' '40_25' '55_40' '70_55') do: [:range |
file := FileStream fileNamed: 'fam', range, '.txt'.
file converter: (TextConverter newForEncoding: 'sjis').
file wantsLineEndConversion: true.
[file atEnd] whileFalse: [wordList addAll: (file nextLine subStrings: String tab)].
file close].
World findATranscript: nil.
in linesDo: [:line |
Transcript cr.
(line subStrings: ' ')
do: [:each |
| foundWords |
foundWords := wordList select: [:word |
(word size - each size) abs <= 1 and: [
word first = each first and: [word last = each last]]].
foundWords ifEmpty: [foundWords := foundWords copyWith: each].
Transcript show: foundWords atRandom]
separatedBy: [Transcript space]]
"=>
ドクヤク org ヘ ヨワミソ
コノ サトビト ハ ダサレタ オトガイ ヲ イトコニ トシワカ キュウロウ
プラズマ ノ タカドノ コロシアム デス
トクユウ ヲ タメシテ ミタイ カタ ハ テナント
トキワズ ナツバテ ミタイ カタ ハ ゲンゴ ノ イチミン ガ オツトメ デス "
|
ボケるのに「レーベンシュタイン距離( http://ja.wikipedia.org/wiki/%E3%83%AC%E3%83%BC%E3%83%99%E3%83%B3%E3%82%B7%E3%83%A5%E3%82%BF%E3%82%A4%E3%83%B3%E8%B7%9D%E9%9B%A2 )」を使ってみました。
入力はテキストファイルからで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | /*
* NightsFormula.java
*
*/
import java.io.*;
import java.util.*;
public class NightsFormula {
private static final double _MINSimilarityRate = 0.51;
public static void main(String[] args){
String[] wordList = getWordList( new String[]{"fam25_10.txt","fam40_25.txt","fam55_40.txt","fam70_55.txt"});
String[] srcList = getWordList(new String[]{"inputSample.txt"});
if(wordList == null || srcList == null){
System.out.println("word list or src list is NULL");
return;
}
StringBuffer outb = new StringBuffer();
for(int i = 0; i < srcList.length; i++){
StringBuffer sb = new StringBuffer();
sb.append(srcList[i]).append("\t");
for(int j = 0; j < wordList.length; j++){
double sr = getSimilarityRate(srcList[i], wordList[j]);
if(sr >=_MINSimilarityRate){
sb.append(wordList[j]).append("\t");
}
}
String[] cand = split(sb);
String sc = cand[(int)(cand.length*Math.random())];
outb.append(sc).append(" ");
}
System.out.println(outb.toString());
}
private static double getSimilarityRate(String str1, String str2){
int ld = getLevenshteinDistance(str1, str2);
int len = (str1.length() > str2.length() ? str1.length() : str2.length());
double result = ((double)(len - ld) / len);
return result;
}
private static int getLevenshteinDistance(String str1, String str2){
int[][] lArr = new int[str1.length()+1][str2.length()+1];
for(int i = 0; i < lArr.length; i++) lArr[i][0] = i;
for(int i = 0; i < lArr[0].length; i++) lArr[0][i] = i;
for(int i = 0; i < str1.length(); i++){
for(int j = 0; j < str2.length(); j++){
int cost = (str1.charAt(i) == str2.charAt(j)) ? 0 : 1;
lArr[i+1][j+1] = getMinimum( lArr[i][j+1] +1, lArr[i+1][j] +1, lArr[i][j] +cost );
}
}
return lArr[str1.length()][str2.length()];
}
private static int getMinimum(int a, int b, int c){
int m = a < b ? a : b;
return m < c ? m : c;
}
private static String[] getWordList(String[] filenames){
FileReader fr = null;
BufferedReader in = null;
StringBuffer sb = new StringBuffer();
for(int i = 0; i < filenames.length; i++){
try{
in = new BufferedReader(fr = new FileReader(filenames[i]));
String line = null;
while((line=in.readLine())!= null){
sb.append(line).append("\t");
}
}catch(Throwable t){
t.printStackTrace();
return null;
}finally{
if(in != null){ try{ in.close(); }catch(Throwable t){ ; }finally{ in = null;} }
if(fr != null){ try{ fr.close(); }catch(Throwable t){ ; }finally{ fr = null;} }
}
}
return split(sb);
}
private static String[] split(StringBuffer sb){
StringTokenizer st = new StringTokenizer(sb.toString(), " \t");
int len = st.countTokens();
String[] wlist = new String[len];
for(int i = 0; i < len; i++){
wlist[i] = st.nextToken();
}
return wlist;
}
}
|
元ネタがわからないので、外してたらすみません。 コード欄が1つなのでつなげていますが、mkdic.plとnaitsu.plの 2つのプログラムからなります。 mkdic.plは、事前にデータを作るプログラムです。 mecabのipadicのソースに含まれるNoun.csvをカレントディレクトリ に置いて実行します。 Noun.csvにある60,477語の総当たり(3,657,407,052通り)で、 読みのLevenshtein距離が1になる組み合わせを求め、DB化します。 # 後から考えると、1操作で変換できる組み合わせだけを抜き出す # (1操作以上は計算しない)のが効率的でしたが、それに気が # ついたのは1日経過後ぐらい。 naitsu.plは、引数のファイルまたは標準入力から読み込んだ テキストをText::MeCabで分解して、単語がmkdic.plで作った DBにあればランダムに置きかえます。 $ perl mkdic.pl (この間3〜4日ぐらい) $ cat sample.txt このサイトは出されたお題をいかに解くか競い合う、プログラマのためのコロシアムです。 $ perl naitsu.pl sample.txt この細部は出されたお蛇尾をいかに解くか競い合う、プログラムのまめのコロシアムです。 $ perl naitsu.pl sample.txt この最多は出されたおだてをいかに解くか競い合う、プログラマーのタネのコロシアムです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | #!/usr/bin/perl
# mkdic.pl
use strict;
use warnings;
use Encode;
use Text::Levenshtein qw(fastdistance);
use DBI;
my $source = './Noun.csv';
my $source_enc = 'euc-jp';
my $dbfile = './ls1.sqlite';
my $db_enc = 'utf-8';
sub readin {
my @list;
open my $fh, '<', $source or die;
while (<$fh>) {
my ($word, $yomi) = (split(/,/, decode($source_enc, $_)))[0, 11];
push @list, { word => $word, yomi => $yomi };
}
close $fh;
\@list;
}
sub make_db {
my $dbh = shift;
$dbh->do('CREATE TABLE ls1 (key string, val string)');
}
sub make_ls1 {
my $dbh = shift;
my $list_ref = shift;
my @list = @{$list_ref};
my $sth = $dbh->prepare('INSERT INTO ls1 VALUES (?,?)');
while (@list) {
my $word1 = shift @list;
for my $word2 (@list) {
if (fastdistance($word1->{yomi}, $word2->{yomi}) == 1) {
my $word1_enc = encode($db_enc, $word1->{word});
my $word2_enc = encode($db_enc, $word2->{word});
$sth->execute($word1_enc, $word2_enc);
$sth->execute($word2_enc, $word1_enc);
print ("$word1_enc:$word2_enc\n");
}
}
}
}
my $list_ref = readin();
unlink $dbfile if (-f $dbfile);
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", '', '',
{ RaiseError => 1, AutoCommit => 0 });
make_db($dbh);
make_ls1($dbh, $list_ref);
$dbh->commit;
$dbh->disconnect();
#!/usr/bin/perl
# naitsu.pl
use strict;
use warnings;
use DBI;
use Text::MeCab;
my $dbfile = './ls1.sqlite';
sub get_ls1s {
my ($sth, $word) = @_;
$sth->execute($word);
my ($word2) = $sth->fetchrow_array or return;
return $word2;
}
my $text = do { local $/; <ARGV>; };
my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", '', '',);
my $sth = $dbh->prepare(
"SELECT val FROM ls1 WHERE key=? ORDER BY random() LIMIT 1" );
my $mecab = Text::MeCab->new();
my $node = $mecab->parse($text);
for (; $node; $node = $node->next) {
my $word = $node->surface;
if (my $naitsu = get_ls1s($sth, $word)) {
print $naitsu;
} else {
print $word;
}
}
print "\n";
$sth->finish;
undef $sth;
$dbh->disconnect;
|
JavaとSmalltalkの投稿を参考に書いてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #! /usr/bin/ruby -Ks
class LevenshteinDistance
def self.distance(a, b)
table = Array.new(a.split('').size + 1) do |i|
i == 0 ? (0..b.split('').size).to_a : Array.new(b.split('').size).unshift(i)
end
(1..table.size - 1).each do |i|
(1..table[i].size - 1).each do |j|
table[i][j] = [
table[i - 1][j] + 1,
table[i][j - 1],
table[i - 1][j - 1] + (a.split('')[i - 1] == b.split('')[j - 1] ? 0 : 1)
].min
end
end
table.last.last
end
end
class Knights
attr_accessor :dictionary
def initialize(dict_file)
self.dictionary = []
File.open(dict_file) { |f| f.each { |line| self.dictionary.concat(line.split(/\s+/)) } }
self
end
def translate(source)
source.split(/\s+/).map do |w|
list = self.dictionary.select { |d| yield(w, d) }.unshift(w)
list[rand(list.size)]
end
end
end
source = <<EOS
ドウカク org ヘ ヨウコソ
コノ サイト ハ ダサレタ オダイ ヲ イカニ トクカ キソイアウ
プログラマ ノ タメノ コロシアム デス
トウコウ ヲ タメシテ ミタイ カタ ハ テスト
トリアエズ ナガメテ ミタイ カタ ハ ゲンゴ ノ イチラン ガ オススメ デス
EOS
knights = Knights.new("./path/to/word_list.txt")
puts "入力:\n#{source}"
puts "出力:"
source.each_line do |line|
puts(
knights.translate(line) do |w, d|
LevenshteinDistance.distance(w, d).to_f / [w.split('').size, d.split('').size].sort.first < 0.4
end.join(" ")
)
end
puts "出力:"
source.each_line do |line|
puts(
knights.translate(line) do |w, d|
(w.split('').size - d.split('').size).abs <= 1 && w.split('').first == d.split('').first && w.split('').last == d.split('').last
end.join(" ")
)
end
|
レーベンシュタイン距離の計算処理に間違いがあったので修正したものを。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | #! /usr/bin/ruby -Ks
class LevenshteinDistance
def self.distance(a, b)
table = Array.new(a.split('').size + 1) do |i|
i == 0 ? (0..b.split('').size).to_a : Array.new(b.split('').size).unshift(i)
end
(1..table.size - 1).each do |i|
(1..table[i].size - 1).each do |j|
table[i][j] = [
table[i - 1][j] + 1,
table[i][j - 1] + 1,
table[i - 1][j - 1] + (a.split('')[i - 1] == b.split('')[j - 1] ? 0 : 1)
].min
end
end
table.last.last
end
end
class Knights
attr_accessor :dictionary
def initialize(dict_file)
self.dictionary = []
File.open(dict_file) { |f| f.each { |line| self.dictionary.concat(line.split(/\s+/)) } }
self
end
def translate(source)
source.split(/\s+/).map do |w|
(list = self.dictionary.select { |d| yield(w, d) }).empty? ? w : list[rand(list.size)]
end
end
end
source = <<EOS
ドウカク org ヘ ヨウコソ
コノ サイト ハ ダサレタ オダイ ヲ イカニ トクカ キソイアウ
プログラマ ノ タメノ コロシアム デス
トウコウ ヲ タメシテ ミタイ カタ ハ テスト
トリアエズ ナガメテ ミタイ カタ ハ ゲンゴ ノ イチラン ガ オススメ デス
EOS
puts "入力:\n#{source}"
puts "出力:"
knights = Knights.new("/path/to/wordlist.txt")
source.each_line do |line|
puts(
knights.translate(line) do |w, d|
LevenshteinDistance.distance(w, d).to_f / [w.split('').size, d.split('').size].max < 0.5
end.join(" ")
)
end
puts "出力:"
source.each_line do |line|
puts(
knights.translate(line) do |w, d|
(w.split('').size - d.split('').size).abs <= 1 && w.split('').first == d.split('').first && w.split('').last == d.split('').last
end.join(" ")
)
end
|
Scalaで。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | import java.io.{File,FileInputStream,InputStreamReader,BufferedReader}
import scala.util.Random
object LevenshteinDistance {
def distance(a:String, b:String) = {
var t:List[List[Int]] = List.range(a.size, -1, -1).foldLeft(List[List[Int]]())(
(r,i) => i match {
case 0 => List.range(0, b.size + 1)::r
case _ => (i::List.make(b.size, 0))::r
}
)
1.to(t.size - 1).foreach(i =>
1.to(t(i).size - 1).foreach(j =>
t = t.take(i) ++
List(
t(i).take(j) ++
List(
(t(i - 1)(j) + 1)
.min(t(i)(j - 1) + 1)
.min(
t(i - 1)(j - 1) +
((a(i - 1) == b(j - 1)) match { case true => 0; case _ => 1 })
)
) ++
t(i).takeRight(t(i).size - j - 1)
) ++
t.takeRight(t.size - i - 1)
)
)
t.last.last
}
}
class CKnights(f:String, e:String) {
var d:List[String] = List()
val i:BufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(f), e))
val r:Random = new Random
while(i.ready) {
val l:String = i.readLine
if (l != "") d = d:::l.split("\\s+").toList
}
i.close
def translate(s:String)(j:(String,String)=>Boolean):List[String] =
s.split("\\s+").foldRight(List[String]())((w,k) =>
(d.filter(e => j(w, e)) match {
case t if t.isEmpty => w
case t => t(r.nextInt(t.size))
})::k
)
}
object Knights {
def source:List[String] = readLine match {
case null => List()
case l => l::source
}
def main(args:Array[String]):Unit = args.size match {
case a if a != 1 => println("usage: Knights DICTFILE")
case _ => {
var s:List[String] = source
var k:CKnights = new CKnights(args.first, "Shift_JIS")
println("入力:")
s.foreach(l => println(l))
println("出力:")
s.foreach(l => println(
k.translate(l)((w, e) =>
LevenshteinDistance.distance(w, e).toFloat / w.size.max(e.size) < 0.5
).mkString(" ")
)
)
println("出力:")
s.foreach(l => println(
k.translate(l)((w, e) =>
Math.abs(w.size - e.size) <= 1 && w.first == e.first && w.last == e.last
).mkString(" ")
)
)
}
}
}
|
Java版でレーベンシュタイン距離を使っていたのに触発されたRuby版に触発されて、同じようなものを書いてみました。if-else式のために、Python2.5以上です。
Java版そのままだと、総あたり検索していて遅かったので、先頭文字が一致する単語のみに絞ることにしました。あと、候補が複数ある場合はランダムに選択するようにしています。
ちなみに単語辞書は毎回元サイトから拾っているので、オフラインでは動きません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | #!coding: utf-8
import urllib, random
def download_words():
prefix = "http://www.ais.riec.tohoku.ac.jp/lab/wordlist/"
files = ["fam70_55.txt", "fam55_40.txt",
"fam40_25.txt", "fam25_10.txt"]
words = []
for f in files:
fd = urllib.urlopen(prefix + f)
for line in fd:
if line.strip() != "":
words.extend(line.strip().decode("sjis").split("\t"))
fd.close()
return words
allwords = download_words()
def Levenshtein_distance(word0, word1):
d = dict()
for i in range(len(word0) + 1): d[(i,0)] = i
for j in range(len(word1) + 1): d[(0,j)] = j
for i in range(1, len(word0) + 1):
for j in range(1, len(word1) + 1):
cost = 0 if word0[i-1] == word1[j-1] else 1
d[(i,j)] = min(d[(i-1,j)]+1, d[(i,j-1)]+1, d[(i-1,j-1)]+cost)
return d[(len(word0), len(word1))]
def similarity(word0, word1):
l = max(len(word0), len(word1))
return float(l - Levenshtein_distance(word0, word1)) / l
def find_alt_word(word):
alt_words = filter(
lambda alt: alt[0] == word[0] and similarity(word, alt) > 0.5,
allwords)
)
return random.choice(alt_words) if alt_words else word
def knights(text):
return u" ".join([find_alt_word(w) for w in text.split(u" ")])
print knights(u'''ドウカク org ヘ ヨウコソ
コノ サイト ハ ダサレタ オダイ ヲ イカニ トクカ キソイアウ
プログラマ ノ タメノ コロシアム デス
トウコウ ヲ タメシテ ミタイ カタ ハ テスト
トリアエズ ナガメテ ミタイ カタ ハ ゲンゴ ノ イチラン ガ オススメ デス'''
).encode("sjis")
|
なるべく面白くボケる様に調整してみましたが、その所為でマジックナンバー溢れるヘンなコードになってしまいました。 あと一々単語データ全部について編集距離を計算してるので、Ruby1.8.x系だと遅くてお話になりません。なんともはや……。 -- ドウヤク org ヘ ヨワミソ コノ サイト ハ ダサレタ オダイ ヲ イチニン トクカ キソイアウ プログラマ ノ タメノ コロシヤ デス トウコウ ヲ タメシテ ミタイ カタ ハ テスト トリアエズ ナガソデ ミタマヤ カタ ハ ゲンタツ ノ イモバン ガ オツトメ デス
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | if RUBY_VERSION >= "1.9"
class Array
def choice
self[ rand(self.size) ]
end
end
end
def levenstein(a, b)
ld = Array.new(a.size+1){ Array.new(b.size+1){0} }
0.upto(a.size){|i|
ld[i][0] = i
}
0.upto(b.size){|i|
ld[0][i] = i
}
1.upto(a.size){|i|
1.upto(b.size){|j|
t = a[i-1] == b[i-1] ? 0 : 1
ld[i][j] = [ld[i-1][j]+1, ld[i][j-1]+1, ld[i-1][j-1] + t].min
}
}
return ld[-1][-1]
end
DIC = File.read("words.dat").encode("UTF-8").split(/\s+/m)
def knights(text)
text.each_line.map{|ln|
ln.split(" ").map{|wd|
DIC.find_all{|w2| levenstein(wd, w2)*15/(wd.size+w2.size) < rand(6) && wd[0] == w2[0]}.choice || wd
}.join(" ")
}.join($/)
end
if $0 == __FILE__
puts knights($<.read)
end
|
javaの解法を参考にさせていただきました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #!/usr/bin/env groovy
final MIN_SIMILARITY_RATE = 0.51
def wordList = []
["fam25_10.txt", "fam40_25.txt", "fam55_40.txt", "fam70_55.txt"].each{
Collections.addAll(wordList, new File(it).text.split(/\s/))
}
def file = new File("inputSample.txt")
assert wordList
assert file?.exists()
println file.text.replaceAll(/(?m)\S+/){ word ->
def convertList = [ word, *wordList.findAll{ other ->
getSimilarityRate(word, other) >= MIN_SIMILARITY_RATE
} ]
Collections.shuffle(convertList)
convertList[0]
}
double getSimilarityRate(String str1, String str2){
def len1 = str1.size()
def len2 = str2.size()
def matrix = new int[len1+1][len2+1]
for(int i = 0; i < matrix.size(); i++){
matrix[i][0] = i
}
for(int i = 0; i < matrix[0].size(); i++){
matrix[0][i] = i
}
for(int i = 0; i < len1; i++){
for(int j = 0; j < len2; j++){
int cost = (str1[i] == str2[j]) ? 0 : 1
matrix[i+1][j+1] = [matrix[i][j+1] +1, matrix[i+1][j] +1, matrix[i][j] +cost].min()
}
}
def longer = [len1, len2].max()
(longer - matrix[len1][len2])/longer
}
|




syat
#8549()
[
Other
]
Rating-2/2=-1.00
(ナベアツ算を見てて思いつきました)
入出力の方法は標準入出力や引数・戻り値など、扱いやすい方法でかまいません。
文字単位でランダムに間違えても面白くないので、単語のリストから似た単語の候補を探すようにしてください。英単語でもOKです。単語のリストは参考ページからダウンロードしたものを加工して利用すると良いと思います。(4000個あります)
結果がつまらなくても構いませんが、面白いボケをうむ工夫があると良いです。
※人が考えたボケは求めてませんよ!
入力の例として「どう書く?org」の前文をお借りしました。ご自分でヤホーで調べたりして手ごろな文章を見つけて下しあ。
see: 日本語単語リスト
Rating-2/2=-1.00-0+
[ reply ]