#c
Вопрос:
caesar.in содержит в первой строке текст (максимум 256 символов), во второй строке число, представляющее модификацию каждой буквы ( Я должен использовать шифр Цезаря, чтобы зашифровать или расшифровать каждую букву в зависимости от номера во 2-й строке и поместить ее в dbftbs.out. Мой код работает, но он настолько несовершенен. Как я могу сделать это более эффективным?
#include <fstream>
#include <iostream>
#include <cstring>
#include <sstream>
using namespace std;
char s[257], crypt[8];
int key,i,l;
int main()
{
ifstream fi("caesar.in");
fi.get(s, 257);
fi >> key;
fi.get();
fi.get(crypt, 8);
l = strlen(s);
if (crypt[0] == 'e')
for (i = 0; i <= l - 1; i ) {
if ((int(s[i]) >= 97) amp;amp; (int(s[i]) <= 122))
if ((s[i] key) > 122)
s[i] = 97 ((s[i] key) - 123);
else
s[i] = s[i] key;
else if ((int(s[i]) >= 65) amp;amp; (int(s[i]) <= 90))
if ((s[i] key) > 90)
s[i] = 65 ((s[i] key) - 91);
else
s[i] = s[i] key;
}
else
for (i = 0; i <= l - 1; i ) {
if ((int(s[i]) >= 97) amp;amp; (int(s[i]) <= 122))
if ((s[i] - key) < 97)
s[i] = 123 - (97 - (s[i] - key));
else
s[i] = s[i] - key;
else if ((int(s[i]) >= 65) amp;amp; int(s[i]) <= 90)
if ((s[i] - key) < 65)
s[i] = 91 - (65 - (s[i] - key));
else
s[i] = s[i] - key;
}
ofstream fo("dbftbs.out");
fo << s;
fi.close();
fo.close();
return 0;
}
Комментарии:
1. лучше использовать символы вместо магических чисел ..
'a'
вместо97
и т. Д.2. Почему вы думаете, что ваш код слишком медленный? Как вы измеряете? Вы провели профилирование, чтобы найти узкое место?
3. Я использую веб-сайт для решения школьных проблем. И за эту проблему я получаю 60/100. Ограничение по времени должно составлять 0,1 с, а используемая память 64 МБ/8 МБ
4. Подсказка: Шифр Цезаря использует оператор остатка
%
и смещение. Поищите в Интернете «Шифр Цезаря C » для примеров.5. Если символ
isupper
, то зашифрованный символ = (((символ — ‘A’) ключ) % 26) ‘A’; Попробуйте.
Ответ №1:
Я не могу представить себе никаких проблем со скоростью вашего кода.
Возможно, онлайн-судья вводил неверные данные в ваш код, и это было частью проблемы. Или, пожалуйста, установите флаги оптимизации скорости в своем компиляторе.
Проблема в этом вопросе заключается в потенциальном переполнении. Так что нам нужно с этим разобраться.
Тогда нам нужно понять, что означает расшифровка. Если шифрование сдвинет что-либо одно вправо, расшифровка снова сдвинет его влево.
- Таким образом, с «def» и ключом=1, зашифрованная строка будет «efg».
- И расшифровка с ключом=1 снова переместит его влево. Результат: «def»
Мы можем наблюдать, что нам просто нужно сдвинуться на -1, поэтому отрицательная часть ключа.
Таким образом, в основном шифрование и дешифрование могут выполняться с помощью одной и той же процедуры. Нам просто нужно перевернуть клавиши.
Давайте теперь рассмотрим проблему переполнения. На данный момент мы начнем только с прописных букв. Символы имеют соответствующий код. Например, в ASCII буква «А» кодируется 65, «В» — 66 и так далее. Поскольку мы не хотим вычислять с таким числом, мы нормализуем их. Мы просто вычитаем » А » из каждого символа. Затем
- «А» — «А» = 0
- ‘B’ — ‘A’ = 1
- ‘C’ — ‘A’ = 2
- ‘D’ — ‘A’ = 3
You see the pattern. If we want to encrypt now the letter ‘C’ with key 3, we can do the following.
‘C’ — ‘A’ 3 = 5 Then we add again ‘A’ to get back the letter and we will get 5 ‘A’ = ‘F’
That is the whole magic.
But what to do with an overflow, beyond ‘Z’. This can be handled by a simple modulo division.
Let us look at ‘Z’ 1. We do ‘Z’ — ‘A’ = 25, then 1 = 26 and now modulo 26 = 0 then plus ‘A’ will be ‘A’
And so on and so on. The resulting Formula is: (c-‘A’ key)& ‘A’
Next, what with negative keys? This is also simple. Assume an ‘A’ and key=-1
Результатом будет «Z». Но это то же самое, что сдвинуть 25 вправо. Таким образом, мы можем просто преобразовать отрицательный ключ в псойтивный сдвиг. Простое утверждение будет:
if (key < 0) key = (26 (key % 26)) % 26;
А затем мы можем вызвать нашу функцию преобразования с помощью простой лямбды. Одна функция для шифрования и расшифровки. Просто с помощью перевернутого ключа.
Пожалуйста, смотрите ниже одно (из многих возможных) решение на C , в котором используется эта схема. И, пожалуйста, обратите внимание. Все преобразование, шифрование и дешифрование, выполняется только с помощью ОДНОГО std::transform
оператора.
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <iomanip>
#include <cctype>
const std::string inFileName{ "r:\ceasar.in" };
const std::string outFileName{ "r:\dbftbs.out" };
int main() {
// Open the files and check, if they could be opened
if (std::ifstream inputStream{ inFileName }; inputStream) {
if (std::ofstream outputStream{ outFileName }; outputStream) {
// Here we store intermediate information from the source file
std::string text{}, command{};
int key{};
// Now, read data from the input file
if (std::getline(std::getline(inputStream, text) >> key >> std::ws, command)) {
// We need to check, if we have a valid command
bool validCommand{};
if (command == "encrypt") {
validCommand = true;
}
else if (command == "decrypt") {
// Revert the key
key = -key;
validCommand = true;
}
else std::cerr << "nError. Invalid command'" << command << "'nn";
// If the command was valid
if (validCommand) {
// Then do the transformation
// Handle negative keys
if (key < 0) key = (26 (key % 26)) % 26;
// Transform and write to file
std::transform(text.begin(), text.end(), std::ostream_iterator<char>(outputStream), [amp;key](const char c) {
char r{ c }; if (std::isalpha(c)) { if (std::isupper(c)) r = (c - 'A' key) % 26 'A'; else r = (c - 'a' key) % 26 'a'; } return r; });
}
}
else std::cerr << "nError. Invalid data in input filen";
}
else std::cerr << "nError. Could not open input file '" << inFileName << "'nn";
}
else std::cerr << "nError Could not open output file '" << outFileName << "'nn";
}
ПРАВКА: Некоторые дополнительные сведения, если вы работаете с представлением символов ASCII. Пожалуйста, взгляните на любую таблицу ASCII. Вы увидите, что любой прописной и строчный символы отличаются на 32. Или, если вы посмотрите в двоичном формате:
char dez bin char dez bin
'A' 65 0100 0001 'a' 97 0110 0001
'B' 66 0100 0010 'b' 98 0110 0010
'C' 67 0100 0011 'b' 99 0110 0011
. . .
Итак, если вы уже знаете, что символ альфа, то единственная разница между прописными и строчными буквами-это бит номер 5. Если мы хотим знать, если char
в нижнем регистре, мы можем получить это, замаскировав этот бит. c amp; 0b0010 0000
это равно c amp; 32
или c amp; 0x20
.
Если мы хотим оперировать либо прописными, либо строчными символами, мы можем замаскировать «регистр». С c amp; 0b00011111
помощью или c amp; 31
или c amp; 0x1F
мы всегда получим эквиваленты для символов верхнего регистра, уже нормализованных для начала с одного.
char dez bin Masking char dez bin Masking
'A' 65 0100 0001 amp; 0x1b = 1 'a' 97 0110 0001 amp; 0x1b = 1
'B' 66 0100 0010 amp; 0x1b = 2 'b' 98 0110 0010 amp; 0x1b = 2
'C' 67 0100 0011 amp; 0x1b = 3 'b' 99 0110 0011 amp; 0x1b = 3
. . .
Итак, если мы используем альфа — символ, маскируем его и вычитаем 1, то в результате получим 0..25 для любого символа верхнего или нижнего регистра.
Кроме того, я хотел бы повторить обработку ключей. Положительные ключи будут шифровать строку, отрицательные ключи будут расшифровывать строку. Но, как было сказано выше, отрицательные ключи могут быть преобразованы в положительные. Пример:
Shifting by -1 is same as shifting by 25
Shifting by -2 is same as shifting by 24
Shifting by -3 is same as shifting by 23
Shifting by -4 is same as shifting by 22
Итак,совершенно очевидно, что мы можем вычислить всегда положительный ключ с помощью: 26 key
. Для отрицательных ключей это даст нам вышеуказанные смещения.
А для позиционных ключей у нас будет переполнение более 26, которое мы можем устранить делением по модулю 26:
'A'--> 0 26 = 26 26 % 26 = 0
'B'--> 1 26 = 27 27 % 26 = 1
'C'--> 2 26 = 28 28 % 26 = 2
'D'--> 3 26 = 29 29 % 26 = 3
—> > (c key) % 26
устранит переполнения и приведет к правильному новому символу en/decryptd.
And, if we combine this with the bove wisdom for negative keys, we can write: ((26 (key&))&)
which will work for all positive and negative keys.
Combining that with that masking, could give us the following program:
const char potentialLowerCaseIndicator = c amp; 0x20;
const char upperOrLower = c amp; 0x1F;
const char normalized = upperOrLower - 1;
const int withOffset = normalized ((26 (key&))&);
const int withOverflowCompensation = withOffset % 26;
const char newUpperCaseCharacter = (char)withOverflowCompensation 'A';
const char result = newUpperCaseCharacter | (potentialLowerCaseIndicator );
Конечно, все вышеперечисленные многие утверждения могут быть преобразованы в одну Лямбду:
auto conv=[amp;](char c){return std::isalpha(c)?(char)((((camp;31)-1 ((26 (key&))&))& 65)|camp;32):c;};
// And the transformation with:
std::transform(text.begin(),text.end(),std::ostream_iterator<char>(outputStream),conv);
И вся, еще более сжатая программа:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
#include <iterator>
#include <iomanip>
#include <cctype>
const std::string inFileName{ "r:\ceasar.in" };
const std::string outFileName{ "r:\dbftbs.out" };
int main() {
// Open the files and check, if they could be opened
if (std::ifstream inputStream{ inFileName }; inputStream) {
if (std::ofstream outputStream{ outFileName }; outputStream) {
// Here we store intermediate information from the source file
std::string text{}, command{};
int key{};
// Now, read data from the input file
if (std::getline(std::getline(inputStream, text) >> key >> std::ws, command)) {
// Lambda / Function for Ceasar encryption/decryption
auto conv=[amp;](char c){return std::isalpha(c)?(char)((((camp;31)-1 ((26 (key&))&))& 65)|camp;32):c; };
auto doJob=[amp;](bool r){if (r)key=-key;std::transform(text.begin(),text.end(),std::ostream_iterator<char>(outputStream),conv); };
// Check parameter
if (command == "encrypt") doJob(false);
else if (command == "decrypt") doJob(true);
else std::cerr << "nError. Invalid command'" << command << "'nn";
}
else std::cerr << "nError. Invalid data in input filen";
}
else std::cerr << "nError. Could not open input file '" << inFileName << "'nn";
}
else std::cerr << "nError Could not open output file '" << outFileName << "'nn";
}
Пожалуйста, обратите внимание, что большая часть программы-это ввод файлов и обработка ошибок.