Last active
February 26, 2023 15:51
-
-
Save vilisov/8278ad08700c89afde28 to your computer and use it in GitHub Desktop.
Алгоритм Хэмминга (Python)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
# длина блока кодирования | |
CHUNK_LENGTH = 8 | |
# проверка длины блока | |
assert not CHUNK_LENGTH % 8, 'Длина блока должна быть кратна 8' | |
# вычисление контрольных бит | |
CHECK_BITS = [i for i in range(1, CHUNK_LENGTH + 1) if not i & (i - 1)] | |
def chars_to_bin(chars): | |
""" | |
Преобразование символов в бинарный формат | |
""" | |
assert not len(chars) * 8 % CHUNK_LENGTH, 'Длина кодируемых данных должна быть кратна длине блока кодирования' | |
return ''.join([bin(ord(c))[2:].zfill(8) for c in chars]) | |
def chunk_iterator(text_bin, chunk_size=CHUNK_LENGTH): | |
""" | |
Поблочный вывод бинарных данных | |
""" | |
for i in range(len(text_bin)): | |
if not i % chunk_size: | |
yield text_bin[i:i + chunk_size] | |
def get_check_bits_data(value_bin): | |
""" | |
Получение информации о контрольных битах из бинарного блока данных | |
""" | |
check_bits_count_map = {k: 0 for k in CHECK_BITS} | |
for index, value in enumerate(value_bin, 1): | |
if int(value): | |
bin_char_list = list(bin(index)[2:].zfill(8)) | |
bin_char_list.reverse() | |
for degree in [2 ** int(i) for i, value in enumerate(bin_char_list) if int(value)]: | |
check_bits_count_map[degree] += 1 | |
check_bits_value_map = {} | |
for check_bit, count in check_bits_count_map.items(): | |
check_bits_value_map[check_bit] = 0 if not count % 2 else 1 | |
return check_bits_value_map | |
def set_empty_check_bits(value_bin): | |
""" | |
Добавить в бинарный блок "пустые" контрольные биты | |
""" | |
for bit in CHECK_BITS: | |
value_bin = value_bin[:bit - 1] + '0' + value_bin[bit - 1:] | |
return value_bin | |
def set_check_bits(value_bin): | |
""" | |
Установить значения контрольных бит | |
""" | |
value_bin = set_empty_check_bits(value_bin) | |
check_bits_data = get_check_bits_data(value_bin) | |
for check_bit, bit_value in check_bits_data.items(): | |
value_bin = '{0}{1}{2}'.format( | |
value_bin[:check_bit - 1], bit_value, value_bin[check_bit:]) | |
return value_bin | |
def get_check_bits(value_bin): | |
""" | |
Получить информацию о контрольных битах из блока бинарных данных | |
""" | |
check_bits = {} | |
for index, value in enumerate(value_bin, 1): | |
if index in CHECK_BITS: | |
check_bits[index] = int(value) | |
return check_bits | |
def exclude_check_bits(value_bin): | |
""" | |
Исключить информацию о контрольных битах из блока бинарных данных | |
""" | |
clean_value_bin = '' | |
for index, char_bin in enumerate(list(value_bin), 1): | |
if index not in CHECK_BITS: | |
clean_value_bin += char_bin | |
return clean_value_bin | |
def set_errors(encoded): | |
""" | |
Допустить ошибку в блоках бинарных данных | |
""" | |
result = '' | |
for chunk in chunk_iterator(encoded, CHUNK_LENGTH + len(CHECK_BITS)): | |
num_bit = random.randint(1, len(chunk)) | |
chunk = '{0}{1}{2}'.format(chunk[:num_bit - 1], int(chunk[num_bit - 1]) ^ 1, chunk[num_bit:]) | |
result += (chunk) | |
return result | |
def check_and_fix_error(encoded_chunk): | |
""" | |
Проверка и исправление ошибки в блоке бинарных данных | |
""" | |
check_bits_encoded = get_check_bits(encoded_chunk) | |
check_item = exclude_check_bits(encoded_chunk) | |
check_item = set_check_bits(check_item) | |
check_bits = get_check_bits(check_item) | |
if check_bits_encoded != check_bits: | |
invalid_bits = [] | |
for check_bit_encoded, value in check_bits_encoded.items(): | |
if check_bits[check_bit_encoded] != value: | |
invalid_bits.append(check_bit_encoded) | |
num_bit = sum(invalid_bits) | |
encoded_chunk = '{0}{1}{2}'.format( | |
encoded_chunk[:num_bit - 1], | |
int(encoded_chunk[num_bit - 1]) ^ 1, | |
encoded_chunk[num_bit:]) | |
return encoded_chunk | |
def get_diff_index_list(value_bin1, value_bin2): | |
""" | |
Получить список индексов различающихся битов | |
""" | |
diff_index_list = [] | |
for index, char_bin_items in enumerate(zip(list(value_bin1), list(value_bin2)), 1): | |
if char_bin_items[0] != char_bin_items[1]: | |
diff_index_list.append(index) | |
return diff_index_list | |
def encode(source): | |
""" | |
Кодирование данных | |
""" | |
text_bin = chars_to_bin(source) | |
result = '' | |
for chunk_bin in chunk_iterator(text_bin): | |
chunk_bin = set_check_bits(chunk_bin) | |
result += chunk_bin | |
return result | |
def decode(encoded, fix_errors=True): | |
""" | |
Декодирование данных | |
""" | |
decoded_value = '' | |
fixed_encoded_list = [] | |
for encoded_chunk in chunk_iterator(encoded, CHUNK_LENGTH + len(CHECK_BITS)): | |
if fix_errors: | |
encoded_chunk = check_and_fix_error(encoded_chunk) | |
fixed_encoded_list.append(encoded_chunk) | |
clean_chunk_list = [] | |
for encoded_chunk in fixed_encoded_list: | |
encoded_chunk = exclude_check_bits(encoded_chunk) | |
clean_chunk_list.append(encoded_chunk) | |
for clean_chunk in clean_chunk_list: | |
for clean_char in [clean_chunk[i:i + 8] for i in range(len(clean_chunk)) if not i % 8]: | |
decoded_value += chr(int(clean_char, 2)) | |
return decoded_value | |
if __name__ == '__main__': | |
source = input('Укажите текст для кодирования/декодирования:') | |
print('Длина блока кодирования: {0}'.format(CHUNK_LENGTH)) | |
print('Контрольные биты: {0}'.format(CHECK_BITS)) | |
encoded = encode(source) | |
print('Закодированные данные: {0}'.format(encoded)) | |
decoded = decode(encoded) | |
print('Результат декодирования: {0}'.format(decoded)) | |
encoded_with_error = set_errors(encoded) | |
print('Допускаем ошибки в закодированных данных: {0}'.format(encoded_with_error)) | |
diff_index_list = get_diff_index_list(encoded, encoded_with_error) | |
print('Допущены ошибки в битах: {0}'.format(diff_index_list)) | |
decoded = decode(encoded_with_error, fix_errors=False) | |
print('Результат декодирования ошибочных данных без исправления ошибок: {0}'.format(decoded)) | |
decoded = decode(encoded_with_error) | |
print('Результат декодирования ошибочных данных с исправлением ошибок: {0}'.format(decoded)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment