I have two files: a text file that will be used to count the most frequent characters, and a binary file that will be used to count the most frequent bytes.
I need to write a function that replaces the most frequent characters with the most frequent bytes.
Here's the output I'm getting after testing the function:
However, the output needs to have 80% of the characters correctly mapped.
What am I doing wrong? I tried increasing the number of pairs from 17 to 100, but the output is still gibberish.
Here's the content of the text file:
Exploring the breathtaking landscapes of the English countryside is truly a remarkable experience.
From the rolling hills covered in vibrant green to the charming little villages nestled along meandering rivers, there is an undeniable sense of tranquility and beauty.
The picturesque scenery, combined with the rich history and culture, creates a perfect blend of old-world charm and modern-day allure.
Whether you choose to stroll through the enchanting gardens, visit historic castles, or indulge in traditional afternoon tea, there is something for everyone to enjoy in this captivating destination.
Here's the script:
def frq_txt(text: str) -> List[Tuple[str, int]]:
"""
Finds how often each character appears in a plaintext string.
The function returns a list of (character, count) pairs ordered
from the most frequent to the least frequent character.
"""
counts = Counter(text)
# Convert the Counter into (char, count) pairs and sort them.
# The lambda picks item[1] (the count) so sorting is based on frequency.
# reverse=True ensures the most frequent characters come first.
freq_list = sorted(counts.items(), key=lambda item: item[1], reverse=True)
return freq_list
def frq_cpr(cipher_bytes: bytes) -> List[Tuple[int, int]]:
"""
Finds how often each byte value appears in a ciphertext.
The function returns (byte_value, count) pairs ordered from
the most frequent byte to the least frequent.
"""
counts = Counter(cipher_bytes)
# Convert counts to (byte, count) pairs and sort by count descending.
# The lambda selects item[1] (the count) so the sorting is based on frequency.
freq_list = sorted(counts.items(), key=lambda item: item[1], reverse=True)
return freq_list
def build_dec_key(
plain_freq: List[Tuple[str, int]],
cipher_freq: List[Tuple[int, int]],
top_n: int = 17,
) -> Dict[int, str]:
"""
Builds a partial decryption key by pairing the top-N most frequent plaintext
characters with the top-N most frequent ciphertext byte values.
Args:
plain_freq: List of (char, count) sorted from most to least frequent.
cipher_freq: List of (byte_value, count) sorted from most to least frequent.
top_n: Number of most frequent symbols to pair.
Returns:
A dictionary mapping ciphertext byte values (int) to guessed
plaintext characters (str).
"""
dec_key: Dict[int, str] = {}
# Take only the top N from each list
plain_top = plain_freq[:top_n]
cipher_top = cipher_freq[:top_n]
# Pair: most frequent plaintext char with most frequent ciphertext byte, etc.
for (ch, _), (byte_val, _) in zip(plain_top, cipher_top):
dec_key[byte_val] = ch
return dec_key
def guess_txt(cipher_bytes: bytes, dec_key: Dict[int, str]) -> str:
"""
Attempts to decrypt ciphertext using a partial decryption key.
Args:
cipher_bytes: The ciphertext (sequence of bytes).
dec_key: A partial decryption key mapping ciphertext byte values (0–255)
to guessed plaintext characters.
Returns:
A string where known bytes are replaced with their guessed plaintext
character, and unknown bytes are replaced with '$'.
"""
result = []
for b in cipher_bytes:
if b in dec_key:
result.append(dec_key[b])
else:
result.append("$")
return "".join(result)
