У Rsa97 отличная идея с алфавитом. Мне захотелось написать код с её реализацией.
У этой идеи есть небольшой нюанс (отнюдь, не минус, а порой даже плюс, если говорить о шифровании): без правильного алфавита расшифровать текст сразу не получится. Если это не нравится, то можно использовать для каждого языка свой алфавит, а не смешивать их воедино. Тогда алгоритм станет более похожим на классический шифр Цезаря, а значит и точный алфавит знать дешифровщику знать не нужно.
Если уходить от задачи с шифром Цезаря, можно перемешать алфавит любым идемпотентным алгоритом (чтобы каждый раз получать одинаковый результат). Тогда шифр станет более надёжным.
А если нет необходимости читать старые данные по прошествии времени, то можно вовсе менять ключ, по которому перемешивается алфавит, каждый день и получить этакую "Энигму".
В C, Java и других языках понадобится следующая формула для приведения shift в нужные границы (0 ≤ shift < len(alphabet)):
shift = (shift % len(alphabet) + len(alphabet)) % len(alphabet)
Первый modulo нужен для ограничения сдвига размером алфавита. Сложение результата с длиной алфавита необходимо для получения неотрицательного числа.
В Python же будет достаточно этого (см. как реализован modulo в Python):
shift %= len(alphabet) # Или shift = shift % len(alphabet)
Но так как при индексации уже берется modulo, здесь никак не нужно обрабатывать shift заранее.
import string
alphabet = "абвгдеёжзийклмнопрстуфхцчшщъыьэюя"
alphabet += alphabet.upper()
alphabet += string.ascii_letters
# Также можно добавить к алфавиту string.punctuation и string.digits
def caesar_cipher(text: str, shift: int) -> str:
return "".join(
alphabet[(alphabet.index(char) + shift) % len(alphabet)] if char in alphabet else char
for char in text
)
cipher = caesar_cipher("Привет, мир! Hello, world!", 46)
text = caesar_cipher(cipher, -46) # Или len(alphabet) - 46
print(f"{cipher}\n{text}")
Итого получаем рабочий алгоритм и результат:
DЭХОСЯ, ЩХЭ! ъYеез, пзкеX!
Привет, мир! Hello, world!