Перевод азбуки Морзе без пробелов - PullRequest
4 голосов
/ 02 декабря 2011

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

Вот вся информация о моих сообщениях.

  • Вывод будет английский
  • Всегда найдется перевод, который имеет смысл
  • Вот и пример сообщения -..-...-...-...-..-.-.-.-.-..-.-.-.-.-.-.-.-.-.-..-...-.
  • Длина сообщений не должна превышать 70 символов
  • Азбука Морзе была взята из более длинного потока, поэтому возможно, что первая или последняя группы могут быть обрезаны и, следовательно, не имеют действительного перевода

У кого-нибудь есть умное решение?

Ответы [ 3 ]

7 голосов
/ 04 декабря 2011

Это не простая проблема, потому что, как предположил Руах, есть много жизнеспособных предложений для данного сообщения.Например, «JACK AND JILL WENT UP THE HILL» имеет ту же кодировку, что и «JACK AND JILL WALK CHALELED».Так как это и грамматические предложения, и слова в каждом из них являются общими, не очевидно, как выбрать одну или другую (или любую другую из 40141055989476564163599 различных последовательностей английских слов, которые имеют ту же кодировку, что и это сообщение), не углубляясь в естественный языкобработка.

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

Следующие усовершенствования должны быть лучшей мерой того, насколько вероятно предложение: возможно, частоты слов, ложноположительные показатели в азбуке Морзе (например, «I» - этообщее слово, но оно часто встречается как часть других последовательностей последовательностей азбуки Морзе).Сложная часть будет формулировать хорошую функцию оценки, которая может быть выражена таким образом, чтобы ее можно было вычислить с помощью динамического программирования.

MORSE = dict(zip('ABCDEFGHIJKLMNOPQRSTUVWXYZ', [
    '.-', '-...', '-.-.', '-..', '.', '..-.', '--.', '....',
    '..', '.---', '-.-', '.-..', '--', '-.', '---', '.--.',
    '--.-', '.-.', '...', '-', '..-', '...-', '.--', '-..-',
    '-.--', '--..'
]))

# Read a file containing A-Z only English words, one per line.
WORDS = set(word.strip().upper() for word in open('dict.en').readlines())
# A set of all possible prefixes of English words.
PREFIXES = set(word[:j+1] for word in WORDS for j in xrange(len(word)))

def translate(msg, c_sep=' ', w_sep=' / '):
    """Turn a message (all-caps space-separated words) into morse code."""
    return w_sep.join(c_sep.join(MORSE[c] for c in word)
                      for word in msg.split(' '))

def encode(msg):
    """Turn a message into timing-less morse code."""
    return translate(msg, '', '')

def c_trans(morse):
    """Construct a map of char transitions.

    The return value is a dict, mapping indexes into the morse code stream
    to a dict of possible characters at that location to where they would go
    in the stream. Transitions that lead to dead-ends are omitted.
    """
    result = [{} for i in xrange(len(morse))]
    for i_ in xrange(len(morse)):
        i = len(morse) - i_ - 1
        for c, m in MORSE.iteritems():
            if i + len(m) < len(morse) and not result[i + len(m)]:
                continue
            if morse[i:i+len(m)] != m: continue
            result[i][c] = i + len(m)
    return result

def find_words(ctr, i, prefix=''):
    """Find all legal words starting from position i.

    We generate all possible words starting from position i in the
    morse code stream, assuming we already have the given prefix.
    ctr is a char transition dict, as produced by c_trans.
    """
    if prefix in WORDS:
        yield prefix, i
    if i == len(ctr): return
    for c, j in ctr[i].iteritems():
        if prefix + c in PREFIXES:
            for w, j2 in find_words(ctr, j, prefix + c):
                yield w, j2

def w_trans(ctr):
    """Like c_trans, but produce a word transition map."""
    result = [{} for i in xrange(len(ctr))]
    for i_ in xrange(len(ctr)):
        i = len(ctr) - i_ - 1
        for w, j in find_words(ctr, i):
            if j < len(result) and not result[j]:
                continue
            result[i][w] = j
    return result

def shortest_sentence(wt):
    """Given a word transition map, find the shortest possible sentence.

    We find the sentence that uses the entire morse code stream, and has
    the fewest number of words. If there are multiple sentences that
    satisfy this, we return the one that uses the smallest number of
    characters.
    """
    result = [-1 for _ in xrange(len(wt))] + [0]
    words = [None] * len(wt)
    for i_ in xrange(len(wt)):
        i = len(wt) - i_ - 1
        for w, j in wt[i].iteritems():
            if result[j] == -1: continue
            if result[i] == -1 or result[j] + 1 + len(w) / 30.0 < result[i]:
                result[i] = result[j] + 1 + len(w) / 30.0
                words[i] = w
    i = 0
    result = []
    while i < len(wt):
        result.append(words[i])
        i = wt[i][words[i]]
    return result

def sentence_count(wt):
    result = [0] * len(wt) + [1]
    for i_ in xrange(len(wt)):
        i = len(wt) - i_ - 1
        for j in wt[i].itervalues():
            result[i] += result[j]
    return result[0]

msg = 'JACK AND JILL WENT UP THE HILL'
print sentence_count(w_trans(c_trans(encode(msg))))
print shortest_sentence(w_trans(c_trans(encode(msg))))
0 голосов
/ 02 декабря 2011

Поддерживать 3 вещи: список слов до сих пор S, текущее слово до сих пор W и текущий символ C.

  • S должны быть только хорошими словами, например.'THE QUICK'
  • W должен быть действительным префиксом слова, например.['BRO']
  • C должен быть действительным префиксом некоторой буквы, например.'.-'

Теперь, учитывая новый символ, скажем, '-', мы расширяем C этим (в этом случае мы получаем '.--').Если C - полная буква (в данном случае это буква «W»), мы можем добавить ее в W или продолжить расширение буквы, добавив больше символов.Если мы расширим W, у нас будет возможность добавить его в S (если это допустимое слово) или продолжить его дальнейшее расширение.

Это поиск, но большинство путей быстро заканчиваются (как только выесли W не является допустимым префиксом любого слова, которое вы можете остановить, и как только C не станет префиксом любой буквы, вы можете остановить).

Чтобы сделать его более эффективным, вы можете использовать динамическое программирование дляизбегайте лишней работы и пытайтесь эффективно тестировать префиксы.

Как может выглядеть код?Пропуск функции is_word, которая проверяет, является ли строка английским словом, и is_word_prefix, которая проверяет, является ли строка началом любого допустимого слова, что-то вроде этого:

morse = {
    '.-': 'A',
    '-...': 'B',
    etc.
}

def is_morse_prefix(C):
    return any(k.startswith(C) for k in morse)

def break_words(input, S, W, C):
    while True:
        if not input:
            if W == C == '':
                yield S
            return
        i, input = input[0], input[1:]
        C += i
        if not is_morse_prefix(C):
            return
        ch = morse.get(C, None)
        if ch is None or not is_word_prefix(W + ch):
            continue
        for result in break_words(input, S, W + ch, ''):
            yield result
        if is_word(W + ch):
            for result in break_words(input, S + ' ' + W + ch, '', ''):
                yield result

for S in break_words('....--', [], '', ''):
    print S
0 голосов
/ 02 декабря 2011

Я не знаю, является ли это "умным", но я бы попробовал поиск в ширину (в отличие от поиска в глубину, неявного в идее регулярного выражения BRPocock). Предположим, что ваша строка выглядит следующим образом:

.---.--.-.-.-.--.-...---...-...-..
J   A C   K  A N D  J   I L   L

Вы начинаете в состоянии ('', 0) ('' - то, что вы до сих пор декодировали; 0 - ваша позиция в строке азбуки Морзе). Начиная с нулевой позиции, возможные начальные символы: . E, .- A, .-- W, .--- J и .---- 1. Итак, вставьте состояния ('E', 1), ('A', 2), ('W', 3), ('J', 4) и ('1', 5) в свою очередь. После отмены состояния ('E', 1) вы ставите в очередь состояния ('ET', 2), ('EM', 3) и ('EO', 4).

Теперь ваша очередь возможных состояний будет расти довольно быстро & mdash; оба из {., -} являются буквами, как и все {.., .-, -., --} и все {..., ..-, .-. , .--, -.., -.-, --., ---}, поэтому при каждом проходе ваше число состояний будет увеличиваться в минимум три & mdash; поэтому у вас должен быть какой-то механизм обратной связи с пользователем. В частности, вам нужно каким-то образом спросить вашего пользователя «Возможно ли, что эта строка начинается с EOS3AIOSF?», И если пользователь скажет «нет», вам нужно будет отбросить состояние ("EOS3AIOSF", 26) из своей очереди. Идеальным было бы предоставить пользователю графический интерфейс, который очень часто показывает все текущие состояния и позволяет ему / ей выбирать, с какими из них стоит продолжить. («Пользователь» также будет вами, конечно. В английском языке не хватает местоимений: если «вы» относится к программе, то какое местоимение относится к пользователю-программисту?!)

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