c++ - 如何在 C++ 中对数组进行洗牌?
问题描述
我有一个数组:
names[4]={john,david,jack,harry};
我想随机洗牌,比如:
names[4]={jack,david,john,harry};
我尝试使用它,但它只是打乱了数组中第一个单词的字母:
random_shuffle(names->begin(), names->end());
这是完整的代码,它从 .txt 文件中读取名称并放入一个数组中:
#include <iostream>
#include <fstream>
#include <string>
#include <algorithm>
using namespace std;
int main() {
ifstream readName("names.txt");
string names[197];
int i = 0;
for (string line; getline(readName, line); ){
readName >> names[i];
i++;
}
readName.close();
random_shuffle(names->begin(), names->end());
for (int i = 0; i < 197; i++) {
cout << names[i] << endl;
}
return 0;
}
我从不同的人那里尝试了一些其他的东西,但我无法做到。有什么帮助,谢谢!
解决方案
这是您的代码,我认为更改量最小。有人可能会争辩说,我不需要对你的第一个 for 循环进行太多更改,但我认为如果你有先见之明知道你正在阅读多少个名字,你不妨使用这些知识。
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator> // std::begin(), std::end(); required for C-arrays
#include <random> // std::mt19937; needed to feed std::shuffle()
#include <string>
// using namespace std; // BAD PRACTICE
int main() {
constexpr int size = 4; // Give your magic number a name; only need to change
// a single location
std::ifstream readName("names.txt");
if (!readName) { // Always check that you successfully opened the file.
std::cerr << "Error opening file.\n";
return 1;
}
std::string names[size];
// int i = 0;
for (int i = 0; i < size; ++i) { // Retool the loop entirely
std::getline(readName, names[i]);
}
readName.close();
// This is a fragile solution. It's only working because the array is in
// scope
std::shuffle(std::begin(names), std::end(names),
std::mt19937{std::random_device{}()});
for (int i = 0; i < size; i++) {
std::cout << names[i]
<< '\n'; // Don't use std::endl unless you actually need it
}
return 0;
}
不过,这不是理想的代码。对输入文件大小的任何更改都需要更改代码并重新编译。最大的单一变化是摆脱std::random_shuffle
和使用std::shuffle()
。std::random_shuffle
已在 C++14 中弃用并在 C++17 中删除。不好用。std::shuffle()
确实增加了提供 PRNG 的要求,但还不错。如果你有一个 PRNG 需要在一个更大的程序中随机化许多不同的东西,它会导致更好的代码。这是因为最好有一个 PRNG 并让它在你的程序的整个过程中都存在,而不是不断地构建新的。
而 C-array 只是让事情变得有点笨拙。输入std::vector
。
#include <algorithm>
#include <fstream>
#include <iostream>
#include <iterator>
#include <random> // std::mt19937; needed to feed std::shuffle()
#include <string>
#include <vector>
int main() {
std::ifstream readName("names.txt");
if (!readName) { // Always check that you successfully opened the file.
std::cerr << "Error opening file.\n";
return 1;
}
std::vector<std::string> names;
std::string name;
while (std::getline(readName, name)) { // Retool the loop entirely
names.push_back(name);
}
readName.close();
std::shuffle(std::begin(names), std::end(names),
std::mt19937{std::random_device{}()});
for (const auto& i : names) {
std::cout << i << '\n';
}
return 0;
}
向量可以根据需要增长,因此您会看到读取名称的循环变得多么简单。它也更加灵活,因为您不必提前知道预期有多少条目。它会“正常工作”。调用std::shuffle()
I 时保留了std::begin(names)
语法,因为许多人认为这是最佳实践,但如果你愿意,你也可以使用names.begin()
,因为向量类提供了自己的迭代器。