Guest User

Compression

a guest
Feb 19th, 2025
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.54 KB | None | 0 0
  1. import json
  2. from collections import defaultdict
  3. import sys
  4.  
  5. def compress_data(data: bytes):
  6. """
  7. Рекурсивно сжимает входные данные, заменяя часто встречающиеся пары.
  8. Возвращает:
  9. - tokens: финальная последовательность токенов (целые числа)
  10. - dictionary: словарь замен вида {новый_токен: (токен1, токен2)}
  11. """
  12. # Представляем данные как список целых чисел (0..255)
  13. tokens = list(data)
  14. dictionary = {}
  15. next_token = 256 # новые токены начинаем с 256
  16. tokens, dictionary, _ = _compress_tokens(tokens, dictionary, next_token)
  17. return tokens, dictionary
  18.  
  19. def _compress_tokens(tokens, dictionary, next_token):
  20. """
  21. Рекурсивная функция, которая:
  22. 1. Сканирует список токенов для подсчёта пар (неперекрывающих).
  23. 2. Если найдена пара, встречающаяся ≥2 раза, заменяет все её вхождения новым токеном,
  24. обновляет словарь и продолжает рекурсию.
  25. 3. Если замена не даёт эффекта, возвращает текущую последовательность.
  26. """
  27. pair_counts = defaultdict(int)
  28. # Подсчёт всех (неперекрывающих) пар
  29. for i in range(len(tokens) - 1):
  30. pair = (tokens[i], tokens[i+1])
  31. pair_counts[pair] += 1
  32.  
  33. if not pair_counts:
  34. return tokens, dictionary, next_token
  35.  
  36. # Выбираем пару с максимальной частотой
  37. best_pair, count = max(pair_counts.items(), key=lambda x: x[1])
  38. # Если ни одна пара не встречается дважды – сжатие закончено
  39. if count < 2:
  40. return tokens, dictionary, next_token
  41.  
  42. # Новый токен для замены найденной пары
  43. new_token = next_token
  44. next_token += 1
  45. dictionary[new_token] = best_pair
  46.  
  47. # Замена всех неперекрывающих вхождений best_pair
  48. new_tokens = []
  49. i = 0
  50. while i < len(tokens):
  51. if i < len(tokens) - 1 and (tokens[i], tokens[i+1]) == best_pair:
  52. new_tokens.append(new_token)
  53. i += 2 # пропускаем оба элемента пары
  54. else:
  55. new_tokens.append(tokens[i])
  56. i += 1
  57.  
  58. # Рекурсивно продолжаем сжатие с обновлённой последовательностью токенов
  59. return _compress_tokens(new_tokens, dictionary, next_token)
  60.  
  61. def decompress_data(tokens, dictionary):
  62. """
  63. Рекурсивно восстанавливает исходные данные из сжатой последовательности токенов.
  64. Каждый токен, который не является исходным байтом (0..255), разворачивается по словарю.
  65. """
  66. def expand(token):
  67. if token < 256:
  68. return [token]
  69. else:
  70. left, right = dictionary[token]
  71. return expand(left) + expand(right)
  72.  
  73. result = []
  74. for token in tokens:
  75. result.extend(expand(token))
  76. return bytes(result)
  77.  
  78. def save_compressed(filename, tokens, dictionary):
  79. """
  80. Сохраняет сжатые данные в файл в формате JSON.
  81. Словарь преобразуется так, чтобы ключи стали строками.
  82. """
  83. dict_to_save = {str(k): v for k, v in dictionary.items()}
  84. data = {
  85. "tokens": tokens,
  86. "dictionary": dict_to_save
  87. }
  88. with open(filename, 'w') as f:
  89. json.dump(data, f)
  90.  
  91. def load_compressed(filename):
  92. """
  93. Загружает сжатые данные из JSON-файла.
  94. Преобразует ключи словаря обратно в числа.
  95. """
  96. with open(filename, 'r') as f:
  97. data = json.load(f)
  98. dictionary = {int(k): tuple(v) for k, v in data["dictionary"].items()}
  99. tokens = data["tokens"]
  100. return tokens, dictionary
  101.  
  102. def print_usage():
  103. print("Usage:")
  104. print(" python recursive_compress.py c input_file # для сжатия")
  105. print(" python recursive_compress.py d input_file # для разжатия")
  106. sys.exit(1)
  107.  
  108. if __name__ == "__main__":
  109. if len(sys.argv) < 3:
  110. print_usage()
  111.  
  112. mode = sys.argv[1]
  113. filename = sys.argv[2]
  114.  
  115. if mode == "c":
  116. # Сжатие файла
  117. try:
  118. with open(filename, 'rb') as f:
  119. data = f.read()
  120. except Exception as e:
  121. print(f"Ошибка при чтении файла: {e}")
  122. sys.exit(1)
  123.  
  124. tokens, dictionary = compress_data(data)
  125. out_filename = filename + ".rc" # расширение для сжатых файлов
  126. try:
  127. save_compressed(out_filename, tokens, dictionary)
  128. except Exception as e:
  129. print(f"Ошибка при сохранении сжатых данных: {e}")
  130. sys.exit(1)
  131. print(f"Файл сжат и сохранён как {out_filename}")
  132.  
  133. elif mode == "d":
  134. # Декомпрессия файла
  135. try:
  136. tokens, dictionary = load_compressed(filename)
  137. except Exception as e:
  138. print(f"Ошибка при загрузке сжатых данных: {e}")
  139. sys.exit(1)
  140. try:
  141. data = decompress_data(tokens, dictionary)
  142. except Exception as e:
  143. print(f"Ошибка при декомпрессии: {e}")
  144. sys.exit(1)
  145. # Если расширение .rc присутствует – убираем его
  146. if filename.endswith(".rc"):
  147. out_filename = filename[:-3]
  148. else:
  149. out_filename = filename + ".dec"
  150. try:
  151. with open(out_filename, 'wb') as f:
  152. f.write(data)
  153. except Exception as e:
  154. print(f"Ошибка при записи декомпрессированного файла: {e}")
  155. sys.exit(1)
  156. print(f"Файл декомпрессирован и сохранён как {out_filename}")
  157.  
  158. else:
  159. print_usage()
  160.  
Advertisement
Add Comment
Please, Sign In to add comment