«Решатель анаграмм» основан на статистике, а не на словаре / таблице? - PullRequest
20 голосов
/ 16 апреля 2010

Моя проблема концептуально похожа на решение анаграмм, за исключением того, что я не могу просто использовать поиск по словарю. Я пытаюсь найти правдоподобные слова, а не настоящие.

Я создал N-граммовую модель (на данный момент N = 2) на основе букв в связке текста. Теперь, учитывая случайную последовательность букв, я хотел бы переставить их в наиболее вероятную последовательность в соответствии с вероятностями перехода. Я думал, что мне понадобится алгоритм Витерби , когда я начал это, но, как я смотрю глубже, алгоритм Витерби оптимизирует последовательность скрытых случайных величин на основе наблюдаемого результата. Я пытаюсь оптимизировать последовательность вывода.

Есть ли известный алгоритм для этого, о котором я могу прочитать? Или я на правильном пути с Витерби, и я просто не вижу, как его применить?

Обновление

Я добавил вознаграждение, чтобы попросить больше разобраться в этой проблеме. (Анализ, объясняющий, почему эффективный подход невозможен, другие эвристики / приближения, кроме имитации отжига и т. Д.)

Ответы [ 4 ]

6 голосов
/ 27 июля 2010

В качестве упражнения я написал простую реализацию цепей Маркова в MATLAB. В основном это буквенная вероятностная модель для генерации слов.

function mc = MC(numStates)
    N = numStates;
    PI = ones(1,N)/N;
    TR = ones(N,N)/N;
    mc = struct('logprob',@logprob, 'train',@train, ...
                'sample',@sample, 'sampleFiltered',@sampleFiltered);

    function train(seqs)
        PI = 1 + histc(cellfun(@(c)c(1), seqs)', 1:N); %#'
        TR = ones(N,N);
        for i=1:numel(seqs)
            ind = sub2ind([N N], seqs{i}(1:end-1), seqs{i}(2:end));
            TR = TR + reshape(histc(ind,1:N*N), [N N]);
        end
        PI = bsxfun(@rdivide, PI, sum(PI,2)+(sum(PI,2)==0));
        TR = bsxfun(@rdivide, TR, sum(TR,2)+(sum(TR,2)==0));
    end

    function seq = sample(len)
        seq = zeros(1,len);
        seq(1) = randsample(1:N, 1, true, PI);
        for t=2:len
            seq(t) = randsample(1:N, 1, true, TR(seq(t-1),:));
        end
    end

    function seq = sampleFiltered(allowed)
        len = numel(allowed);
        seq = zeros(1,len);
        seq(1) = randsample(allowed, 1, true, PI(allowed));
        allowed( find(allowed==seq(1),1,'first') ) = [];
        for t=2:len-1
            seq(t) = randsample(allowed, 1, true, TR(seq(t-1),allowed));
            allowed( find(allowed==seq(t),1,'first') ) = [];
        end
        seq(t) = allowed;
        seq = seq(seq~=0);
    end

    function LL = logprob(seq)
        LL = log(PI(seq(1))) + ...
             sum( log(TR(sub2ind([N N],seq(1:end-1),seq(2:end)))) );
    end
end

Нам понадобится текст для обучения модели. Мы используем «Чудесного волшебника страны Оз» из проекта Гутенберга.

%# read the text document
str = lower( urlread('http://www.gutenberg.org/files/55/55.txt') );
SP = char(32);                        %# delimiter (space)
str( ~isstrprop(str, 'alpha') ) = SP; %# replace non-letters with spaces
str( findstr(str, [SP SP]) ) = [];    %# consecutive spaces as one
idx = ( str == SP );                  %# location of spaces
df = diff([1 idx 1]);
len = find(df > 0) - find(df < 0);    %# length of each word
[seqs gn] = grp2idx( str(~idx)' );    %#' map letters to numbers starting from 1
seqs = mat2cell(seqs', 1, len)';      %# put each word in a separate cell
N = length(gn);                       %# A to Z

Наконец, мы используем модель для выборки случайных слов или слов из набора букв:

%# train Markov chain
mc = MC(N);
mc.train(seqs);

%# sample a random word
seq = mc.sample( randi([3 10]) );
fprintf('word = %s , logP(word)=%f\n', [gn{seq}], mc.logprob(seq))

%# sample a word from a set of letters
letters = lower('markovchains');
lettersIdx = cellfun(@(c) find(strcmp(c,gn)), cellstr(letters'));   %#'
seq = mc.sampleFiltered(lettersIdx);
fprintf('word = %s , logP(word)=%f\n', [gn{seq}], mc.logprob(seq))

Вот несколько примеров, сгенерированных из букв 'markovchains', а также логарифмическая вероятность слова, заданного моделью:

word = mivorancask , logP(word)=-29.610819
word = arknoamshiv , logP(word)=-32.496090
word = ancoramshik , logP(word)=-29.299897
word = orchisankav , logP(word)=-29.987204
word = avinchasorm , logP(word)=-27.178507
word = aronchaskim , logP(word)=-25.651964

Вы можете видеть, что хотя ни одно из слов не является правильным, оно все же лучше, чем просто случайная последовательность букв. Очевидно, что использовать только предыдущий символ для генерации следующего недостаточно, однако его можно легко распространить на более сложные случаи (N-грамм).

Хорошая особенность такого подхода заключается в том, что он не ограничен одним языком и может быть адаптирован к любому другому, просто передавая ему документы с вашего языка.

6 голосов
/ 23 июля 2010

Рассмотрим множество K букв как вершин в графе.

Добавьте направленные ребра, чтобы представить 2 грамма от каждой буквы всем остальным, с весами, соответствующими их вероятностям.

«Слово» - это путь через (полный, направленный) граф.

Вы ищете лучшее (наименее или наиболее взвешенное) "слово" (путь), которое использует все буквы (посещает все вершины).

Это проблема асимметричного коммивояжера . Это NP-полный. Я не думаю, что станет легче, если вы будете использовать N-граммы больше, чем N = 2, поэтому вряд ли вы найдете эффективный алгоритм, но дайте нам знать, если вы сделаете

(имитация отжига или что-то в этом роде, вероятно, и есть путь)

4 голосов
/ 16 апреля 2010

Если я правильно понимаю вашу проблему, вы ищете все перестановки букв в слове для того, который имеет наименьшее произведение вероятностей в 2 грамма.

Если ваше слово слишком длинное, чтобы просто перебрать все комбинации, я обнаружил, что алгоритмы стохастической оптимизации дают хорошие результаты за короткое время. Я (имея математическое образование) проделал некоторую работу над алгоритмом " Имитация отжига ", который, я думаю, хорошо подошел бы к вашей проблеме. И это довольно легко реализовать.

1 голос
/ 25 июля 2010

Вы также можете сделать это стохастически с марковской цепью. Для начала убедитесь, что ваша N-граммовая таблица содержит символ «начало слова»; затем найдите доступные переходы из этого состояния и отфильтруйте их так, чтобы они включали только доступные буквы из вашего пула, и выберите случайным образом среди них, используя взвешенные вероятности. Затем найдите переходы из состояния next , отфильтруйте их до все еще доступных букв и завершите, когда в пуле больше нет букв (или, если вы достигнете состояния, из которого вы не можете перейти из, вернитесь к началу и попробуйте снова).

На самом деле может оказаться полезным, что это больше случайно, чем некоторые из других доступных опций, а если оно слишком случайно, у вас есть возможность массирования вероятностей, или просто генерация некоторого числа n (скажем, 100) случайных слов, сортировка их по «вероятности», а затем случайный выбор из числа лучших m (возможно, 10), что дает вам относительно точный контроль над тем, являются ли слова, которые вы генерируете из любого пакета букв, более последовательными или случайными.

...