Получить строку, которая является срединной точкой между двумя другими строками - программирование
Подтвердить что ты не робот

Получить строку, которая является срединной точкой между двумя другими строками

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

Предпочтительно, код будет в Python.

Справочная информация:

Это кажется простой проблемой на поверхности, но я как бы борюсь с ней:

  • Очевидно, что средняя точка между "A" и "C" будет "B".
  • При кодировании base64, средняя точка между "A" и "B", вероятно, будет "Ag"
  • С кодировкой UTF-8 я не уверен, какой будет действительная средняя точка, потому что средний символ выглядит как управляющий символ: U+0088 c2 88 <control>

Практическое применение:

Причина, по которой я спрашиваю, заключается в том, что я надеялся написать алгоритм типа map-reduce, чтобы читать все записи из нашей базы данных и обрабатывать их. Первичными ключами в базе данных являются строки с кодировкой UTF-8 со случайными распределениями символов. База данных, которую мы используем, - это Cassandra.

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

Пример, если строки были закодированы по базе 16: (некоторые из средних точек являются приблизительными):

Starting highest and lowest keys:  '000'                'FFF'
                                   /   \              /       \
                              '000'     '8'         '8'       'FFF'
                              /   \     /  \       /  \       /   \
Result:                  '000'    '4' '4'  '8'   '8'  'B8'  'B8'  'FFF'
(After 3 levels of recursion)
4b9b3361

Ответ 1

К сожалению не все последовательности байтов действительны UTF-8, поэтому нетривиально просто взять среднюю точку значений UTF-8, как показано ниже.

def midpoint(s, e):
    '''Midpoint of start and end strings'''
    (sb, eb) = (int.from_bytes(bytes(x, 'utf-8'), byteorder='big') for x in (s, e))
    midpoint = int((eb - sb) / 2 + sb)

    midpoint_bytes = midpoint.to_bytes((midpoint.bit_length() // 8) + 1, byteorder='big')
    return midpoint_bytes.decode('utf-8')

В основном этот код преобразует каждую строку в целое число, представленное последовательностью байтов в памяти, находит середину этих двух целых чисел и пытается снова интерпретировать байты "средней точки" как UTF-8.

В зависимости от того, какое поведение вы хотели бы, следующим шагом может быть замена неверных байтов в midpoint_bytes на какой-то символ замены, чтобы сформировать допустимую строку UTF-8. Для вашей проблемы может не иметь значения, какой именно характер вы используете для замены, если вы согласны.

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

Ответ 2

Здесь общее решение, которое дает приблизительную середину m между любыми двумя строками Unicode a и b, такими, что a < m < b, если это возможно:

from os.path import commonprefix

# This should be set according to the range and frequency of
# characters used.
MIDCHAR = u'm'


def midpoint(a, b):
    prefix = commonprefix((a, b))
    p = len(prefix)
    # Find the codepoints at the position where the strings differ.
    ca = ord(a[p]) if len(a) > p else None
    cb = ord(b[p])
    # Find the approximate middle code point.
    cm = (cb // 2 if ca is None else (ca + cb) // 2)
    # If a middle code point was found, add it and return.
    if ca < cm < cb:
        return prefix + unichr(cm)
    # If b still has more characters after this, then just use
    # b code point and return.
    if len(b) > p + 1:
        return prefix + unichr(cb)
    # Otherwise, if cb == 0, then a and b are consecutive so there
    # is no midpoint. Return a.
    if cb == 0:
        return a
    # Otherwise, use part of a and an extra character so that
    # the result is greater than a.
    i = p + 1
    while i < len(a) and a[i] >= MIDCHAR:
        i += 1
    return a[:i] + MIDCHAR

Функция предполагает, что a < b. Кроме этого, он должен работать с произвольными строками Unicode, даже с символами u'\x00'. Также обратите внимание, что он может возвращать строки, содержащие u'\x00' или другие нестандартные кодовые точки. Если нет средней точки из-за b == a + u'\x00', возвращается a.

Ответ 3

Если вы посмотрите на метод JAVA StringTokinizer, он будет делать то, что вы хотите, и многое другое.